diff --git a/hr/__init__.py b/hr/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hr/admin.py b/hr/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/hr/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/hr/apps.py b/hr/apps.py new file mode 100644 index 0000000..339a3b6 --- /dev/null +++ b/hr/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class HRConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'hr' diff --git a/hr/models.py b/hr/models.py new file mode 100644 index 0000000..cbfbb1e --- /dev/null +++ b/hr/models.py @@ -0,0 +1,92 @@ +import datetime +from django.db import models +from io import BytesIO +from reportlab.lib.pagesizes import letter +from reportlab.pdfgen import canvas + +# Employee Directory +class Employee(models.Model): + first_name = models.CharField(max_length=50) + last_name = models.CharField(max_length=50) + position = models.CharField(max_length=100) + email = models.EmailField(unique=True) + hired_date = models.DateField() + work_schedule = models.JSONField(default=dict) + + def __str__(self): + return f"{self.first_name} {self.last_name}" + +# Time Tracking System +class WorkTimeLog(models.Model): + employee = models.ForeignKey(Employee, on_delete=models.CASCADE) + date = models.DateField() + check_in = models.TimeField(null=True, blank=True) + check_out = models.TimeField(null=True, blank=True) + worked_hours = models.FloatField(null=True, blank=True) + + def calculate_worked_hours(self): + if self.check_in and self.check_out: + delta = datetime.combine(datetime.date.min, self.check_out) - datetime.combine(datetime.date.min, self.check_in) + self.worked_hours = delta.total_seconds() / 3600 + else: + self.worked_hours = 0 + + def save(self, *args, **kwargs): + self.calculate_worked_hours() + super().save(*args, **kwargs) + + def __str__(self): + return f"{self.employee}: {self.date}" + +# Leave Management +class Leave(models.Model): + LEAVE_TYPE_CHOICES = [ + ("vacation", "Vacation"), + ("sick", "Sick Leave"), + ] + + employee = models.ForeignKey(Employee, on_delete=models.CASCADE) + leave_type = models.CharField(max_length=10, choices=LEAVE_TYPE_CHOICES) + start_date = models.DateField() + end_date = models.DateField() + reason = models.TextField(blank=True) + + def __str__(self): + return f"{self.employee}: {self.leave_type} ({self.start_date} - {self.end_date})" + + def duration(self): + return (self.end_date - self.start_date).days + 1 + +# Overtime Reporting +class OvertimeReport(models.Model): + employee = models.ForeignKey(Employee, on_delete=models.CASCADE) + date = models.DateField() + worked_hours = models.FloatField() + required_hours = models.FloatField(default=8.0) + overtime = models.FloatField() + comment = models.TextField(blank=True) + + def calculate_overtime(self): + self.overtime = self.worked_hours - self.required_hours + + def save(self, *args, **kwargs): + self.calculate_overtime() + super().save(*args, **kwargs) + + def __str__(self): + return f"{self.employee}: {self.overtime} hours on {self.date}" + +# PDF Generation for HR Reports +class Report(models.Model): + employee = models.ForeignKey(Employee, on_delete=models.CASCADE) + report_date = models.DateField(auto_now_add=True) + + def generate_pdf(self): + buffer = BytesIO() + c = canvas.Canvas(buffer, pagesize=letter) + c.drawString(100, 750, f"Employee: {self.employee.first_name} {self.employee.last_name}") + c.drawString(100, 730, f"Date: {self.report_date}") + c.showPage() + c.save() + buffer.seek(0) + return buffer diff --git a/hr/serializers.py b/hr/serializers.py new file mode 100644 index 0000000..564c5b2 --- /dev/null +++ b/hr/serializers.py @@ -0,0 +1,40 @@ +from rest_framework import serializers +from .models import Employee, WorkTimeLog, Leave, OvertimeReport, Report + +# Сериализатор для модели Employee +class EmployeeSerializer(serializers.ModelSerializer): + class Meta: + model = Employee + fields = ['id', 'first_name', 'last_name', 'position', 'email', 'hired_date', 'work_schedule'] + +# Сериализатор для модели WorkTimeLog +class WorkTimeLogSerializer(serializers.ModelSerializer): + employee = EmployeeSerializer() # Вложенный сериализатор для сотрудника + + class Meta: + model = WorkTimeLog + fields = ['id', 'employee', 'date', 'check_in', 'check_out', 'worked_hours'] + +# Сериализатор для модели Leave +class LeaveSerializer(serializers.ModelSerializer): + employee = EmployeeSerializer() # Вложенный сериализатор для сотрудника + + class Meta: + model = Leave + fields = ['id', 'employee', 'leave_type', 'start_date', 'end_date', 'reason'] + +# Сериализатор для модели OvertimeReport +class OvertimeReportSerializer(serializers.ModelSerializer): + employee = EmployeeSerializer() # Вложенный сериализатор для сотрудника + + class Meta: + model = OvertimeReport + fields = ['id', 'employee', 'date', 'worked_hours', 'required_hours', 'overtime', 'comment'] + +# Сериализатор для модели Report +class ReportSerializer(serializers.ModelSerializer): + employee = EmployeeSerializer() # Вложенный сериализатор для сотрудника + + class Meta: + model = Report + fields = ['id', 'employee', 'report_date'] diff --git a/hr/tests.py b/hr/tests.py new file mode 100644 index 0000000..e69de29 diff --git a/hr/urls.py b/hr/urls.py new file mode 100644 index 0000000..6d88889 --- /dev/null +++ b/hr/urls.py @@ -0,0 +1,37 @@ +from django.urls import path, include +from rest_framework.routers import DefaultRouter +from drf_yasg.views import get_schema_view +from drf_yasg import openapi +from rest_framework.permissions import AllowAny +from .views import ( + EmployeeViewSet, WorkTimeLogViewSet, LeaveViewSet, + OvertimeReportViewSet, ReportViewSet +) + +# Create the router for HR-related views +router_hr = DefaultRouter() +router_hr.register(r'employees', EmployeeViewSet) +router_hr.register(r'work-time-logs', WorkTimeLogViewSet) +router_hr.register(r'leaves', LeaveViewSet) +router_hr.register(r'overtime-reports', OvertimeReportViewSet) +router_hr.register(r'reports', ReportViewSet) + +# Swagger schema view for HR +schema_view_hr = get_schema_view( + openapi.Info( + title="Human Resources API", + default_version='v1', + description="API for managing HR-related data and operations", + terms_of_service="https://www.example.com/terms/", + contact=openapi.Contact(email="support@example.com"), + license=openapi.License(name="BSD License"), + ), + public=True, + permission_classes=(AllowAny,), +) + +urlpatterns = [ + path('', include(router_hr.urls)), + path('swagger/', schema_view_hr.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui-hr'), + path('redoc/', schema_view_hr.with_ui('redoc', cache_timeout=0), name='schema-redoc-hr'), +] diff --git a/hr/views.py b/hr/views.py new file mode 100644 index 0000000..f958047 --- /dev/null +++ b/hr/views.py @@ -0,0 +1,31 @@ +from rest_framework import viewsets +from .models import Employee, WorkTimeLog, Leave, OvertimeReport, Report +from .serializers import ( + EmployeeSerializer, WorkTimeLogSerializer, LeaveSerializer, + OvertimeReportSerializer, ReportSerializer +) + +# ViewSet для модели Employee +class EmployeeViewSet(viewsets.ModelViewSet): + queryset = Employee.objects.all() + serializer_class = EmployeeSerializer + +# ViewSet для модели WorkTimeLog +class WorkTimeLogViewSet(viewsets.ModelViewSet): + queryset = WorkTimeLog.objects.all() + serializer_class = WorkTimeLogSerializer + +# ViewSet для модели Leave +class LeaveViewSet(viewsets.ModelViewSet): + queryset = Leave.objects.all() + serializer_class = LeaveSerializer + +# ViewSet для модели OvertimeReport +class OvertimeReportViewSet(viewsets.ModelViewSet): + queryset = OvertimeReport.objects.all() + serializer_class = OvertimeReportSerializer + +# ViewSet для модели Report +class ReportViewSet(viewsets.ModelViewSet): + queryset = Report.objects.all() + serializer_class = ReportSerializer diff --git a/urls.py b/urls.py index 49a048e..022f400 100644 --- a/urls.py +++ b/urls.py @@ -7,4 +7,5 @@ urlpatterns = [ path('api/pricing/', include('pricing.urls')), # Pricing URLs path('api/goods-reception/', include('goods_reception.urls')), path('api/warehouse/', include('warehouse.urls')), + path('api/hr/', include('hr.urls')), ]