[ADD] HORILLA API: Add Horilla API into master code base

This commit is contained in:
Horilla
2024-10-10 15:41:46 +05:30
parent f42ac1a391
commit 053964edd9
42 changed files with 7330 additions and 0 deletions

View File

@@ -8,4 +8,5 @@ from horilla import (
horilla_context_processors,
horilla_middlewares,
horilla_settings,
rest_conf,
)

49
horilla/rest_conf.py Normal file
View File

@@ -0,0 +1,49 @@
"""
rest_conf.py
"""
from horilla import settings
from horilla.settings import INSTALLED_APPS
from datetime import timedelta
# Injecting installed apps to settings
REST_APPS = ["rest_framework",
"rest_framework_simplejwt",
"drf_yasg",
"horilla_api"
]
INSTALLED_APPS.extend(REST_APPS)
REST_FRAMEWORK_SETTINGS = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
'PAGE_SIZE': 20,
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=30),
}
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'Bearer': {
'type': 'apiKey',
'name': 'Authorization',
'in': 'header',
'description': 'Enter your Bearer token here',
},
'Basic': {
'type': 'basic',
'description': 'Basic authentication. Enter your username and password.',
}
},
'SECURITY': [{'Bearer': []}, {'Basic': []}],
}
# Inject the REST framework settings into the Django project settings
setattr(settings, 'REST_FRAMEWORK', REST_FRAMEWORK_SETTINGS)
setattr(settings, 'SIMPLE_JWT', SIMPLE_JWT)
setattr(settings, 'SWAGGER_SETTINGS', SWAGGER_SETTINGS)

View File

@@ -35,6 +35,7 @@ urlpatterns = [
"^inbox/notifications/", include(notifications.urls, namespace="notifications")
),
path("i18n/", include("django.conf.urls.i18n")),
path("api/", include("horilla_api.urls")),
]
if settings.DEBUG:

0
horilla_api/__init__.py Normal file
View File

3
horilla_api/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,101 @@
from rest_framework.permissions import BasePermission
from base.models import MultipleApprovalManagers
from employee.models import EmployeeWorkInformation
from functools import wraps
from rest_framework.response import Response
from rest_framework import status
class ManagerPermission(BasePermission):
leave_perm = [
"leave.view_leaverequest",
"leave.change_leaverequest",
"leave.delete_leaverequest",
]
def has_permission(self, request, perm):
user = request.user
employee = user.employee_get
if perm in self.leave_perm:
is_approval_manager = MultipleApprovalManagers.objects.filter(
employee_id=employee.id
).exists()
if is_approval_manager:
return True
is_manager = EmployeeWorkInformation.objects.filter(
reporting_manager_id=employee
).exists()
if user.has_perm(perm) or is_manager:
return True
return False
def manager_permission_required(perm):
"""
Decorator for views that checks whether the user has appropriate manager permissions.
"""
def decorator(func):
@wraps(func)
def wrapper(self, request, *args, **kwargs):
permission = ManagerPermission()
if permission.has_permission(request, perm):
return func(self, request, *args, **kwargs)
else:
return Response(
{"error": "You do not have permission to perform this action."},
status=status.HTTP_403_FORBIDDEN,
)
return wrapper
return decorator
def manager_or_owner_permission_required(model_class, perm):
"""
Decorator for views that checks whether the user has either manager or owner permissions and a specific permission for a specific object for a given model class.
"""
def decorator(func):
@wraps(func)
def wrapper(self, request, pk=None, *args, **kwargs):
if pk:
try:
obj = model_class.objects.get(pk=pk)
# Check if the requesting user is the owner of the object
if obj.employee_id == request.user.employee_get:
return func(self, request, pk, *args, **kwargs)
except model_class.DoesNotExist:
return Response({"error": f"{model_class.__name__} does not exist"}, status=status.HTTP_404_NOT_FOUND)
else:
if request.data.get('employee_id', None) == request.user.employee_get.id:
return func(self, request, *args, **kwargs)
# If not the owner, check for manager permission
permission = ManagerPermission()
if permission.has_permission(request, perm) and pk:
return func(self, request,pk, *args, **kwargs)
elif permission.has_permission(request, perm) and pk == None:
return func(self, request, *args, **kwargs)
else:
return Response(
{"error": "You do not have permission to perform this action."},
status=status.HTTP_403_FORBIDDEN,
)
return wrapper
return decorator
def check_approval_status(model, perm):
""" checking the object approval status """
def decorator(func):
@wraps(func)
def wrapper(self, request, pk, *args, **kwargs):
object = model.objects.filter(id = pk).first()
if object.approved:
return Response({"error":f"Approved {model.__name__} can't preform this action "},status=400)
if object.canceled:
return Response({"error":f"Canceled {model.__name__} can't preform this action "},status=400)
return func(self, request, pk ,*args, **kwargs)
return wrapper
return decorator

View File

@@ -0,0 +1,19 @@
from functools import wraps
from django.http import HttpResponseForbidden
from django.utils.decorators import method_decorator
def or_condition(*decorators):
"""
Combines multiple decorators with OR logic.
"""
def decorator(view_func):
@wraps(view_func)
def _wrapped_view(request, *args, **kwargs):
# Check if any of the decorators allow access
for dec in decorators:
if dec(view_func)(request, *args, **kwargs):
return view_func(request, *args, **kwargs)
# If none of the decorators allow access, return forbidden response
return HttpResponseForbidden("You don't have permission to access this page.")
return _wrapped_view
return decorator

View File

@@ -0,0 +1,11 @@
from django_filters import FilterSet
import django_filters
from asset.models import *
class AssetCategoryFilter(FilterSet):
search = django_filters.CharFilter(field_name='asset_category_name', lookup_expr="icontains")
class Meta:
model = AssetCategory
fields = "__all__"

View File

@@ -0,0 +1,66 @@
from django.http import QueryDict
from rest_framework.pagination import PageNumberPagination
from employee.models import EmployeeWorkInformation
from collections import Counter
from django.db.models import Q
def get_filter_url(current_url, request):
url_parts = current_url.split('?')
base_url = request.path
query_params = QueryDict(url_parts[1], mutable=True)
query_params.pop('groupby_field', None)
return base_url + '?' + query_params.urlencode()
def groupby_queryset(request, url, field_name, queryset):
queryset_with_counts = queryset.values(
field_name)
counts = Counter(item[field_name] for item in queryset_with_counts)
result_list = []
for i in counts:
result_list.append({field_name: i, 'count': counts[i]})
counts_and_objects = []
url = get_filter_url(url, request)
for item in result_list:
count = item['count']
related_fields = field_name.split("__")
if item[field_name]:
related_obj = queryset.filter(
**{field_name: item[field_name]}).first()
for field in related_fields:
related_obj = getattr(related_obj, field)
counts_and_objects.append(
{'count': count,
'name': str(related_obj),
"filter_url": f"{url}&{field_name}={item[field_name]}"})
pagination = PageNumberPagination()
page = pagination.paginate_queryset(counts_and_objects, request)
return pagination.get_paginated_response(page)
def permission_based_queryset(user, perm, queryset, user_obj=None):
if user.has_perm(perm):
return queryset
employee = user.employee_get
is_manager = EmployeeWorkInformation.objects.filter(
reporting_manager_id=employee
).exists()
if is_manager:
if user_obj:
return queryset.filter(
Q(employee_id=employee) |
Q(employee_id__employee_work_info__reporting_manager_id=employee)
)
manager_filter = Q(employee_id=employee)
subordinates_filter = Q(
employee_id__employee_work_info__reporting_manager_id=employee)
merged_filter = manager_filter | subordinates_filter
merged_queryset = queryset.filter(merged_filter)
return merged_queryset
return queryset.filter(employee_id=employee)

View File

@@ -0,0 +1,31 @@
from django.http import QueryDict
from employee.models import Employee
from rest_framework.pagination import PageNumberPagination
from base.models import *
from employee.models import *
def get_next_badge_id():
"""
This method is used to generate badge id
"""
try:
highest_badge_id = Employee.objects.filter(
badge_id__isnull=False).order_by('-badge_id').first().badge_id
except AttributeError:
highest_badge_id = None
# Increment the badge_id if it exists, otherwise start from '1'
if highest_badge_id:
if '#' in highest_badge_id:
prefix, number = highest_badge_id.split(
'#') # Split prefix and number
# Increment the number
new_number = str(int(number) + 1).zfill(len(number))
new_badge_id = f"{prefix}#{new_number}"
else:
# Add number to existing prefix
new_badge_id = f"{highest_badge_id}#001"
else:
new_badge_id = "EMP#001" # Default start badge ID if no employees exist
return new_badge_id

View File

@@ -0,0 +1,137 @@
from rest_framework import serializers
from asset.models import *
class AssetCategorySerializer(serializers.ModelSerializer):
asset_count = serializers.SerializerMethodField()
class Meta:
model = AssetCategory
exclude = ['created_at', 'created_by', 'company_id', 'is_active']
def get_asset_count(self, obj):
return obj.asset_set.all().count()
class AssetCategoryMiniSerializer(serializers.ModelSerializer):
class Meta:
model = AssetCategory
fields = ['id', 'asset_category_name']
def get_asset_count(self, obj):
return obj.asset_set.all().count()
class AssetLotSerializer(serializers.ModelSerializer):
class Meta:
model = AssetLot
fields = '__all__'
class AssetGetAllSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = ['id', 'asset_name', 'asset_status']
class AssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = '__all__'
class AssetAssignmentSerializer(serializers.ModelSerializer):
class Meta:
model = AssetAssignment
fields = '__all__'
class AssetAssignmentGetSerializer(serializers.ModelSerializer):
asset = serializers.SerializerMethodField()
asset_category = serializers.SerializerMethodField()
allocated_user = serializers.SerializerMethodField()
class Meta:
model = AssetAssignment
fields = ['id', 'asset', 'asset_category', 'allocated_user', 'assigned_date', 'return_status']
def get_asset(self, obj):
return obj.asset_id.asset_name
def get_asset_category(self, obj):
return obj.asset_id.asset_category_id.asset_category_name
def get_allocated_user(self, obj):
return EmployeeGetSerializer(obj.assigned_to_employee_id).data
class AssetRequestSerializer(serializers.ModelSerializer):
class Meta:
model = AssetRequest
fields = '__all__'
class AssetRequestGetSerializer(serializers.ModelSerializer):
asset_category_id = serializers.SerializerMethodField()
requested_employee_id = serializers.SerializerMethodField()
class Meta:
model = AssetRequest
fields = '__all__'
def get_asset_category_id(self, obj):
return AssetCategoryMiniSerializer(obj.asset_category_id).data
def get_requested_employee_id(self, obj):
return EmployeeGetSerializer(obj.requested_employee_id).data
class EmployeeGetSerializer(serializers.ModelSerializer):
full_name = serializers.SerializerMethodField()
class Meta:
model = Employee
fields = ['id', 'full_name', 'employee_profile', 'badge_id']
def get_full_name(self, obj):
return obj.get_full_name()
class AssetApproveSerializer(serializers.ModelSerializer):
class Meta:
model = AssetAssignment
fields = ['id', 'asset_id', 'assigned_to_employee_id', 'assigned_by_employee_id', 'assign_images']
def validate_asset_id(self, value):
asset_request = self.context.get('asset_request')
asset_category = asset_request.asset_category_id
if value.asset_category_id != asset_category:
raise serializers.ValidationError("Invalid asset.")
return value
class AssetReturnSerializer(serializers.ModelSerializer):
return_status = serializers.CharField(required=True)
image = serializers.FileField(required=True)
class Meta:
model = AssetAssignment
fields = ['return_status', 'return_condition', 'image']
def validate_return_status(self, value):
if value not in [status[0] for status in AssetAssignment.STATUS]:
raise serializers.ValidationError("Invalid Choice")
return value
def validate(self, data):
if self.instance.return_date:
raise serializers.ValidationError("Already Returned")
return data
# class ReturnImageSerializer(serializers.ModelSerializer):
# image = serializers.FileField(required=True)
# class Meta:
# model = ReturnImages
# fields = '__all__'

View File

@@ -0,0 +1,200 @@
from rest_framework import serializers
from attendance.models import *
from recruitment.models import RecruitmentMailTemplate
class AttendanceSerializer(serializers.ModelSerializer):
employee_first_name = serializers.CharField(
source="employee_id.employee_first_name", read_only=True)
employee_last_name = serializers.CharField(
source="employee_id.employee_last_name", read_only=True)
shift_name = serializers.CharField(
source="shift_id.employee_shift", read_only=True)
badge_id = serializers.CharField(
source="employee_id.badge_id", read_only=True)
employee_profile_url = serializers.SerializerMethodField(read_only=True)
work_type = serializers.CharField(source="work_type_id.work_type",read_only=True)
class Meta:
model = Attendance
exclude = [
"overtime_second",
"at_work_second",
"attendance_day",
"request_description",
"approved_overtime_second",
"request_type",
"requested_data",
"is_validate_request",
"is_validate_request_approved",
"attendance_overtime",
]
def validate(self, data):
# Check if attendance exists for the employee on the current date
if self.instance:
return data
employee_id = data.get('employee_id')
attendance_date = data.get('attendance_date', date.today())
if Attendance.objects.filter(employee_id=employee_id, attendance_date=attendance_date).exists():
raise ValidationError(
("Attendance for this employee on the current date already exists."))
return data
def get_employee_profile_url(self, obj):
try:
employee_profile = obj.employee_id.employee_profile
return employee_profile.url
except:
return None
class AttendanceRequestSerializer(serializers.ModelSerializer):
employee_first_name = serializers.CharField(
source="employee_id.employee_first_name", read_only=True)
employee_last_name = serializers.CharField(
source="employee_id.employee_last_name", read_only=True)
shift_name = serializers.CharField(
source="shift_id.employee_shift", read_only=True)
badge_id = serializers.CharField(
source="employee_id.badge_id", read_only=True)
employee_profile_url = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Attendance
exclude = [
"attendance_overtime",
"attendance_overtime_approve",
"attendance_validated",
"approved_overtime_second",
"is_validate_request",
"is_validate_request_approved",
"request_type",
"created_at",
]
def create(self, validated_data):
# Extract relevant data from validated_data
employee_id = validated_data.get("employee_id")
attendance_date = validated_data.get("attendance_date")
# Check if attendance exists for the employee and date
attendances = Attendance.objects.filter(
employee_id=employee_id, attendance_date=attendance_date
)
data = {
"employee_id": validated_data.get("employee_id"),
"attendance_date": validated_data.get("attendance_date"),
"attendance_clock_in_date": validated_data.get("attendance_clock_in_date"),
"attendance_clock_in": validated_data.get("attendance_clock_in"),
"attendance_clock_out": validated_data.get("attendance_clock_out"),
"attendance_clock_out_date": validated_data.get("attendance_clock_out_date"),
"shift_id": validated_data.get("shift_id"),
"work_type_id": validated_data.get("work_type_id"),
"attendance_worked_hour": validated_data.get("attendance_worked_hour"),
"minimum_hour": validated_data.get("minimum_hour"),
}
if attendances.exists():
data["employee_id"] = employee_id.id
data["attendance_date"] = str(attendance_date)
data["attendance_clock_in_date"] = self.data["attendance_clock_in_date"]
data["attendance_clock_in"] = self.data["attendance_clock_in"]
data["attendance_clock_out"] = (
None
if data["attendance_clock_out"] == "None"
else data["attendance_clock_out"]
)
data["attendance_clock_out_date"] = (
None
if data["attendance_clock_out_date"] == "None"
else data["attendance_clock_out_date"]
)
data["work_type_id"] = self.data["work_type_id"]
data["shift_id"] = self.data["shift_id"]
attendance = attendances.first()
for key, value in data.items():
data[key] = str(value)
attendance.requested_data = json.dumps(data)
attendance.is_validate_request = True
if attendance.request_type != "create_request":
attendance.request_type = "update_request"
attendance.request_description = self.data["request_description"]
return attendance.save()
new_instance = Attendance(**data)
new_instance.is_validate_request = True
new_instance.attendance_validated = False
new_instance.request_description = self.data["request_description"]
new_instance.request_type = "create_request"
new_instance.save()
return new_instance
def update(self, instance, validated_data):
if 'employee_id' in validated_data:
validated_data.pop('employee_id')
return super().update(instance, validated_data)
def get_employee_profile_url(self, obj):
try:
employee_profile = obj.employee_id.employee_profile
return employee_profile.url
except:
return None
class AttendanceOverTimeSerializer(serializers.ModelSerializer):
badge_id = serializers.CharField(
source='employee_id.badge_id', read_only=True)
employee_first_name = serializers.CharField(
source="employee_id.employee_first_name", read_only=True)
employee_last_name = serializers.CharField(
source="employee_id.employee_last_name", read_only=True)
employee_profile_url = serializers.SerializerMethodField(read_only=True)
class Meta:
model = AttendanceOverTime
fields = [
"id",
"employee_first_name",
"employee_last_name",
"employee_profile_url",
"badge_id",
"employee_id",
"month",
"year",
"worked_hours",
"pending_hours",
"overtime",
]
def get_employee_profile_url(self, obj):
try:
employee_profile = obj.employee_id.employee_profile
return employee_profile.url
except:
return None
class AttendanceLateComeEarlyOutSerializer(serializers.ModelSerializer):
employee_first_name = serializers.CharField(
source="employee_id.employee_first_name", read_only=True)
employee_last_name = serializers.CharField(
source="employee_id.employee_last_name", read_only=True)
class Meta:
model = AttendanceLateComeEarlyOut
fields = '__all__'
class AttendanceActivitySerializer(serializers.ModelSerializer):
employee_first_name = serializers.CharField(
source="employee_id.employee_first_name", read_only=True)
employee_last_name = serializers.CharField(
source="employee_id.employee_last_name", read_only=True)
class Meta:
model = AttendanceActivity
fields = "__all__"
class MailTemplateSerializer(serializers.ModelSerializer):
class Meta:
model = RecruitmentMailTemplate
fields = "__all__"

View File

@@ -0,0 +1,12 @@
from rest_framework import serializers
from employee.models import Employee
class GetEmployeeSerializer(serializers.ModelSerializer):
full_name = serializers.SerializerMethodField()
class Meta:
model = Employee
fields = ['id', 'full_name', 'employee_profile']
def get_full_name(self, obj):
return obj.get_full_name()

View File

@@ -0,0 +1,398 @@
import django
from django.core.exceptions import ValidationError as DjangoValidationError
from datetime import timezone
from rest_framework import serializers
from rest_framework.serializers import ValidationError
from base.models import Company, Department, EmployeeShift, EmployeeShiftDay, EmployeeShiftSchedule, JobPosition, JobRole, RotatingShift, RotatingShiftAssign, RotatingWorkType, RotatingWorkTypeAssign, ShiftRequest, WorkType, WorkTypeRequest
from employee.models import Actiontype
from employee.models import Employee
from django.http import QueryDict
class CompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = "__all__"
class JobPositionSerializer(serializers.ModelSerializer):
class Meta:
model = JobPosition
fields = "__all__"
class JobRoleSerializer(serializers.ModelSerializer):
class Meta:
model = JobRole
fields = "__all__"
class DepartmentSerializer(serializers.ModelSerializer):
class Meta:
model = Department
fields = "__all__"
def create(self, validated_data):
comapny_id = validated_data.pop('company_id', [])
obj = Department(**validated_data)
obj.save()
obj.company_id.set(comapny_id)
return obj
class WorkTypeSerializer(serializers.ModelSerializer):
class Meta:
model = WorkType
fields = '__all__'
def validate(self, attrs):
# Create an instance of the model with the provided data
instance = WorkType(**attrs)
# Call the model's clean method for validation
try:
instance.clean()
except DjangoValidationError as e:
# Raise DRF's ValidationError with the same message
raise serializers.ValidationError(e)
return attrs
def create(self, validated_data):
return super().create(validated_data)
def update(self, instance, validated_data):
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.clean() # Call clean method before saving the instance
instance.save()
return instance
class RotatingWorkTypeSerializer(serializers.ModelSerializer):
class Meta:
model = RotatingWorkType
fields = '__all__'
def validate(self, attrs):
# Create an instance of the model with the provided data
instance = RotatingWorkType(**attrs)
# Call the model's clean method for validation
try:
instance.clean()
except DjangoValidationError as e:
# Raise DRF's ValidationError with the same message
raise serializers.ValidationError(e)
return attrs
def create(self, validated_data):
return super().create(validated_data)
def update(self, instance, validated_data):
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.clean() # Call clean method before saving the instance
instance.save()
return instance
class RotatingWorkTypeAssignSerializer(serializers.ModelSerializer):
rotating_work_type_name = serializers.SerializerMethodField(read_only=True)
current_work_type_name = serializers.SerializerMethodField(read_only=True)
next_work_type_name = serializers.SerializerMethodField(read_only=True)
class Meta:
model = RotatingWorkTypeAssign
fields = '__all__'
def get_current_work_type_name(self, instance):
current_work_type = instance.current_work_type
if current_work_type:
return current_work_type.work_type
else:
return None # Return null if previous_work_type_id doesn't exist
def get_next_work_type_name(self, instance):
next_work_type = instance.next_work_type
if next_work_type:
return next_work_type.work_type
else:
return None # Return null if previous_work_type_id doesn't exist
def get_rotating_work_type_name(self, instance):
rotating_work_type_id = instance.rotating_work_type_id
if rotating_work_type_id:
return rotating_work_type_id.name
else:
return None # Return null if previous_work_type_id doesn't exist
def validate(self, attrs):
if self.instance:
return attrs
# Create an instance of the model with the provided data
instance = RotatingWorkTypeAssign(**attrs)
# Call the model's clean method for validation
try:
instance.clean()
except DjangoValidationError as e:
# Raise DRF's ValidationError with the same message
raise serializers.ValidationError(e)
return attrs
def create(self, validated_data):
return super().create(validated_data)
def update(self, instance, validated_data):
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
return instance
class EmployeeShiftDaySerializer(serializers.ModelSerializer):
class Meta:
model = EmployeeShiftDay
fields = '__all__'
class EmployeeShiftSerializer(serializers.ModelSerializer):
class Meta:
model = EmployeeShift
fields = '__all__'
def validate(self, attrs):
# Create an instance of the model with the provided data
instance = EmployeeShift(**attrs)
# Call the model's clean method for validation
try:
instance.clean()
except DjangoValidationError as e:
# Raise DRF's ValidationError with the same message
raise serializers.ValidationError(e)
return attrs
def create(self, validated_data):
return super().create(validated_data)
def update(self, instance, validated_data):
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.clean() # Call clean method before saving the instance
instance.save()
return instance
class EmployeeShiftScheduleSerializer(serializers.ModelSerializer):
class Meta:
model = EmployeeShiftSchedule
fields = '__all__'
class RotatingShiftSerializer(serializers.ModelSerializer):
class Meta:
model = RotatingShift
fields = '__all__'
def validate(self, attrs):
# Create an instance of the model with the provided data
instance = RotatingShift(**attrs)
# Call the model's clean method for validation
try:
instance.clean()
except DjangoValidationError as e:
# Raise DRF's ValidationError with the same message
raise serializers.ValidationError(e)
return attrs
def create(self, validated_data):
return super().create(validated_data)
def update(self, instance, validated_data):
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.clean() # Call clean method before saving the instance
instance.save()
return instance
class RotatingShiftAssignSerializer(serializers.ModelSerializer):
current_shift_name = serializers.SerializerMethodField(read_only=True)
next_shift_name = serializers.SerializerMethodField(read_only=True)
rotating_shift_name = serializers.SerializerMethodField(read_only=True)
rotate = serializers.CharField(read_only=True)
class Meta:
model = RotatingShiftAssign
fields = '__all__'
def validate(self, attrs):
# Create an instance of the model with the provided data
instance = RotatingShiftAssign(**attrs)
# Call the model's clean method for validation
try:
instance.clean()
except DjangoValidationError as e:
# Raise DRF's ValidationError with the same message
raise serializers.ValidationError(e)
return attrs
def create(self, validated_data):
return super().create(validated_data)
def to_representation(self, instance):
representation = super().to_representation(instance)
if instance.based_on == 'after':
representation['rotate'] = f"Rotate after {instance.rotate_after_day} days"
elif instance.based_on == 'weekly':
representation['rotate'] = f"Weekly every {instance.rotate_every_weekend}"
elif instance.based_on == 'monthly':
if instance.rotate_every == '1':
representation['rotate'] = f"Rotate every {instance.rotate_every}st day of month"
elif instance.rotate_every == '2':
representation['rotate'] = f"Rotate every {instance.rotate_every}nd day of month"
elif instance.rotate_every == '3':
representation['rotate'] = f"Rotate every {instance.rotate_every}rd day of month"
elif instance.rotate_every == 'last':
representation['rotate'] = "Rotate every last day of month"
else:
representation['rotate'] = f"Rotate every {instance.rotate_every}th day of month"
return representation
def get_rotating_shift_name(self, instance):
rotating_shift_id = instance.rotating_shift_id
if rotating_shift_id:
return rotating_shift_id.name
else:
return None # Return null if previous_work_type_id doesn't exist
def get_next_shift_name(self, instance):
next_shift = instance.next_shift
if next_shift:
return next_shift.employee_shift
else:
return None # Return null if previous_work_type_id doesn't exist
def get_current_shift_name(self, instance):
current_shift = instance.current_shift
if current_shift:
return current_shift.employee_shift
else:
return None # Return null if previous_work_type_id doesn't exist
def update(self, instance, validated_data):
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.clean() # Call clean method before saving the instance
instance.save()
return instance
class WorkTypeRequestSerializer(serializers.ModelSerializer):
employee_first_name = serializers.CharField(
source='employee_id.employee_first_name', read_only=True)
employee_last_name = serializers.CharField(
source='employee_id.employee_last_name', read_only=True)
work_type_name = serializers.CharField(
source='work_type_id.work_type', read_only=True)
previous_work_type_name = serializers.SerializerMethodField(read_only=True)
class Meta:
model = WorkTypeRequest
fields = '__all__'
def validate(self, attrs):
# Create an instance of the model with the provided data
instance = WorkTypeRequest(**attrs)
# Call the model's clean method for validation
try:
instance.clean()
except DjangoValidationError as e:
# Raise DRF's ValidationError with the same message
raise serializers.ValidationError(e)
return attrs
def create(self, validated_data):
return super().create(validated_data)
def get_previous_work_type_name(self, instance):
previous_work_type = instance.previous_work_type_id
if previous_work_type:
return previous_work_type.work_type
else:
return None # Return null if previous_work_type_id doesn't exist
def update(self, instance, validated_data):
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.clean() # Call clean method before saving the instance
instance.save()
return instance
class ShiftRequestSerializer(serializers.ModelSerializer):
employee_first_name = serializers.CharField(
source='employee_id.employee_first_name', read_only=True)
employee_last_name = serializers.CharField(
source='employee_id.employee_last_name', read_only=True)
shift_name = serializers.SerializerMethodField(read_only=True)
previous_shift_name = serializers.SerializerMethodField(read_only=True)
def get_previous_shift_name(self, instance):
previous_shift_id = instance.previous_shift_id
if previous_shift_id:
return previous_shift_id.employee_shift
else:
return None # Re
def get_shift_name(self, instance):
shift_id = instance.shift_id
if shift_id:
return shift_id.employee_shift
else:
return None # Re
def validate(self, attrs):
# Create an instance of the model with the provided data
instance = ShiftRequest(**attrs)
# Call the model's clean method for validation
try:
instance.clean()
except DjangoValidationError as e:
# Raise DRF's ValidationError with the same message
raise serializers.ValidationError(e)
return attrs
def create(self, validated_data):
return super().create(validated_data)
def update(self, instance, validated_data):
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.clean() # Call clean method before saving the instance
instance.save()
return instance
class Meta:
model = ShiftRequest
fields = '__all__'
class ActiontypeSerializer(serializers.ModelSerializer):
class Meta:
model = Actiontype
fields = ['id', 'title', 'action_type']

View File

@@ -0,0 +1,128 @@
from employee.models import Policy
from rest_framework import serializers
from base.models import Department, EmployeeType, JobPosition
from employee.models import DisciplinaryAction, Employee, EmployeeBankDetails, EmployeeWorkInformation
from ...api_methods.employee.methods import get_next_badge_id
from horilla_documents.models import Document, DocumentRequest
class EmployeeListSerializer(serializers.ModelSerializer):
job_position_name = serializers.CharField(
source='employee_work_info.job_position_id.job_position', read_only=True)
employee_work_info_id = serializers.CharField(
source="employee_work_info.id", read_only=True)
employee_bank_details_id = serializers.CharField(
source="employee_bank_details.id", read_only=True)
class Meta:
model = Employee
fields = ['id','employee_first_name', 'employee_last_name',
'email', 'job_position_name', 'employee_work_info_id', 'employee_profile', 'employee_bank_details_id']
class EmployeeSerializer(serializers.ModelSerializer):
job_position_name = serializers.CharField(
source='employee_work_info.job_position_id.job_position', read_only=True)
job_position_id = serializers.CharField(
source='employee_work_info.job_position_id.id', read_only=True)
employee_work_info_id = serializers.CharField(
source="employee_work_info.id", read_only=True)
employee_bank_details_id = serializers.CharField(
source="employee_bank_details.id", read_only=True)
class Meta:
model = Employee
fields = "__all__"
def create(self, validated_data):
validated_data['badge_id'] = get_next_badge_id()
return super().create(validated_data)
class EmployeeWorkInformationSerializer(serializers.ModelSerializer):
job_position_name = serializers.CharField(
source='job_position_id.job_position', read_only=True)
department_name = serializers.CharField(
source='department_id.department', read_only=True)
shift_name = serializers.CharField(
source='shift_id.employee_shift', read_only=True)
employee_type_name = serializers.CharField(
source='employee_type_id.employee_type', read_only=True)
reporting_manager_first_name = serializers.CharField(
source='reporting_manager_id.employee_first_name', read_only=True)
reporting_manager_last_name = serializers.CharField(
source='reporting_manager_id.employee_last_name', read_only=True)
work_type_name = serializers.CharField(
source='work_type_id.work_type', read_only=True)
company_name = serializers.CharField(
source='company_id.company', read_only=True)
class Meta:
model = EmployeeWorkInformation
fields = "__all__"
class EmployeeBankDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = EmployeeBankDetails
fields = '__all__'
class EmployeeTypeSerializer(serializers.ModelSerializer):
class Meta:
model = EmployeeType
fields = '__all__'
class EmployeeBulkUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
# fields = [
# 'employee_last_name',
# 'address',
# 'country',
# 'state',
# 'city',
# 'zip',
# 'dob',
# 'gender',
# 'qualification',
# 'experience',
# 'marital_status',
# 'children',
# ]
fields = [
'employee_last_name',
]
class DisciplinaryActionSerializer(serializers.ModelSerializer):
class Meta:
model = DisciplinaryAction
fields = '__all__'
class PolicySerializer(serializers.ModelSerializer):
class Meta:
model = Policy
fields = '__all__'
class DocumentRequestSerializer(serializers.ModelSerializer):
class Meta:
model = DocumentRequest
fields = '__all__'
class DocumentSerializer(serializers.ModelSerializer):
class Meta:
model = Document
fields = '__all__'
class EmployeeSelectorSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
fields = ['id','employee_first_name','employee_last_name','badge_id','employee_profile']

View File

@@ -0,0 +1,504 @@
from rest_framework import serializers
from leave.models import *
from leave.forms import calculate_requested_days, cal_effective_requested_days
from employee.models import Employee
def leave_Validations(self, data):
start_date = data.get("start_date")
end_date = data.get("end_date")
start_date_breakdown = (
data.get("start_date_breakdown")
if data.get("start_date_breakdown") is not None
else "full_day"
)
end_date_breakdown = (
data.get("end_date_breakdown")
if data.get("end_date_breakdown") is not None
else "full_day"
)
employee = data.get("employee_id")
leave_type_id = data.get("leave_type_id")
attachment = data.get("attachment")
available_leave = (
AvailableLeave.objects.filter(
leave_type_id=leave_type_id, employee_id=employee
)[0]
if AvailableLeave.objects.filter(
leave_type_id=leave_type_id, employee_id=employee
).exists()
else None
)
if not available_leave:
raise serializers.ValidationError(
f"Employee is not assigned with leave type {leave_type_id}."
)
requested_days = calculate_requested_days(
start_date, end_date, start_date_breakdown, end_date_breakdown
)
effective_requested_days = cal_effective_requested_days(
start_date=start_date,
end_date=end_date,
leave_type_id=leave_type_id,
requested_days=requested_days,
)
total_leave_days = (
available_leave.available_days + available_leave.carryforward_days
)
errors = {}
# checking if there is any requested days is overlapping with the existing leave request
leave_requests = employee.leaverequest_set.filter(
start_date__lte=end_date, end_date__gte=start_date
)
if self.instance:
leave_requests = leave_requests.exclude(id=self.instance.id)
if leave_requests:
raise serializers.ValidationError(
"There is already a leave request for this date range."
)
# checking if the end date is less than the start date
if not start_date <= end_date:
errors["end_date"] = ["End date should not be less than start date."]
if start_date == end_date and start_date_breakdown != end_date_breakdown:
raise serializers.ValidationError(
"There is a mismatch in the breakdown of the start date and end date."
)
if not effective_requested_days <= total_leave_days:
raise serializers.ValidationError("Employee doesn't have enough leave days..")
if leave_type_id.require_attachment == "yes" and attachment == None:
errors["attachment"] = ["This field is required."]
if errors:
raise serializers.ValidationError(errors)
class GetAvailableLeaveTypeSerializer(serializers.ModelSerializer):
leave_type_id = serializers.SerializerMethodField()
icon = serializers.SerializerMethodField()
class Meta:
model = AvailableLeave
fields = [
"id",
"leave_type_id",
"icon",
"available_days",
"carryforward_days",
"total_leave_days",
]
def get_leave_type_id(self, obj):
if obj.leave_type_id:
return LeaveTypeAllGetSerializer(obj.leave_type_id).data
return None
def get_icon(self, obj):
try:
return obj.leave_type_id.icon.url
except:
return None
class GetAvailableLeaveTypeSerializer(serializers.ModelSerializer):
leave_type_id = serializers.SerializerMethodField()
icon = serializers.SerializerMethodField()
total_leave_days = serializers.SerializerMethodField()
class Meta:
model = AvailableLeave
fields = [
"id",
"leave_type_id",
"icon",
"available_days",
"carryforward_days",
"total_leave_days",
]
def get_leave_type_id(self, obj):
if obj.leave_type_id:
return LeaveTypeAllGetSerializer(obj.leave_type_id).data
return None
def get_icon(self, obj):
try:
return obj.leave_type_id.icon.url
except:
return None
def get_total_leave_days(self, obj):
return obj.available_days + obj.carryforward_days
class userLeaveRequestGetAllSerilaizer(serializers.ModelSerializer):
leave_type_id = serializers.SerializerMethodField()
class Meta:
model = LeaveRequest
exclude = [
"requested_date",
"description",
"attachment",
"approved_available_days",
"approved_carryforward_days",
"created_at",
"reject_reason",
"employee_id",
"created_by",
]
def get_leave_type_id(self, obj):
if obj.leave_type_id:
return LeaveTypeAllGetSerializer(obj.leave_type_id).data
return None
class UserLeaveRequestGetSerilaizer(serializers.ModelSerializer):
leave_type_id = serializers.SerializerMethodField()
class Meta:
model = LeaveRequest
exclude = [
"requested_date",
"approved_available_days",
"approved_carryforward_days",
"created_at",
"reject_reason",
"employee_id",
"created_by",
]
def get_leave_type_id(self, obj):
if obj.leave_type_id:
return LeaveTypeAllGetSerializer(obj.leave_type_id).data
return None
class LeaveRequestCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = LeaveRequest
fields = [
"employee_id",
"leave_type_id",
"start_date",
"start_date_breakdown",
"end_date",
"end_date_breakdown",
"description",
"attachment",
]
def validate(self, data):
leave_Validations(self, data)
return data
class UpdateLeaveRequestSerializer(serializers.ModelSerializer):
class Meta:
model = LeaveRequest
fields = [
"start_date",
"start_date_breakdown",
"end_date",
"end_date_breakdown",
"description",
"attachment",
]
def validate(self, data):
leave_Validations(self, data)
return data
class LeaveTypeGetCreateSerilaizer(serializers.ModelSerializer):
class Meta:
model = LeaveType
fields = "__all__"
def validate(self, data):
reset = data.get("reset")
reset_based = data.get("reset_based")
reset_month = data.get("reset_month")
reset_day = data.get("reset_day")
reset_weekday = data.get("reset_weekday")
carryforward_type = data.get("carryforward_type")
carryforward_max = data.get("carryforward_max")
if reset == True:
if reset_based == None:
raise serializers.ValidationError(
{"reset_based": ["This field is required."]}
)
elif reset_based == "yearly" and reset_month == None:
raise serializers.ValidationError(
{"reset_month": ["This field is required."]}
)
elif reset_based in ["yearly", "monthly"] and reset_day == "":
raise serializers.ValidationError(
{"reset_day": ["This field is required."]}
)
elif reset_based == "weekly" and reset_weekday == None:
raise serializers.ValidationError(
{"reset_weekday": ["This field is required."]}
)
# elif carryforward_type in ['carryforward', 'carryforward expire'] and carryforward_max
return data
class LeaveTypeAllGetSerializer(serializers.ModelSerializer):
class Meta:
model = LeaveType
fields = ["id", "name", "icon"]
class LeaveAllocationRequestCreateSerializer(serializers.ModelSerializer):
requested_days = serializers.FloatField(required=True)
class Meta:
model = LeaveAllocationRequest
fields = [
"leave_type_id",
"employee_id",
"requested_days",
"created_by",
"description",
"attachment",
]
class AssignLeaveCreateSerializer(serializers.Serializer):
leave_type_ids = serializers.PrimaryKeyRelatedField(
many=True, queryset=LeaveType.objects.all()
)
employee_ids = serializers.PrimaryKeyRelatedField(
many=True, queryset=Employee.objects.all()
)
def validate_leave_type_ids(self, value):
if not value:
raise serializers.ValidationError(
{"leave_type_ids": ["This field is required."]}
)
return value
def validate_employee_ids(self, value):
if not value:
raise serializers.ValidationError(
{"employee_ids": ["This field is required."]}
)
return value
class AssignLeaveGetSerializer(serializers.ModelSerializer):
employee_id = serializers.SerializerMethodField()
leave_type_id = serializers.SerializerMethodField()
class Meta:
model = AvailableLeave
exclude = ["reset_date", "expired_date"]
def get_employee_id(self, obj):
employee = obj.employee_id
if employee:
return EmployeeGetSerializer(employee).data
return None
def get_leave_type_id(self, obj):
if obj.leave_type_id:
return LeaveTypeAllGetSerializer(obj.leave_type_id).data
return None
class EmployeeGetSerializer(serializers.ModelSerializer):
full_name = serializers.SerializerMethodField()
class Meta:
model = Employee
fields = ["id", "full_name", "employee_profile", "badge_id"]
def get_full_name(self, obj):
return obj.get_full_name()
class AvailableLeaveUpdateSerializer(serializers.ModelSerializer):
available_days = serializers.FloatField(required=True)
class Meta:
model = AvailableLeave
fields = ["available_days", "carryforward_days"]
class LeaveRequestGetAllSerilaizer(serializers.ModelSerializer):
employee_id = serializers.SerializerMethodField()
leave_type_id = serializers.SerializerMethodField()
multiple_approve = serializers.SerializerMethodField()
class Meta:
model = LeaveRequest
exclude = [
"requested_date",
"description",
"attachment",
"approved_available_days",
"approved_carryforward_days",
"created_at",
"reject_reason",
"created_by",
]
def get_employee_id(self, obj):
employee = obj.employee_id
if employee:
return EmployeeGetSerializer(employee).data
return None
def get_leave_type_id(self, obj):
if obj.leave_type_id:
return LeaveTypeAllGetSerializer(obj.leave_type_id).data
return None
def get_multiple_approve(self, obj):
approvals = LeaveRequestConditionApproval.objects.filter(leave_request_id=obj)
employee = self.context["request"].user.employee_get
if approvals and obj.status == "requested":
is_approved = approvals.filter(is_approved=True)
count = f"{is_approved.count()} / {approvals.count()}"
is_approved = (
True if is_approved.filter(manager_id=employee).exists() else False
)
return {"count": count, "is_approved": is_approved}
return None
class LeaveRequestGetSerilaizer(serializers.ModelSerializer):
employee_id = serializers.SerializerMethodField()
leave_type_id = serializers.SerializerMethodField()
multiple_approve = serializers.SerializerMethodField()
class Meta:
model = LeaveRequest
exclude = [
"requested_date",
"approved_available_days",
"approved_carryforward_days",
"created_at",
"reject_reason",
"created_by",
]
def get_employee_id(self, obj):
employee = obj.employee_id
if employee:
return EmployeeGetSerializer(employee).data
return None
def get_leave_type_id(self, obj):
if obj.leave_type_id:
return LeaveTypeAllGetSerializer(obj.leave_type_id).data
return None
def get_multiple_approve(self, obj):
approvals = LeaveRequestConditionApproval.objects.filter(leave_request_id=obj)
employee = self.context["request"].user.employee_get
if approvals and obj.status == "requested":
is_approved = approvals.filter(is_approved=True)
count = f"{is_approved.count()} / {approvals.count()}"
is_approved = (
True if is_approved.filter(manager_id=employee).exists() else False
)
return {"count": count, "is_approved": is_approved}
return None
class LeaveAllocationRequestSerilaizer(serializers.ModelSerializer):
class Meta:
model = LeaveAllocationRequest
exclude = [
"requested_date",
"status",
"created_by",
"created_at",
"reject_reason",
]
class LeaveAllocationRequestGetSerializer(serializers.ModelSerializer):
employee_id = serializers.SerializerMethodField()
leave_type_id = serializers.SerializerMethodField()
created_by = serializers.SerializerMethodField()
class Meta:
model = LeaveAllocationRequest
exclude = ["requested_date", "created_at", "reject_reason"]
def get_employee_id(self, obj):
employee = obj.employee_id
if employee:
return EmployeeGetSerializer(employee).data
return None
def get_leave_type_id(self, obj):
if obj.leave_type_id:
return LeaveTypeAllGetSerializer(obj.leave_type_id).data
return None
def get_created_by(self, obj):
created_by = obj.created_by
if created_by:
return EmployeeGetSerializer(created_by).data
return None
class CompanyLeaveSerializer(serializers.ModelSerializer):
class Meta:
model = CompanyLeave
exclude = ["company_id"]
class HoildaySerializer(serializers.ModelSerializer):
class Meta:
model = Holiday
exclude = ["company_id"]
def validate(self, data):
start_date = data.get("start_date")
end_date = data.get("end_date")
if end_date and not start_date <= end_date:
raise serializers.ValidationError(
{"end_date": ["End date should not be less than start date."]}
)
return data
class LeaveRequestApproveSerializer(serializers.ModelSerializer):
class Meta:
model = LeaveRequest
fields = []
def validate(self, data):
leave_request = self.instance
if leave_request.status != "requested":
raise serializers.ValidationError("Nothing to approve.")
employee_id = leave_request.employee_id
leave_type_id = leave_request.leave_type_id
available_leave = AvailableLeave.objects.get(
leave_type_id=leave_type_id, employee_id=employee_id
)
total_available_leave = (
available_leave.available_days + available_leave.carryforward_days
)
if not total_available_leave >= leave_request.requested_days:
raise serializers.ValidationError(
f"{employee_id} dont have enough leave days to approve the request.."
)
data["available_leave"] = available_leave
return data

View File

@@ -0,0 +1,8 @@
from notifications . models import Notification
from rest_framework import serializers
class NotificationSerializer(serializers.ModelSerializer):
class Meta:
model = Notification
fields = ['id','level', 'unread', 'verb', 'timestamp', 'deleted', 'data']

View File

@@ -0,0 +1,279 @@
from rest_framework import serializers
from employee.models import BonusPoint, Employee
from leave.models import LeaveType
from payroll.models.models import Allowance, Contract, Deduction, LoanAccount, MultipleCondition, Payslip, Reimbursement, ReimbursementMultipleAttachment
from payroll.models.tax_models import TaxBracket
class PayslipSerializer(serializers.ModelSerializer):
employee_first_name = serializers.CharField(
source="employee_id.employee_first_name", read_only=True)
employee_last_name = serializers.CharField(
source="employee_id.employee_last_name", read_only=True)
shift_name = serializers.CharField(
source="shift_id.employee_shift", read_only=True)
badge_id = serializers.CharField(
source="employee_id.badge_id", read_only=True)
employee_profile_url = serializers.SerializerMethodField(read_only=True)
department_name = serializers.CharField(
source='employee_id.employee_work_info.department_id.department', read_only=True)
bank_account_check_number = serializers.CharField(
source='employee_id.employee_bank_details.account_number', read_only=True)
class Meta:
model = Payslip
fields = '__all__'
# exclude = ['reference',
# 'sent_to_employee',
# 'installment_ids', 'created_at']
def get_employee_profile_url(self, obj):
try:
employee_profile = obj.employee_id.employee_profile
return employee_profile.url
except:
return None
class ContractSerializer(serializers.ModelSerializer):
employee_first_name = serializers.CharField(
source="employee_id.employee_first_name", read_only=True)
employee_last_name = serializers.CharField(
source="employee_id.employee_last_name", read_only=True)
shift_name = serializers.CharField(
source="shift_id.employee_shift", read_only=True)
badge_id = serializers.CharField(
source="employee_id.badge_id", read_only=True)
employee_profile_url = serializers.SerializerMethodField(read_only=True)
job_position_name = serializers.CharField(
source='job_position_id.job_position', read_only=True)
job_role_name = serializers.CharField(
source='job_role_id.job_role', read_only=True)
department_name = serializers.CharField(
source='department_id.department', read_only=True)
shift_name = serializers.CharField(
source='shift_id.employee_shift', read_only=True)
work_type_name = serializers.CharField(
source='work_type_id.work_type', read_only=True)
def get_employee_profile_url(self, obj):
try:
employee_profile = obj.employee_id.employee_profile
return employee_profile.url
except:
return None
class Meta:
model = Contract
fields = '__all__'
class MultipleConditionSerializer(serializers.ModelSerializer):
class Meta:
model = MultipleCondition
fields = '__all__'
class AllowanceSerializer(serializers.ModelSerializer):
specific_employees = serializers.PrimaryKeyRelatedField(
queryset=Employee.objects.all(), many=True, required=False
)
exclude_employees = serializers.PrimaryKeyRelatedField(
queryset=Employee.objects.all(), many=True, required=False
)
other_conditions = serializers.PrimaryKeyRelatedField(
queryset=MultipleCondition.objects.all(), many=True, required=False
)
class Meta:
model = Allowance
fields = '__all__'
read_only_fields = ['id', 'company_id', 'only_show_under_employee', 'is_loan']
def create(self, validated_data):
specific_employees = validated_data.pop('specific_employees', [])
exclude_employees = validated_data.pop('exclude_employees', [])
other_conditions = validated_data.pop('other_conditions', [])
allowance = Allowance.objects.create(**validated_data)
# Set the ManyToMany relationships after the instance is created
allowance.specific_employees.set(specific_employees)
allowance.exclude_employees.set(exclude_employees)
allowance.other_conditions.set(other_conditions)
return allowance
def validate(self, data):
is_fixed = data.get('is_fixed')
amount = data.get('amount')
based_on = data.get('based_on')
per_attendance_fixed_amount = data.get('per_attendance_fixed_amount')
shift_id = data.get('shift_id')
work_type_id = data.get('work_type_id')
is_condition_based = data.get('is_condition_based')
field = data.get('field')
condition = data.get('condition')
value = data.get('value')
has_max_limit = data.get('has_max_limit')
maximum_amount = data.get('maximum_amount')
if is_fixed and (amount is None or amount < 0):
raise serializers.ValidationError("If 'is_fixed' is True, 'amount' must be a positive number.")
if not is_fixed and not based_on:
raise serializers.ValidationError("If 'is_fixed' is False, 'based_on' is required.")
if based_on == "attendance" and not per_attendance_fixed_amount:
raise serializers.ValidationError("If 'based_on' is 'attendance', 'per_attendance_fixed_amount' is required.")
if based_on == "shift_id" and not shift_id:
raise serializers.ValidationError("If 'based_on' is 'shift_id', 'shift_id' is required.")
if based_on == "work_type_id" and not work_type_id:
raise serializers.ValidationError("If 'based_on' is 'work_type_id', 'work_type_id' is required.")
if is_condition_based and (not field or not value or not condition):
raise serializers.ValidationError("If 'is_condition_based' is True, 'field', 'value', and 'condition' are required.")
if has_max_limit and maximum_amount is None:
raise serializers.ValidationError("If 'has_max_limit' is True, 'maximum_amount' is required.")
return data
class DeductionSerializer(serializers.ModelSerializer):
class Meta:
model = Deduction
fields = '__all__'
class LoanAccountSerializer(serializers.ModelSerializer):
employee_profile_url = serializers.SerializerMethodField(read_only=True)
employee_full_name = serializers.CharField(source='employee_id.get_full_name',read_only=True)
badge_id = serializers.CharField(source='employee_id.badge_id',read_only=True)
job_position_name = serializers.CharField(source='employee_id.get_job_position',read_only=True)
class Meta:
model = LoanAccount
fields = '__all__'
def get_employee_profile_url(self, obj):
try:
employee_profile = obj.employee_id.employee_profile
return employee_profile.url
except:
return None
class ReimbursementSerializer(serializers.ModelSerializer):
other_attachements = serializers.SerializerMethodField()
leave_type_name = serializers.CharField(source='leave_type_id.name',read_only=True)
employee_profile_url = serializers.SerializerMethodField(read_only=True)
badge_id = serializers.CharField(source='employee_id.badge_id')
employee_full_name = serializers.CharField(source='employee_id.get_full_name')
def get_employee_profile_url(self, obj):
try:
employee_profile = obj.employee_id.employee_profile
return employee_profile.url
except:
return None
class Meta:
model = Reimbursement
fields = '__all__'
def get_other_attachements(self, obj):
attachments = []
for attachment in obj.other_attachments.all():
try:
attachments.append(attachment.attachment.url)
except :
pass
return attachments
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
exclude_fields = []
# Get type from data or instance
instance_type = getattr(self.instance, 'type', None)
if instance_type == "reimbursement":
exclude_fields.extend(
["leave_type_id", "cfd_to_encash", "ad_to_encash", "bonus_to_encash"])
elif instance_type == "leave_encashment":
exclude_fields.extend(["attachment", "amount", "bonus_to_encash"])
elif instance_type == "bonus_encashment":
exclude_fields.extend(
["attachment", "amount", "leave_type_id", "cfd_to_encash", "ad_to_encash"])
# Remove excluded fields from serializer fields
for field in exclude_fields:
self.fields.pop(field, None)
def get_encashable_leaves(self, employee):
leaves = LeaveType.objects.filter(
employee_available_leave__employee_id=employee,
employee_available_leave__total_leave_days__gte=1,
is_encashable=True,
)
return leaves
def validate(self, data):
try:
employee_id = self.instance.employee_id
type = self.instance.type
leave_type_id = self.instance.leave_type_id
except:
employee_id = data["employee_id"]
type = data["type"]
leave_type_id = data["leave_type_id"] if data.get("leave_type_id",None) else None
available_points = BonusPoint.objects.filter(
employee_id=employee_id
).first()
if type == "bonus_encashment":
try:
bonus_to_encash = self.instance.bonus_to_encash
except:
bonus_to_encash = data["bonus_to_encash"]
if available_points.points < bonus_to_encash:
raise serializers.ValidationError(
{"bonus_to_encash": "Not enough bonus points to redeem"}
)
if bonus_to_encash <= 0:
raise serializers.ValidationError(
{
"bonus_to_encash": "Points must be greater than zero to redeem."
}
)
if type == "leave_encashment":
leave_type_id = leave_type_id
encashable_leaves = self.get_encashable_leaves(employee_id)
if (leave_type_id is None) or (leave_type_id not in encashable_leaves):
raise serializers.ValidationError(
{"leave_type_id": "This leave type is not encashable"}
)
return data
def save(self, **kwargs):
multiple_attachment_ids = []
request_files = self.context['request'].FILES
attachments = request_files.getlist("attachment")
if attachments:
for attachment in attachments:
file_instance = ReimbursementMultipleAttachment()
file_instance.attachment = attachment
file_instance.save()
multiple_attachment_ids.append(file_instance.pk)
instance = super().save()
instance.other_attachments.add(*multiple_attachment_ids)
return super().save(**kwargs)
class TaxBracketSerializer(serializers.ModelSerializer):
class Meta:
fields = '__all__'
model = TaxBracket

View File

View File

@@ -0,0 +1,13 @@
from django.urls import re_path, path
from ...api_views.asset.views import *
urlpatterns = [
re_path(r'^asset-categories/(?P<pk>\d+)?$', AssetCategoryAPIView.as_view(), name='asset-category-detail'),
re_path(r'^asset-lots/(?P<pk>\d+)?$', AssetLotAPIView.as_view(), name='asset-lot-detail'),
re_path(r'^assets/(?P<pk>\d+)?$', AssetAPIView.as_view(), name='asset-detail'),
re_path(r'^asset-allocations/(?P<pk>\d+)?$', AssetAllocationAPIView.as_view(), name='asset-allocation-detail'),
re_path(r'^asset-requests/(?P<pk>\d+)?$', AssetRequestAPIView.as_view(), name='asset-request-detail'),
path('asset-return/<int:pk>', AssetReturnAPIView.as_view(), name='asset-return'),
path('asset-reject/<int:pk>', AssetRejectAPIView.as_view(), name='asset-reject'),
path('asset-approve/<int:pk>', AssetApproveAPIView.as_view(), name='asset-approve'),
]

View File

@@ -0,0 +1,53 @@
"""
horilla_api/urls/attendance/urls.py
"""
from django.urls import path
from horilla_api.api_views.attendance.views import *
from horilla_api.api_views.attendance.permission_views import AttendancePermissionCheck
urlpatterns = [
path("clock-in/", ClockInAPIView.as_view(), name="check-in"),
path("clock-out/", ClockOutAPIView.as_view(), name="check-out"),
path("attendance/", AttendanceView.as_view(), name="attendance-list"),
path("attendance/<int:pk>", AttendanceView.as_view(), name="attendance-detail"),
path(
"attendance/list/<str:type>", AttendanceView.as_view(), name="attendance-list"
),
path("attendance-validate/<int:pk>", ValidateAttendanceView.as_view(), name=""),
path(
"attendance-request/",
AttendanceRequestView.as_view(),
name="attendance-request-view",
),
path(
"attendance-request/<int:pk>",
AttendanceRequestView.as_view(),
name="attendance-request-view",
),
path(
"attendance-request-approve/<int:pk>",
AttendanceRequestApproveView.as_view(),
name="",
),
path(
"attendance-request-cancel/<int:pk>",
AttendanceRequestCancelView.as_view(),
name="",
),
path("overtime-approve/<int:pk>", OvertimeApproveView.as_view(), name=""),
path(
"attendance-hour-account/<int:pk>/", AttendanceOverTimeView.as_view(), name=""
),
path("attendance-hour-account/", AttendanceOverTimeView.as_view(), name=""),
path("late-come-early-out-view/", LateComeEarlyOutView.as_view(), name=""),
path("attendance-activity/", AttendanceActivityView.as_view(), name=""),
path("today-attendance/", TodayAttendance.as_view(), name=""),
path("offline-employees/count/", OfflineEmployeesCountView.as_view(), name=""),
path("offline-employees/list/", OfflineEmployeesListView.as_view(), name=""),
path("permission-check/attendance", AttendancePermissionCheck.as_view()),
path("checking-in", CheckingStatus.as_view()),
path("offline-employee-mail-send", OfflineEmployeeMailsend.as_view()),
path("converted-mail-template", ConvertedMailTemplateConvert.as_view()),
path("mail-templates", MailTemplateView.as_view()),
]

View File

@@ -0,0 +1,7 @@
from django.urls import path
from ...api_views.auth.views import LoginAPIView
urlpatterns = [
path('login/', LoginAPIView.as_view())
]

View File

@@ -0,0 +1,66 @@
from django.urls import re_path,path
from ...api_views.base import views
urlpatterns = [
path('job-positions/', views.JobPositionView.as_view(), name="job_position_detail"),
path('job-positions/<int:pk>/', views.JobPositionView.as_view(), name="job_position_detail_with_pk"),
path('job-roles/', views.JobRoleView.as_view(), name="job_roles_details"),
path('job-roles/<int:pk>/', views.JobRoleView.as_view(), name="job_roles_details_with_pk"),
path('companies/', views.CompanyView.as_view(), name="companies_detail"),
path('companies/<int:pk>/', views.CompanyView.as_view(), name="companies_detail_with_pk"),
path('departments/', views.DepartmentView.as_view(), name="department_detail"),
path('departments/<int:pk>/', views.DepartmentView.as_view(), name="department_detail_with_pk"),
path('worktypes/', views.WorkTypeView.as_view(), name="worktype_detail"),
path('worktypes/<int:pk>/', views.WorkTypeView.as_view(), name="worktype_detail_with_pk"),
path('rotating-worktypes/', views.RotatingWorkTypeView.as_view(), name="rotating_worktypes_detail"),
path('rotating-worktypes/<int:pk>/', views.RotatingWorkTypeView.as_view(), name="rotating_worktypes_detail_with_pk"),
path('rotating-worktype-assigns/', views.RotatingWorkTypeAssignView.as_view(), name='rotating_worktype_assign_detail'),
path('individual-rotating-worktypes/', views.IndividualRotatingWorktypesView.as_view(), name='individual-worktype'),
path('individual-rotating-worktypes/<int:pk>', views.IndividualRotatingWorktypesView.as_view(), name='individual-worktype'),
path('individual-worktype-request/', views.IndividualWorkTypeRequestView.as_view(), name='individual-worktype-request'),
path('individual-worktype-request/<int:pk>', views.IndividualWorkTypeRequestView.as_view(), name='individual-worktype-request'),
path('rotating-worktype-assigns/<int:pk>/', views.RotatingWorkTypeAssignView.as_view(), name='rotating_worktype_assign_detail_with_pk'),
path('employee-shift/', views.EmployeeShiftView.as_view(), name='employee_shift_detail'),
path('employee-shift/<int:pk>/', views.EmployeeShiftView.as_view(), name='employee_shift_detail_with_pk'),
path('employee-shift-schedules/', views.EmployeeShiftScheduleView.as_view(), name='employee_shift_schedule_detail'),
path('employee-shift-schedules/<int:pk>/', views.EmployeeShiftScheduleView.as_view(), name='employee_shift_schedule_detail_with_pk'),
path('rotating-shifts/', views.RotatingShiftView.as_view(), name='rotating_shifts_detail'),
path('rotating-shifts/<int:pk>/', views.RotatingShiftView.as_view(), name='rotating_shifts_detail_with_pk'),
path('rotating-shift-assigns/', views.RotatingShiftAssignView.as_view(), name='rotating_shift_assigns_detail'),
path('rotating-shift-assigns/<int:pk>/', views.RotatingShiftAssignView.as_view(), name='rotating_shift_assigns_detail_with_pk'),
path('individual-rotating-shifts/', views.IndividualRotatingShiftView.as_view(), name='individual-worktype-request'),
path('individual-rotating-shifts/<int:pk>', views.IndividualRotatingShiftView.as_view(), name='individual-worktype-request'),
path('worktype-requests/', views.WorkTypeRequestView.as_view(), name='worktype_requests_detail'),
path('worktype-requests/<int:pk>/', views.WorkTypeRequestView.as_view(), name='worktype_requests_detail_with_pk'),
path('worktype-requests-cancel/<int:pk>/', views.WorkTypeRequestCancelView.as_view(), name='worktype_requests_detail_with_pk'),
path('worktype-requests-approve/<int:pk>/', views.WorkRequestApproveView.as_view(), name='worktype_requests_detail_with_pk'),
path('shift-requests/', views.ShiftRequestView.as_view(), name='shift_requests_detail'),
path('shift-requests/<int:pk>/', views.ShiftRequestView.as_view(), name='shift_requests_detail_with_pk'),
path('individual-shift-request/', views.IndividualShiftRequestView.as_view(), name='individual-worktype-request'),
path('individual-shift-request/<int:pk>', views.IndividualShiftRequestView.as_view(), name='individual-worktype-request'),
path('shift-request-approve/<int:pk>', views.ShiftRequestApproveView.as_view(), name='shift-requests-approve'),
path('shift-request-bulk-approve', views.ShiftRequestBulkApproveView.as_view(), name='shift-request-bulk-approve'),
path('shift-request-cancel/<int:pk>', views.ShiftRequestCancelView.as_view(), name='shift-request-cancel'),
path('shift-request-bulk-cancel', views.ShiftRequestBulkCancelView.as_view(), name='shift-request-bulk-cancel'),
path('shift-request-delete/<int:pk>', views.ShiftRequestDeleteView.as_view(), name='shift-request-delete'),
path('shift-request-bulk-delete', views.ShiftRequestDeleteView.as_view(), name='shift-request-bulk-delete'),
path('shift-request-export', views.ShiftRequestExportView.as_view(), name='shift-request-export'),
path('shift-request-allocation/<int:id>', views.ShiftRequestAllocationView.as_view(), name='shift-request-allocation'),
path('work-type-request-export', views.WorkTypeRequestExport.as_view(), name='work-type-request-export'),
path('rotating-shift-assign-export', views.RotatingShiftAssignExport.as_view(), name='rotating-shift-assigns-export'),
path('rotating-shift-assign-bulk-archive/<str:status>', views.RotatingShiftAssignBulkArchive.as_view(), name='rotating-shift-assigns-archive'),
path('rotating-shift-assign-bulk-delete', views.RotatingShiftAssignBulkDelete.as_view(), name='rotating-shift-assigns-bulk-delete'),
path('disciplinary-action-type/', views.ActiontypeView.as_view(), name='disciplinary-action-type'),
path('disciplinary-action-type/<int:pk>/', views.ActiontypeView.as_view(), name='disciplinary-action-type'),
path('rotating-worktype-create-permission-check/<int:id>', views.RotatingWorKTypePermissionCheck.as_view(), name='rotating-worktype-create-permission-check'),
path('rotating-shift-create-permission-check/<int:id>', views.RotatingShiftPermissionCheck.as_view(), name='rotating-shift-create-permission-check'),
path('shift-request-approve-permission-check', views.ShiftRequestApprovePermissionCheck.as_view(), name='rotating-worktype-create-permission-check'),
path('worktype-request-approve-permission-check', views.WorktypeRequestApprovePermissionCheck.as_view(), name='rotating-shift-create-permission-check'),
path('employee-tab-permission-check', views.EmployeeTabPermissionCheck.as_view(), name='rotating-shift-create-permission-check'),
]

View File

@@ -0,0 +1,44 @@
from django.urls import path
from ...api_views.employee import views as views
urlpatterns = [
path('employees/', views.EmployeeAPIView.as_view(), name='employees-list'),
path('employees/<int:pk>/', views.EmployeeAPIView.as_view(), name='employee-detail'),
path('employee-type/<int:pk>', views.EmployeeTypeAPIView.as_view(), name='employees'),
path('employee-type/', views.EmployeeTypeAPIView.as_view(), name='employees'),
path('list/employees/', views.EmployeeListAPIView.as_view(), name='employee-list-detailed'), # Alternative endpoint for listing employees
path('employee-bank-details/', views.EmployeeBankDetailsAPIView.as_view(), name='employee-bank-details-list'),
path('employee-bank-details/<int:pk>/', views.EmployeeBankDetailsAPIView.as_view(), name='employee-bank-details-detail'),
path('employee-work-information/', views.EmployeeWorkInformationAPIView.as_view(), name='employee-work-information-list'),
path('employee-work-information/<int:pk>/', views.EmployeeWorkInformationAPIView.as_view(), name='employee-work-information-detail'),
path('employee-work-info-export/', views.EmployeeWorkInfoExportView.as_view(), name='employee-work-info-export'),
path('employee-work-info-import/', views.EmployeeWorkInfoImportView.as_view(), name='employee-work-info-import'),
path('employee-bulk-update/', views.EmployeeBulkUpdateView.as_view(), name='employee-bulk-update'),
path('disciplinary-action/', views.DisciplinaryActionAPIView.as_view(), name='disciplinary-action-list'),
path('disciplinary-action/<int:pk>/', views.DisciplinaryActionAPIView.as_view(), name='disciplinary-action-detail'),
path('policies/', views.PolicyAPIView.as_view(), name='policy-list'),
path('policies/<int:pk>/', views.PolicyAPIView.as_view(), name='policy-detail'),
path('document-request/', views.DocumentRequestAPIView.as_view(), name='document-request-list'),
path('document-request/<int:pk>/', views.DocumentRequestAPIView.as_view(), name='document-request-detail'),
path('document-bulk-approve-reject/', views.DocumentBulkApproveRejectAPIView.as_view(), name='document-bulk-approve-reject'),
path('document-request-approve-reject/<int:id>/<str:status>/', views.DocumentRequestApproveRejectView.as_view(), name='document-request-approve-reject'),
path('documents/', views.DocumentAPIView.as_view(), name='document-list'),
path('documents/<int:pk>/', views.DocumentAPIView.as_view(), name='document-detail'),
path("employee-bulk- archive/<str:is_active>/", views.EmployeeBulkArchiveView.as_view(), name='employee-bulk-archive'),
path("employee-archive/<int:id>/<str:is_active>/", views.EmployeeArchiveView.as_view(), name='employee-archive'),
path("employee-selector/", views.EmployeeSelectorView.as_view(), name='employee-selector'),
path("manager-check/", views.ReportingManagerCheck.as_view(), name='manager-check'),
]

View File

@@ -0,0 +1,47 @@
"""
horilla_api/urls/leave/urls.py
"""
from django.urls import path
from horilla_api.api_views.leave.views import *
urlpatterns = [
path("available-leave/", EmployeeAvailableLeaveGetAPIView.as_view()),
path("user-request/", EmployeeLeaveRequestGetCreateAPIView.as_view()),
path("user-request/<int:pk>/", EmployeeLeaveRequestUpdateDeleteAPIView.as_view()),
path("leave-type/", LeaveTypeGetCreateAPIView.as_view()),
path("leave-type/<int:pk>/", LeaveTypeGetUpdateDeleteAPIView.as_view()),
path("allocation-request/", LeaveAllocationRequestGetCreateAPIView.as_view()),
path(
"allocation-request/<int:pk>/",
LeaveAllocationRequestGetUpdateDeleteAPIView.as_view(),
),
path("assign-leave/", AssignLeaveGetCreateAPIView.as_view()),
path("assign-leave/<int:pk>/", AssignLeaveGetUpdateDeleteAPIView.as_view()),
path("request/", LeaveRequestGetCreateAPIView.as_view()),
path("request/<int:pk>/", LeaveRequestGetUpdateDeleteAPIView.as_view()),
path("company-leave/", CompanyLeaveGetCreateAPIView.as_view()),
path("company-leave/<int:pk>/", CompanyLeaveGetUpdateDeleteAPIView.as_view()),
path("holiday/", HolidayGetCreateAPIView.as_view()),
path("holiday/<int:pk>/", HolidayGetUpdateDeleteAPIView.as_view()),
path("approve/<int:pk>/", LeaveRequestApproveAPIView.as_view()),
path("reject/<int:pk>/", LeaveRequestRejectAPIView.as_view()),
path("cancel/<int:pk>/", LeaveRequestCancelAPIView.as_view()),
path("allocation-approve/<int:pk>/", LeaveAllocationApproveAPIView.as_view()),
path("allocation-reject/<int:pk>/", LeaveAllocationRequestRejectAPIView.as_view()),
path("request-bulk-action/", LeaveRequestBulkApproveDeleteAPIview.as_view()),
path("user-allocation-request/", EmployeeLeaveAllocationGetCreateAPIView.as_view()),
path(
"user-allocation-request/<int:pk>/",
EmployeeLeaveAllocationUpdateDeleteAPIView.as_view(),
),
path("status/", LeaveRequestedApprovedCountAPIView.as_view()),
path(
"employee-leave-type/<int:pk>/", EmployeeAvailableLeaveTypeGetAPIView.as_view()
),
path("check-type/", LeaveTypeGetPermissionCheckAPIView.as_view()),
path("check-allocation/", LeaveAllocationGetPermissionCheckAPIView.as_view()),
path("check-request/", LeaveRequestGetPermissionCheckAPIView.as_view()),
path("check-assign/", LeaveAssignGetPermissionCheckAPIView.as_view()),
path("check-perm/", LeavePermissionCheckAPIView.as_view()),
]

View File

@@ -0,0 +1,11 @@
from django.urls import re_path, path
from ...api_views.notifications import views
urlpatterns = [
path("notifications/list/<str:type>", views.NotificationView.as_view()),
path("notifications/<int:id>/", views.NotificationReadDelView.as_view()),
path("notifications/bulk-delete-unread/", views.NotificationBulkDelUnreadMessageView.as_view()),
path("notifications/bulk-read/", views.NotificationBulkReadDelView.as_view()),
path("notifications/bulk-delete/", views.NotificationBulkReadDelView.as_view()),
]

View File

@@ -0,0 +1,279 @@
from rest_framework import serializers
from employee.models import BonusPoint, Employee
from leave.models import LeaveType
from payroll.models.models import Allowance, Contract, Deduction, LoanAccount, MultipleCondition, Payslip, Reimbursement, ReimbursementMultipleAttachment
from payroll.models.tax_models import TaxBracket
class PayslipSerializer(serializers.ModelSerializer):
employee_first_name = serializers.CharField(
source="employee_id.employee_first_name", read_only=True)
employee_last_name = serializers.CharField(
source="employee_id.employee_last_name", read_only=True)
shift_name = serializers.CharField(
source="shift_id.employee_shift", read_only=True)
badge_id = serializers.CharField(
source="employee_id.badge_id", read_only=True)
employee_profile_url = serializers.SerializerMethodField(read_only=True)
department_name = serializers.CharField(
source='employee_id.employee_work_info.department_id.department', read_only=True)
bank_account_check_number = serializers.CharField(
source='employee_id.employee_bank_details.account_number', read_only=True)
class Meta:
model = Payslip
fields = '__all__'
# exclude = ['reference',
# 'sent_to_employee',
# 'installment_ids', 'created_at']
def get_employee_profile_url(self, obj):
try:
employee_profile = obj.employee_id.employee_profile
return employee_profile.url
except:
return None
class ContractSerializer(serializers.ModelSerializer):
employee_first_name = serializers.CharField(
source="employee_id.employee_first_name", read_only=True)
employee_last_name = serializers.CharField(
source="employee_id.employee_last_name", read_only=True)
shift_name = serializers.CharField(
source="shift_id.employee_shift", read_only=True)
badge_id = serializers.CharField(
source="employee_id.badge_id", read_only=True)
employee_profile_url = serializers.SerializerMethodField(read_only=True)
job_position_name = serializers.CharField(
source='job_position_id.job_position', read_only=True)
job_role_name = serializers.CharField(
source='job_role_id.job_role', read_only=True)
department_name = serializers.CharField(
source='department_id.department', read_only=True)
shift_name = serializers.CharField(
source='shift_id.employee_shift', read_only=True)
work_type_name = serializers.CharField(
source='work_type_id.work_type', read_only=True)
def get_employee_profile_url(self, obj):
try:
employee_profile = obj.employee_id.employee_profile
return employee_profile.url
except:
return None
class Meta:
model = Contract
fields = '__all__'
class MultipleConditionSerializer(serializers.ModelSerializer):
class Meta:
model = MultipleCondition
fields = '__all__'
class AllowanceSerializer(serializers.ModelSerializer):
specific_employees = serializers.PrimaryKeyRelatedField(
queryset=Employee.objects.all(), many=True, required=False
)
exclude_employees = serializers.PrimaryKeyRelatedField(
queryset=Employee.objects.all(), many=True, required=False
)
other_conditions = serializers.PrimaryKeyRelatedField(
queryset=MultipleCondition.objects.all(), many=True, required=False
)
class Meta:
model = Allowance
fields = '__all__'
read_only_fields = ['id', 'company_id', 'only_show_under_employee', 'is_loan']
def create(self, validated_data):
specific_employees = validated_data.pop('specific_employees', [])
exclude_employees = validated_data.pop('exclude_employees', [])
other_conditions = validated_data.pop('other_conditions', [])
allowance = Allowance.objects.create(**validated_data)
# Set the ManyToMany relationships after the instance is created
allowance.specific_employees.set(specific_employees)
allowance.exclude_employees.set(exclude_employees)
allowance.other_conditions.set(other_conditions)
return allowance
def validate(self, data):
is_fixed = data.get('is_fixed')
amount = data.get('amount')
based_on = data.get('based_on')
per_attendance_fixed_amount = data.get('per_attendance_fixed_amount')
shift_id = data.get('shift_id')
work_type_id = data.get('work_type_id')
is_condition_based = data.get('is_condition_based')
field = data.get('field')
condition = data.get('condition')
value = data.get('value')
has_max_limit = data.get('has_max_limit')
maximum_amount = data.get('maximum_amount')
if is_fixed and (amount is None or amount < 0):
raise serializers.ValidationError("If 'is_fixed' is True, 'amount' must be a positive number.")
if not is_fixed and not based_on:
raise serializers.ValidationError("If 'is_fixed' is False, 'based_on' is required.")
if based_on == "attendance" and not per_attendance_fixed_amount:
raise serializers.ValidationError("If 'based_on' is 'attendance', 'per_attendance_fixed_amount' is required.")
if based_on == "shift_id" and not shift_id:
raise serializers.ValidationError("If 'based_on' is 'shift_id', 'shift_id' is required.")
if based_on == "work_type_id" and not work_type_id:
raise serializers.ValidationError("If 'based_on' is 'work_type_id', 'work_type_id' is required.")
if is_condition_based and (not field or not value or not condition):
raise serializers.ValidationError("If 'is_condition_based' is True, 'field', 'value', and 'condition' are required.")
if has_max_limit and maximum_amount is None:
raise serializers.ValidationError("If 'has_max_limit' is True, 'maximum_amount' is required.")
return data
class DeductionSerializer(serializers.ModelSerializer):
class Meta:
model = Deduction
fields = '__all__'
class LoanAccountSerializer(serializers.ModelSerializer):
employee_profile_url = serializers.SerializerMethodField(read_only=True)
employee_full_name = serializers.CharField(source='employee_id.get_full_name',read_only=True)
badge_id = serializers.CharField(source='employee_id.badge_id',read_only=True)
job_position_name = serializers.CharField(source='employee_id.get_job_position',read_only=True)
class Meta:
model = LoanAccount
fields = '__all__'
def get_employee_profile_url(self, obj):
try:
employee_profile = obj.employee_id.employee_profile
return employee_profile.url
except:
return None
class ReimbursementSerializer(serializers.ModelSerializer):
other_attachements = serializers.SerializerMethodField()
leave_type_name = serializers.CharField(source='leave_type_id.name',read_only=True)
employee_profile_url = serializers.SerializerMethodField(read_only=True)
badge_id = serializers.CharField(source='employee_id.badge_id')
employee_full_name = serializers.CharField(source='employee_id.get_full_name')
def get_employee_profile_url(self, obj):
try:
employee_profile = obj.employee_id.employee_profile
return employee_profile.url
except:
return None
class Meta:
model = Reimbursement
fields = '__all__'
def get_other_attachements(self, obj):
attachments = []
for attachment in obj.other_attachments.all():
try:
attachments.append(attachment.attachment.url)
except :
pass
return attachments
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
exclude_fields = []
# Get type from data or instance
instance_type = getattr(self.instance, 'type', None)
if instance_type == "reimbursement":
exclude_fields.extend(
["leave_type_id", "cfd_to_encash", "ad_to_encash", "bonus_to_encash"])
elif instance_type == "leave_encashment":
exclude_fields.extend(["attachment", "amount", "bonus_to_encash"])
elif instance_type == "bonus_encashment":
exclude_fields.extend(
["attachment", "amount", "leave_type_id", "cfd_to_encash", "ad_to_encash"])
# Remove excluded fields from serializer fields
for field in exclude_fields:
self.fields.pop(field, None)
def get_encashable_leaves(self, employee):
leaves = LeaveType.objects.filter(
employee_available_leave__employee_id=employee,
employee_available_leave__total_leave_days__gte=1,
is_encashable=True,
)
return leaves
def validate(self, data):
try:
employee_id = self.instance.employee_id
type = self.instance.type
leave_type_id = self.instance.leave_type_id
except:
employee_id = data["employee_id"]
type = data["type"]
leave_type_id = data["leave_type_id"] if data.get("leave_type_id",None) else None
available_points = BonusPoint.objects.filter(
employee_id=employee_id
).first()
if type == "bonus_encashment":
try:
bonus_to_encash = self.instance.bonus_to_encash
except:
bonus_to_encash = data["bonus_to_encash"]
if available_points.points < bonus_to_encash:
raise serializers.ValidationError(
{"bonus_to_encash": "Not enough bonus points to redeem"}
)
if bonus_to_encash <= 0:
raise serializers.ValidationError(
{
"bonus_to_encash": "Points must be greater than zero to redeem."
}
)
if type == "leave_encashment":
leave_type_id = leave_type_id
encashable_leaves = self.get_encashable_leaves(employee_id)
if (leave_type_id is None) or (leave_type_id not in encashable_leaves):
raise serializers.ValidationError(
{"leave_type_id": "This leave type is not encashable"}
)
return data
def save(self, **kwargs):
multiple_attachment_ids = []
request_files = self.context['request'].FILES
attachments = request_files.getlist("attachment")
if attachments:
for attachment in attachments:
file_instance = ReimbursementMultipleAttachment()
file_instance.attachment = attachment
file_instance.save()
multiple_attachment_ids.append(file_instance.pk)
instance = super().save()
instance.other_attachments.add(*multiple_attachment_ids)
return super().save(**kwargs)
class TaxBracketSerializer(serializers.ModelSerializer):
class Meta:
fields = '__all__'
model = TaxBracket

View File

@@ -0,0 +1,23 @@
from django.urls import path
from ...api_views.payroll.views import *
urlpatterns = [
path('contract/',ContractView.as_view(),),
path('contract/<int:id>',ContractView.as_view(),),
path('payslip/', PayslipView.as_view(), name=''),
path('payslip/<int:id>', PayslipView.as_view(), name=''),
path('payslip-download/<int:id>', PayslipDownloadView.as_view(), name=''),
path('payslip-send-mail/', PayslipSendMailView.as_view(), name=''),
path('loan-account/', LoanAccountView.as_view(), name=''),
path('loan-account/<int:pk>', LoanAccountView.as_view(), name=''),
path('reimbusement/', ReimbursementView.as_view(), name=''),
path('reimbusement/<int:pk>', ReimbursementView.as_view(), name=''),
path('reimbusement-approve-reject/<int:pk>', ReimbusementApproveRejectView.as_view(), name=''),
path('tax-bracket/<int:pk>', TaxBracketView.as_view(), name=''),
path('tax-bracket/', TaxBracketView.as_view(), name=''),
path('allowance', AllowanceView.as_view(), name=''),
path('allowance/<int:pk>', AllowanceView.as_view(), name=''),
path('deduction', DeductionView.as_view(), name=''),
path('deduction/<int:pk>', DeductionView.as_view(), name=''),
]

View File

@@ -0,0 +1,301 @@
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from asset.models import *
from ...api_serializers.asset.serializers import *
from rest_framework.pagination import PageNumberPagination
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend
from ...api_filters.asset.filters import AssetCategoryFilter
from asset.filters import AssetFilter
from django.http import QueryDict
from datetime import date
class AssetAPIView(APIView):
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filterset_class = AssetFilter
def get_asset(self, pk):
try:
return Asset.objects.get(pk=pk)
except Asset.DoesNotExist as e:
raise serializers.ValidationError(e)
def get(self, request, pk=None):
if pk:
asset = self.get_asset(pk)
serializer = AssetSerializer(asset)
return Response(serializer.data)
paginator = PageNumberPagination()
queryset = Asset.objects.all()
filterset = self.filterset_class(request.GET, queryset=queryset)
page = paginator.paginate_queryset(filterset.qs, request)
serializer = AssetGetAllSerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
def post(self, request):
serializer = AssetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def put(self, request, pk):
asset = self.get_asset(pk)
serializer = AssetSerializer(asset, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
asset = self.get_asset(pk)
asset.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class AssetCategoryAPIView(APIView):
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filterset_class = AssetCategoryFilter
def get_asset_category(self, pk):
try:
return AssetCategory.objects.get(pk=pk)
except AssetCategory.DoesNotExist as e:
raise serializers.ValidationError(e)
def get(self, request, pk=None):
if pk:
asset_category = self.get_asset_category(pk)
serializer = AssetCategorySerializer(asset_category)
return Response(serializer.data)
paginator = PageNumberPagination()
queryset = AssetCategory.objects.all()
filterset = self.filterset_class(request.GET, queryset=queryset)
page = paginator.paginate_queryset(filterset.qs, request)
serializer = AssetCategorySerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
def post(self, request):
serializer = AssetCategorySerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def put(self, request, pk):
asset_category = self.get_asset_category(pk)
serializer = AssetCategorySerializer(asset_category, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
asset_category = self.get_asset_category(pk)
asset_category.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class AssetLotAPIView(APIView):
permission_classes = [IsAuthenticated]
def get_asset_lot(self, pk):
try:
return AssetLot.objects.get(pk=pk)
except AssetLot.DoesNotExist as e:
raise serializers.ValidationError(e)
def get(self, request, pk=None):
if pk:
asset_lot = self.get_asset_lot(pk)
serializer = AssetLotSerializer(asset_lot)
return Response(serializer.data)
paginator = PageNumberPagination()
assets = AssetLot.objects.all()
page = paginator.paginate_queryset(assets, request)
serializer = AssetLotSerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
def post(self, request):
serializer = AssetLotSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def put(self, request, pk):
asset_lot = self.get_asset_lot(pk)
serializer = AssetLotSerializer(asset_lot, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
asset_lot = self.get_asset_lot(pk)
asset_lot.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class AssetAllocationAPIView(APIView):
permission_classes = [IsAuthenticated]
def get_asset_assignment(self, pk):
try:
return AssetAssignment.objects.get(pk=pk)
except AssetAssignment.DoesNotExist as e:
raise serializers.ValidationError(e)
def get(self, request, pk=None):
if pk:
asset_assignment = self.get_asset_assignment(pk)
serializer = AssetAssignmentGetSerializer(asset_assignment)
return Response(serializer.data)
paginator = PageNumberPagination()
assets = AssetAssignment.objects.all()
page = paginator.paginate_queryset(assets, request)
serializer = AssetAssignmentGetSerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
def post(self, request):
serializer = AssetAssignmentSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def put(self, request, pk):
asset_assignment = self.get_asset_assignment(pk)
serializer = AssetAssignmentSerializer(
asset_assignment, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
asset_assignment = self.get_asset_assignment(pk)
asset_assignment.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class AssetRequestAPIView(APIView):
permission_classes = [IsAuthenticated]
def get_asset_request(self, pk):
try:
return AssetRequest.objects.get(pk=pk)
except AssetRequest.DoesNotExist as e:
raise serializers.ValidationError(e)
def get(self, request, pk=None):
if pk:
asset_request = self.get_asset_request(pk)
serializer = AssetRequestGetSerializer(asset_request)
return Response(serializer.data)
paginator = PageNumberPagination()
assets = AssetRequest.objects.all().order_by('-id')
page = paginator.paginate_queryset(assets, request)
serializer = AssetRequestGetSerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
def post(self, request):
serializer = AssetRequestSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def put(self, request, pk):
asset_request = self.get_asset_request(pk)
serializer = AssetRequestSerializer(asset_request, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
asset_request = self.get_asset_request(pk)
asset_request.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class AssetRejectAPIView(APIView):
permission_classes = [IsAuthenticated]
def get_asset_request(self, pk):
try:
return AssetRequest.objects.get(pk=pk)
except AssetRequest.DoesNotExist as e:
raise serializers.ValidationError(e)
def put(self, request, pk):
asset_request = self.get_asset_request(pk)
if asset_request.asset_request_status == "Requested":
asset_request.asset_request_status = 'Rejected'
asset_request.save()
return Response(status=204)
raise serializers.ValidationError({"error":"Access Denied.."})
class AssetApproveAPIView(APIView):
permission_classes = [IsAuthenticated]
def get_asset_request(self, pk):
try:
return AssetRequest.objects.get(pk=pk)
except AssetRequest.DoesNotExist as e:
raise serializers.ValidationError(e)
def put(self, request, pk):
asset_request = self.get_asset_request(pk)
if asset_request.asset_request_status == "Requested":
data = request.data
if isinstance(data, QueryDict):
data = data.dict()
data['assigned_to_employee_id'] = asset_request.requested_employee_id.id
data['assigned_by_employee_id'] = request.user.employee_get.id
serializer = AssetApproveSerializer(data=data, context={'asset_request': asset_request})
if serializer.is_valid():
serializer.save()
asset_id = Asset.objects.get(id=data['asset_id'])
asset_id.asset_status = "In use"
asset_id.save()
asset_request.asset_request_status = 'Approved'
asset_request.save()
return Response(status=200)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
raise serializers.ValidationError({"error":"Access Denied.."})
class AssetReturnAPIView(APIView):
permission_classes = [IsAuthenticated]
def get_asset_assignment(self, pk):
try:
return AssetAssignment.objects.get(pk=pk)
except AssetAssignment.DoesNotExist as e:
raise serializers.ValidationError(e)
def put(self, request, pk):
asset_assignment = self.get_asset_assignment(pk)
if request.user.has_perm('app_name.change_mymodel'):
serializer = AssetReturnSerializer(instance=asset_assignment, data=request.data)
if serializer.is_valid():
images = [ReturnImages.objects.create(image=image) for image in request.data.getlist('image')]
asset_return = serializer.save()
asset_return.return_images.set(images)
if asset_return.return_status == 'Healthy':
Asset.objects.filter(id=pk).update(asset_status='Available')
else:
Asset.objects.filter(id=pk).update(asset_status='Not-Available')
AssetAssignment.objects.filter(id=asset_return.id).update(return_date=date.today())
return Response(status=200)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else:
AssetAssignment.objects.filter(id=pk).update(return_request=True)
return Response(status=200)

View File

@@ -0,0 +1,23 @@
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from ...api_decorators.base.decorators import manager_permission_required
class AttendancePermissionCheck(APIView):
permission_classes = [IsAuthenticated]
@manager_permission_required("attendance.view_attendance")
def get(self,request):
return Response(status=200)
class AttendancePermissionCheck(APIView):
permission_classes = [IsAuthenticated]
@manager_permission_required("attendance.view_attendance")
def get(self,request):
return Response(status=200)

View File

@@ -0,0 +1,917 @@
from django.db.models import Case, Value, When, F, CharField
from django.http import QueryDict
from attendance.models import AttendanceActivity
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from datetime import date, datetime, timedelta, timezone
from attendance.models import EmployeeShiftDay
from attendance.views.dashboard import (
find_expected_attendances,
find_late_come,
find_on_time,
)
from attendance.views.views import *
from attendance.views.clock_in_out import *
from django.shortcuts import get_object_or_404
from rest_framework.response import Response
from attendance.models import Attendance
from base.methods import is_reportingmanager
from ...api_decorators.base.decorators import manager_permission_required
from ...api_methods.base.methods import groupby_queryset, permission_based_queryset
from employee.filters import EmployeeFilter
from horilla_api.api_serializers.attendance.serializers import (
AttendanceActivitySerializer,
AttendanceLateComeEarlyOutSerializer,
AttendanceOverTimeSerializer,
AttendanceRequestSerializer,
AttendanceSerializer,
MailTemplateSerializer,
)
from rest_framework.pagination import PageNumberPagination
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import permission_required
from django.conf import settings
from django.core.mail import EmailMessage
from recruitment.models import RecruitmentMailTemplate
from base.backends import ConfiguredEmailBackend
from django import template
from base.methods import generate_pdf
# Create your views here.
def query_dict(data):
query_dict = QueryDict("", mutable=True)
for key, value in data.items():
if isinstance(value, list):
for item in value:
query_dict.appendlist(key, item)
else:
query_dict.update({key: value})
return query_dict
class ClockInAPIView(APIView):
"""
Allows authenticated employees to clock in, determining the correct shift and attendance date, including handling night shifts.
Methods:
post(request): Processes and records the clock-in time.
"""
permission_classes = [IsAuthenticated]
def post(self, request):
employee, work_info = employee_exists(request)
datetime_now = datetime.now()
if request.__dict__.get("datetime"):
datetime_now = request.datetime
if employee and work_info is not None:
shift = work_info.shift_id
date_today = date.today()
if request.__dict__.get("date"):
date_today = request.date
attendance_date = date_today
day = date_today.strftime("%A").lower()
day = EmployeeShiftDay.objects.get(day=day)
now = datetime.now().strftime("%H:%M")
if request.__dict__.get("time"):
now = request.time.strftime("%H:%M")
now_sec = strtime_seconds(now)
mid_day_sec = strtime_seconds("12:00")
minimum_hour, start_time_sec, end_time_sec = shift_schedule_today(
day=day, shift=shift
)
if start_time_sec > end_time_sec:
# night shift
# ------------------
# Night shift in Horilla consider a 24 hours from noon to next day noon,
# the shift day taken today if the attendance clocked in after 12 O clock.
if mid_day_sec > now_sec:
# Here you need to create attendance for yesterday
date_yesterday = date_today - timedelta(days=1)
day_yesterday = date_yesterday.strftime("%A").lower()
day_yesterday = EmployeeShiftDay.objects.get(day=day_yesterday)
minimum_hour, start_time_sec, end_time_sec = shift_schedule_today(
day=day_yesterday, shift=shift
)
attendance_date = date_yesterday
day = day_yesterday
clock_in_attendance_and_activity(
employee=employee,
date_today=date_today,
attendance_date=attendance_date,
day=day,
now=now,
shift=shift,
minimum_hour=minimum_hour,
start_time=start_time_sec,
end_time=end_time_sec,
in_datetime=datetime_now,
)
return Response({"message": "Clocked-In"}, status=200)
return Response(
{
"error": "You Don't have work information filled or your employee detail neither entered "
}
)
class ClockOutAPIView(APIView):
"""
Allows authenticated employees to clock out, updating the latest attendance record and handling early outs.
Methods:
post(request): Records the clock-out time.
"""
permission_classes = [IsAuthenticated]
def post(self, request):
datetime_now = datetime.now()
if request.__dict__.get("datetime"):
datetime_now = request.datetime
employee, work_info = employee_exists(request)
shift = work_info.shift_id
date_today = date.today()
if request.__dict__.get("date"):
date_today = request.date
day = date_today.strftime("%A").lower()
day = EmployeeShiftDay.objects.get(day=day)
attendance = (
Attendance.objects.filter(employee_id=employee)
.order_by("id", "attendance_date")
.last()
)
if attendance is not None:
day = attendance.attendance_day
now = datetime.now().strftime("%H:%M")
if request.__dict__.get("time"):
now = request.time.strftime("%H:%M")
minimum_hour, start_time_sec, end_time_sec = shift_schedule_today(
day=day, shift=shift
)
early_out_instance = attendance.late_come_early_out.filter(type="early_out")
if not early_out_instance.exists():
early_out(
attendance=attendance, start_time=start_time_sec, end_time=end_time_sec
)
clock_out_attendance_and_activity(
employee=employee, date_today=date_today, now=now, out_datetime=datetime_now
)
return Response({"message": "Clocked-Out"}, status=200)
class AttendanceView(APIView):
"""
Handles CRUD operations for attendance records.
Methods:
get_queryset(request, type): Returns filtered attendance records.
get(request, pk=None, type=None): Retrieves a specific record or a list of records.
post(request): Creates a new attendance record.
put(request, pk): Updates an existing attendance record.
delete(request, pk): Deletes an attendance record and adjusts related overtime if needed.
"""
permission_classes = [IsAuthenticated]
filterset_class = AttendanceFilters
def get_queryset(self, request, type):
if type == "ot":
condition = AttendanceValidationCondition.objects.first()
minot = strtime_seconds("00:30")
if condition is not None:
minot = strtime_seconds(condition.minimum_overtime_to_approve)
queryset = Attendance.objects.filter(
overtime_second__gte=minot,
attendance_validated=True,
)
elif type == "validated":
queryset = Attendance.objects.filter(attendance_validated=True)
elif type == "non-validated":
queryset = Attendance.objects.filter(attendance_validated=False)
else:
queryset = Attendance.objects.all()
user = request.user
# checking user level permissions
perm = "attendance.view_attendance"
queryset = permission_based_queryset(user, perm, queryset, user_obj=True)
return queryset
def get(self, request, pk=None, type=None):
# individual object workflow
if pk:
attendance = get_object_or_404(Attendance, pk=pk)
serializer = AttendanceSerializer(instance=attendance)
return Response(serializer.data, status=200)
# permission based querysete
attendances = self.get_queryset(request, type)
# filtering queryset
attendances_filter_queryset = self.filterset_class(
request.GET, queryset=attendances
).qs
field_name = request.GET.get("groupby_field", None)
if field_name:
url = request.build_absolute_uri()
return groupby_queryset(
request, url, field_name, attendances_filter_queryset
)
# pagination workflow
paginater = PageNumberPagination()
page = paginater.paginate_queryset(attendances_filter_queryset, request)
serializer = AttendanceSerializer(page, many=True)
return paginater.get_paginated_response(serializer.data)
@manager_permission_required("attendance.add_attendance")
def post(self, request):
serializer = AttendanceSerializer(data=request.data)
if serializer.is_valid():
validated_data = serializer.validated_data
instance = Attendance(**validated_data)
instance.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@method_decorator(
permission_required("attendance.change_attendance", raise_exception=True)
)
def put(self, request, pk):
try:
attendance = Attendance.objects.get(id=pk)
except Attendance.DoesNotExist:
return Response({"detail": "Attendance record not found."}, status=404)
serializer = AttendanceSerializer(instance=attendance, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
# Customize error message for unique constraint
serializer_errors = serializer.errors
if "non_field_errors" in serializer.errors:
unique_error_msg = (
"The fields employee_id, attendance_date must make a unique set."
)
if unique_error_msg in serializer.errors["non_field_errors"]:
serializer_errors = {
"non_field_errors": [
"The employee already has attendance on this date."
]
}
return Response(serializer_errors, status=400)
@method_decorator(
permission_required("attendance.delete_attendance", raise_exception=True)
)
def delete(self, request, pk):
attendance = Attendance.objects.get(id=pk)
month = attendance.attendance_date
month = month.strftime("%B").lower()
overtime = attendance.employee_id.employee_overtime.filter(month=month).last()
if overtime is not None:
if attendance.attendance_overtime_approve:
# Subtract overtime of this attendance
total_overtime = strtime_seconds(overtime.overtime)
attendance_overtime_seconds = strtime_seconds(
attendance.attendance_overtime
)
if total_overtime > attendance_overtime_seconds:
total_overtime = total_overtime - attendance_overtime_seconds
else:
total_overtime = attendance_overtime_seconds - total_overtime
overtime.overtime = format_time(total_overtime)
overtime.save()
try:
attendance.delete()
return Response({"status", "deleted"}, status=200)
except Exception as error:
return Response({"error:", f"{error}"}, status=400)
else:
try:
attendance.delete()
return Response({"status", "deleted"}, status=200)
except Exception as error:
return Response({"error:", f"{error}"}, status=400)
class ValidateAttendanceView(APIView):
"""
Validates an attendance record and sends a notification to the employee.
Method:
put(request, pk): Marks the attendance as validated and notifies the employee.
"""
def put(self, request, pk):
attendance = Attendance.objects.filter(id=pk).update(attendance_validated=True)
attendance = Attendance.objects.filter(id=pk).first()
try:
notify.send(
request.user.employee_get,
recipient=attendance.employee_id.employee_user_id,
verb=f"Your attendance for the date {attendance.attendance_date} is validated",
verb_ar=f"تم تحقيق حضورك في تاريخ {attendance.attendance_date}",
verb_de=f"Deine Anwesenheit für das Datum {attendance.attendance_date} ist bestätigt.",
verb_es=f"Se valida tu asistencia para la fecha {attendance.attendance_date}.",
verb_fr=f"Votre présence pour la date {attendance.attendance_date} est validée.",
redirect="/attendance/view-my-attendance",
icon="checkmark",
api_redirect=f"/api/attendance/attendance?employee_id{attendance.employee_id}",
)
except:
pass
return Response(status=200)
class OvertimeApproveView(APIView):
"""
Approves overtime for an attendance record and sends a notification to the employee.
Method:
put(request, pk): Marks the overtime as approved and notifies the employee.
"""
def put(self, request, pk):
try:
attendance = Attendance.objects.filter(id=pk).update(
attendance_overtime_approve=True
)
except Exception as E:
return Response({"error": str(E)}, status=400)
attendance = Attendance.objects.filter(id=pk).first()
try:
notify.send(
request.user.employee_get,
recipient=attendance.employee_id.employee_user_id,
verb=f"Your {attendance.attendance_date}'s attendance overtime approved.",
verb_ar=f"تمت الموافقة على إضافة ساعات العمل الإضافية لتاريخ {attendance.attendance_date}.",
verb_de=f"Die Überstunden für den {attendance.attendance_date} wurden genehmigt.",
verb_es=f"Se ha aprobado el tiempo extra de asistencia para el {attendance.attendance_date}.",
verb_fr=f"Les heures supplémentaires pour la date {attendance.attendance_date} ont été approuvées.",
redirect="/attendance/attendance-overtime-view",
icon="checkmark",
api_redirect="/api/attendance/attendance-hour-account/",
)
except:
pass
return Response(status=200)
class AttendanceRequestView(APIView):
"""
Handles requests for creating, updating, and viewing attendance records.
Methods:
get(request, pk=None): Retrieves a specific attendance request by `pk` or a filtered list of requests.
post(request): Creates a new attendance request.
put(request, pk): Updates an existing attendance request.
"""
serializer_class = AttendanceRequestSerializer
permission_classes = [IsAuthenticated]
def get(self, request, pk=None):
if pk:
attendance = Attendance.objects.get(id=pk)
serializer = AttendanceRequestSerializer(instance=attendance)
return Response(serializer.data, status=200)
requests = Attendance.objects.filter(
is_validate_request=True,
)
requests = filtersubordinates(
request=request,
perm="attendance.view_attendance",
queryset=requests,
)
requests = requests | Attendance.objects.filter(
employee_id__employee_user_id=request.user,
is_validate_request=True,
)
request_filtered_queryset = AttendanceFilters(request.GET, requests).qs
field_name = request.GET.get("groupby_field", None)
if field_name:
# groupby workflow
url = request.build_absolute_uri()
return groupby_queryset(request, url, field_name, request_filtered_queryset)
pagenation = PageNumberPagination()
page = pagenation.paginate_queryset(request_filtered_queryset, request)
serializer = self.serializer_class(page, many=True)
return pagenation.get_paginated_response(serializer.data)
def post(self, request):
serializer = AttendanceRequestSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=404)
@manager_permission_required("attendance.update_attendance")
def put(self, request, pk):
attendance = Attendance.objects.get(id=pk)
serializer = AttendanceRequestSerializer(instance=attendance, data=request.data)
if serializer.is_valid():
instance = serializer.save()
instance.employee_id = attendance.employee_id
instance.id = attendance.id
if attendance.request_type != "create_request":
attendance.requested_data = json.dumps(instance.serialize())
attendance.request_description = instance.request_description
# set the user level validation here
attendance.is_validate_request = True
attendance.save()
else:
instance.is_validate_request_approved = False
instance.is_validate_request = True
instance.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=404)
class AttendanceRequestApproveView(APIView):
"""
Approves and updates an attendance request.
Method:
put(request, pk): Approves the attendance request, updates attendance records, and handles related activities.
"""
@manager_permission_required("attendance.change_attendance")
def put(self, request, pk):
try:
attendance = Attendance.objects.get(id=pk)
prev_attendance_date = attendance.attendance_date
prev_attendance_clock_in_date = attendance.attendance_clock_in_date
prev_attendance_clock_in = attendance.attendance_clock_in
attendance.attendance_validated = True
attendance.is_validate_request_approved = True
attendance.is_validate_request = False
attendance.request_description = None
attendance.save()
if attendance.requested_data is not None:
requested_data = json.loads(attendance.requested_data)
requested_data["attendance_clock_out"] = (
None
if requested_data["attendance_clock_out"] == "None"
else requested_data["attendance_clock_out"]
)
requested_data["attendance_clock_out_date"] = (
None
if requested_data["attendance_clock_out_date"] == "None"
else requested_data["attendance_clock_out_date"]
)
Attendance.objects.filter(id=pk).update(**requested_data)
# DUE TO AFFECT THE OVERTIME CALCULATION ON SAVE METHOD, SAVE THE INSTANCE ONCE MORE
attendance = Attendance.objects.get(id=pk)
attendance.save()
if (
attendance.attendance_clock_out is None
or attendance.attendance_clock_out_date is None
):
attendance.attendance_validated = True
activity = AttendanceActivity.objects.filter(
employee_id=attendance.employee_id,
attendance_date=prev_attendance_date,
clock_in_date=prev_attendance_clock_in_date,
clock_in=prev_attendance_clock_in,
)
if activity:
activity.update(
employee_id=attendance.employee_id,
attendance_date=attendance.attendance_date,
clock_in_date=attendance.attendance_clock_in_date,
clock_in=attendance.attendance_clock_in,
)
else:
AttendanceActivity.objects.create(
employee_id=attendance.employee_id,
attendance_date=attendance.attendance_date,
clock_in_date=attendance.attendance_clock_in_date,
clock_in=attendance.attendance_clock_in,
)
except Exception as E:
return Response({"error": str(E)}, status=400)
return Response({"status": "approved"}, status=200)
class AttendanceRequestCancelView(APIView):
"""
Cancels an attendance request.
Method:
put(request, pk): Cancels the attendance request, resetting its status and data, and deletes the request if it was a create request.
"""
def put(self, request, pk):
try:
attendance = Attendance.objects.get(id=pk)
if (
attendance.employee_id.employee_user_id == request.user
or is_reportingmanager(request)
or request.user.has_perm("attendance.change_attendance")
):
attendance.is_validate_request_approved = False
attendance.is_validate_request = False
attendance.request_description = None
attendance.requested_data = None
attendance.request_type = None
attendance.save()
if attendance.request_type == "create_request":
attendance.delete()
except Exception as E:
return Response({"error": str(E)}, status=400)
return Response({"status": "success"}, status=200)
class AttendanceOverTimeView(APIView):
"""
Manages CRUD operations for attendance overtime records.
Methods:
get(request, pk=None): Retrieves a specific overtime record by `pk` or a list of records with filtering and pagination.
post(request): Creates a new overtime record.
put(request, pk): Updates an existing overtime record.
delete(request, pk): Deletes an overtime record.
"""
permission_classes = [IsAuthenticated]
def get(self, request, pk=None):
if pk:
attendance_ot = get_object_or_404(AttendanceOverTime, pk=pk)
serializer = AttendanceOverTimeSerializer(attendance_ot)
return Response(serializer.data, status=200)
filterset_class = AttendanceOverTimeFilter(request.GET)
queryset = filterset_class.qs
self_account = queryset.filter(employee_id__employee_user_id=request.user)
permission_based_queryset = filtersubordinates(
request, queryset, "attendance.view_attendanceovertime"
)
queryset = permission_based_queryset | self_account
field_name = request.GET.get("groupby_field", None)
if field_name:
# groupby workflow
url = request.build_absolute_uri()
return groupby_queryset(request, url, field_name, queryset)
pagenation = PageNumberPagination()
page = pagenation.paginate_queryset(queryset, request)
serializer = AttendanceOverTimeSerializer(page, many=True)
return pagenation.get_paginated_response(serializer.data)
@manager_permission_required("attendance.add_attendanceovertime")
def post(self, request):
serializer = AttendanceOverTimeSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@manager_permission_required("attendance.change_attendanceovertime")
def put(self, request, pk):
attendance_ot = get_object_or_404(AttendanceOverTime, pk=pk)
serializer = AttendanceOverTimeSerializer(
instance=attendance_ot, data=request.data
)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@method_decorator(
permission_required(
"attendance.delete_attendanceovertime", raise_exception=True
)
)
def delete(self, request, pk):
attendance = get_object_or_404(AttendanceOverTime, pk=pk)
attendance.delete()
return Response({"message": "Overtime deleted successfully"}, status=204)
class LateComeEarlyOutView(APIView):
"""
Handles retrieval and deletion of late come and early out records.
Methods:
get(request, pk=None): Retrieves a list of late come and early out records with filtering.
delete(request, pk=None): Deletes a specific late come or early out record by `pk`.
"""
permission_classes = [IsAuthenticated]
def get(self, request, pk=None):
data = LateComeEarlyOutFilter(request.GET)
serializer = AttendanceLateComeEarlyOutSerializer(data.qs, many=True)
return Response(serializer.data, status=200)
def delete(self, request, pk=None):
attendance = get_object_or_404(AttendanceLateComeEarlyOut, pk=pk)
attendance.delete()
return Response({"message": "Attendance deleted successfully"}, status=204)
class AttendanceActivityView(APIView):
"""
Retrieves attendance activity records.
Method:
get(request, pk=None): Retrieves a list of all attendance activity records.
"""
def get(self, request, pk=None):
data = AttendanceActivity.objects.all()
serializer = AttendanceActivitySerializer(data, many=True)
return Response(serializer.data, status=200)
class TodayAttendance(APIView):
"""
Provides the ratio of marked attendances to expected attendances for the current day.
Method:
get(request): Calculates and returns the attendance ratio for today.
"""
def get(self, request):
today = datetime.today()
week_day = today.strftime("%A").lower()
on_time = find_on_time(request, today=today, week_day=week_day)
late_come = find_late_come(start_date=today)
late_come_obj = len(late_come)
marked_attendances = late_come_obj + on_time
expected_attendances = find_expected_attendances(week_day=week_day)
marked_attendances_ratio = 0
if expected_attendances != 0:
marked_attendances_ratio = (
f"{(marked_attendances / expected_attendances) * 100:.2f}"
)
return Response(
{"marked_attendances_ratio": marked_attendances_ratio}, status=200
)
class OfflineEmployeesCountView(APIView):
"""
Retrieves the count of active employees who have not clocked in today.
Method:
get(request): Returns the number of active employees who are not yet clocked in.
"""
def get(self, request):
count = (
EmployeeFilter({"not_in_yet": date.today()})
.qs.exclude(employee_work_info__isnull=True)
.filter(is_active=True)
.count()
)
return Response({"count": count}, status=200)
class OfflineEmployeesListView(APIView):
"""
Lists active employees who have not clocked in today, including their leave status.
Method:
get(request): Retrieves and paginates a list of employees not clocked in today with their leave status.
"""
def get(self, request):
queryset = (
EmployeeFilter({"not_in_yet": date.today()})
.qs.exclude(employee_work_info__isnull=True)
.filter(is_active=True)
)
leave_status = self.get_leave_status(queryset)
pagenation = PageNumberPagination()
page = pagenation.paginate_queryset(leave_status, request)
return pagenation.get_paginated_response(page)
def get_leave_status(self, queryset):
today = date.today()
queryset = queryset.distinct()
# Annotate each employee with their leave status
employees_with_leave_status = queryset.annotate(
leave_status=Case(
# Define different cases based on leave requests and attendance
When(
leaverequest__start_date__lte=today,
leaverequest__end_date__gte=today,
leaverequest__status="approved",
then=Value("On Leave"),
),
When(
leaverequest__start_date__lte=today,
leaverequest__end_date__gte=today,
leaverequest__status="requested",
then=Value("Waiting Approval"),
),
When(
leaverequest__start_date__lte=today,
leaverequest__end_date__gte=today,
then=Value("Canceled / Rejected"),
),
When(
employee_attendances__attendance_date=today, then=Value("Working")
),
default=Value("Expected working"), # Default status
output_field=CharField(),
),
job_position_id=F("employee_work_info__job_position_id"),
).values(
"employee_first_name",
"employee_last_name",
"leave_status",
"employee_profile",
"id",
"job_position_id",
)
for employee in employees_with_leave_status:
if employee["employee_profile"]:
employee["employee_profile"] = (
settings.MEDIA_URL + employee["employee_profile"]
)
return employees_with_leave_status
class CheckingStatus(APIView):
"""
Checks and provides the current attendance status for the authenticated user.
Method:
get(request): Returns the attendance status, duration at work, and clock-in time if available.
"""
permission_classes = [IsAuthenticated]
@classmethod
def _format_seconds(cls, seconds):
hours = seconds // 3600
minutes = (seconds % 3600) // 60
seconds = seconds % 60
return f"{hours:02}:{minutes:02}:{seconds:02}"
def get(self, request):
attendance_activity = (
AttendanceActivity.objects.filter(employee_id=request.user.employee_get)
.order_by("-id")
.first()
)
duration = None
work_seconds = request.user.employee_get.get_forecasted_at_work()[
"forecasted_at_work_seconds"
]
duration = CheckingStatus._format_seconds(int(work_seconds))
status = False
clock_in_time = None
today = datetime.now()
attendance_activity_first = (
AttendanceActivity.objects.filter(
employee_id=request.user.employee_get, clock_in_date=today
)
.order_by("in_datetime")
.first()
)
if attendance_activity:
clock_in_time = attendance_activity_first.clock_in.strftime("%I:%M %p")
if attendance_activity.clock_out_date:
status = False
else:
status = True
return Response(
{"status": status, "duration": duration, "clock_in": clock_in_time},
status=200,
)
return Response(
{"status": status, "duration": duration, "clock_in_time": clock_in_time},
status=200,
)
class MailTemplateView(APIView):
"""
Retrieves a list of recruitment mail templates.
Method:
get(request): Returns all recruitment mail templates.
"""
permission_classes = [IsAuthenticated]
def get(self, request):
instances = RecruitmentMailTemplate.objects.all()
serializer = MailTemplateSerializer(instances, many=True)
return Response(serializer.data, status=200)
class ConvertedMailTemplateConvert(APIView):
"""
Renders a recruitment mail template with data from a specified employee.
Method:
put(request): Renders the mail template body with employee and user data and returns the result.
"""
permission_classes = [IsAuthenticated]
def put(self, request):
template_id = request.data.get("template_id", None)
employee_id = request.data.get("employee_id", None)
employee = Employee.objects.filter(id=employee_id).first()
bdy = RecruitmentMailTemplate.objects.filter(id=template_id).first()
template_bdy = template.Template(bdy.body)
context = template.Context(
{"instance": employee, "self": request.user.employee_get}
)
render_bdy = template_bdy.render(context)
return Response(render_bdy)
class OfflineEmployeeMailsend(APIView):
"""
Sends an email with attachments and rendered templates to a specified employee.
Method:
post(request): Renders email templates with employee and user data, attaches files, and sends the email.
"""
permission_classes = [IsAuthenticated]
def post(self, request):
employee_id = request.POST.get("employee_id")
subject = request.POST.get("subject", "")
bdy = request.POST.get("body", "")
other_attachments = request.FILES.getlist("other_attachments")
attachments = [
(file.name, file.read(), file.content_type) for file in other_attachments
]
email_backend = ConfiguredEmailBackend()
host = email_backend.dynamic_username
employee = Employee.objects.get(id=employee_id)
template_attachment_ids = request.POST.getlist("template_attachments")
bodys = list(
RecruitmentMailTemplate.objects.filter(
id__in=template_attachment_ids
).values_list("body", flat=True)
)
for html in bodys:
# due to not having solid template we first need to pass the context
template_bdy = template.Template(html)
context = template.Context(
{"instance": employee, "self": request.user.employee_get}
)
render_bdy = template_bdy.render(context)
attachments.append(
(
"Document",
generate_pdf(render_bdy, {}, path=False, title="Document").content,
"application/pdf",
)
)
template_bdy = template.Template(bdy)
context = template.Context(
{"instance": employee, "self": request.user.employee_get}
)
render_bdy = template_bdy.render(context)
email = EmailMessage(
subject,
render_bdy,
host,
[employee.employee_work_info.email],
)
email.content_subtype = "html"
email.attachments = attachments
try:
email.send()
if employee.employee_work_info.email:
return Response(f"Mail sent to {employee.get_full_name()}")
else:
return Response(f"Email not set for {employee.get_full_name()}")
except Exception as e:
return Response("Something went wrong")

View File

@@ -0,0 +1,27 @@
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework.views import APIView
from rest_framework.response import Response
from django.contrib.auth import authenticate
from ...api_serializers.auth.serializers import GetEmployeeSerializer
class LoginAPIView(APIView):
def post(self, request):
if 'username' and 'password' in request.data.keys():
username = request.data.get('username')
password = request.data.get('password')
user = authenticate(username=username, password=password)
if user:
refresh = RefreshToken.for_user(user)
employee = user.employee_get
result = {
'employee' : GetEmployeeSerializer(employee).data,
'access': str(refresh.access_token),
}
return Response(result, status=200)
else:
return Response({'error': 'Invalid credentials'}, status=401)
else:
return Response({'error':'Please provide Username and Password'})

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,749 @@
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.pagination import PageNumberPagination
from django.http import Http404
from ...api_decorators.base.decorators import manager_or_owner_permission_required, manager_permission_required
from employee.filters import DisciplinaryActionFilter, DocumentRequestFilter, EmployeeFilter
from employee.models import DisciplinaryAction, Employee, EmployeeBankDetails, EmployeeWorkInformation, Policy,EmployeeType
from employee.views import work_info_export, work_info_import
from ...api_methods.base.methods import groupby_queryset, permission_based_queryset
from ...api_decorators.employee.decorators import or_condition
from horilla.decorators import owner_can_enter
from horilla_documents.models import Document, DocumentRequest
from ... api_serializers.employee.serializers import (DisciplinaryActionSerializer, DocumentRequestSerializer, DocumentSerializer,
EmployeeBankDetailsSerializer, EmployeeListSerializer, EmployeeSelectorSerializer, EmployeeSerializer, EmployeeTypeSerializer, EmployeeWorkInformationSerializer, PolicySerializer)
from django_filters.rest_framework import DjangoFilterBackend
from django.contrib.auth.decorators import permission_required
from django.utils.decorators import method_decorator
from rest_framework.permissions import IsAuthenticated
from django.db.models import ProtectedError
from notifications.signals import notify
from django.db.models import Q
class EmployeeTypeAPIView(APIView):
"""
Retrieves employee types.
Methods:
get(request, pk=None): Returns a single employee type if pk is provided, otherwise returns all employee types.
"""
def get(self,request,pk=None):
if pk:
employee_type = EmployeeType.objects.get(id=pk)
serializer = EmployeeTypeSerializer(employee_type)
return Response(serializer.data,status=200)
employee_type = EmployeeType.objects.all()
serializer = EmployeeTypeSerializer(employee_type,many=True)
return Response(serializer.data,status=200)
class EmployeeAPIView(APIView):
"""
Handles CRUD operations for employees.
Methods:
get(request, pk=None):
- Retrieves a single employee by pk if provided.
- Retrieves and filters all employees if pk is not provided.
post(request):
- Creates a new employee if the user has the 'employee.change_employee' permission.
put(request, pk):
- Updates an existing employee if the user is the employee, a manager, or has 'employee.change_employee' permission.
delete(request, pk):
- Deletes an employee if the user has the 'employee.delete_employee' permission.
"""
filter_backends = [DjangoFilterBackend]
filterset_class = EmployeeFilter
permission_classes = [IsAuthenticated]
def get(self, request, pk=None):
if pk:
try:
employee = Employee.objects.get(pk=pk)
except Employee.DoesNotExist:
return Response({"error": "Employee does not exist"}, status=status.HTTP_404_NOT_FOUND)
serializer = EmployeeSerializer(employee)
return Response(serializer.data)
paginator = PageNumberPagination()
employees_queryset = Employee.objects.all()
employees_filter_queryset = self.filterset_class(
request.GET, queryset=employees_queryset).qs
field_name = request.GET.get("groupby_field", None)
if field_name:
url = request.build_absolute_uri()
return groupby_queryset(request, url, field_name, employees_filter_queryset)
page = paginator.paginate_queryset(employees_filter_queryset, request)
serializer = EmployeeSerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
@manager_permission_required("employee.change_employee")
def post(self, request):
serializer = EmployeeSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def put(self, request, pk):
user = request.user
employee = Employee.objects.get(pk=pk)
is_manager = EmployeeWorkInformation.objects.filter(
reporting_manager_id=user.employee_get).first()
if employee == user.employee_get or is_manager or user.has_perm("employee.change_employee"):
serializer = EmployeeSerializer(
employee, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
return Response({"error": "You don't have permission"}, status=400)
@method_decorator(permission_required('employee.delete_employee'))
def delete(self, request, pk):
try:
employee = Employee.objects.get(pk=pk)
employee.delete()
except Employee.DoesNotExist:
return Response({"error": "Employee does not exist"}, status=status.HTTP_404_NOT_FOUND)
except ProtectedError as e:
return Response({"error": str(e)}, status=status.HTTP_204_NO_CONTENT)
return Response(status=status.HTTP_204_NO_CONTENT)
class EmployeeListAPIView(APIView):
"""
Retrieves a paginated list of employees with optional search functionality.
Methods:
get(request):
- Returns a paginated list of employees.
- Optionally filters employees based on a search query in the first or last name.
"""
permission_classes = [IsAuthenticated]
def get(self, request):
paginator = PageNumberPagination()
paginator.page_size = 13
search = request.query_params.get('search',None)
if search:
employees_queryset = Employee.objects.filter(Q(employee_first_name__icontains = search)|Q(employee_last_name__icontains = search))
else:
employees_queryset = Employee.objects.all()
page = paginator.paginate_queryset(employees_queryset, request)
serializer = EmployeeListSerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
class EmployeeBankDetailsAPIView(APIView):
"""
Manage employee bank details with CRUD operations.
Methods:
get(request, pk=None):
- Retrieves bank details for a specific employee if `pk` is provided.
- Returns a paginated list of all employee bank details if `pk` is not provided.
post(request):
- Creates a new bank detail entry for an employee.
put(request, pk):
- Updates existing bank details for an employee identified by `pk`.
delete(request, pk):
- Deletes bank details for an employee identified by `pk`.
"""
permission_classes = [IsAuthenticated]
def get_queryset(self):
queryset = EmployeeBankDetails.objects.all()
user = self.request.user
# checking user level permissions
perm = "base.view_employeebankdetails"
queryset = permission_based_queryset(user, perm, queryset)
return queryset
def get(self, request, pk=None):
if pk:
try:
bank_detail = EmployeeBankDetails.objects.get(pk=pk)
except EmployeeBankDetails.DoesNotExist:
return Response({"error": "Bank details do not exist"}, status=status.HTTP_404_NOT_FOUND)
serializer = EmployeeBankDetailsSerializer(bank_detail)
return Response(serializer.data)
paginator = PageNumberPagination()
employee_bank_details = self.get_queryset(request)
page = paginator.paginate_queryset(employee_bank_details, request)
serializer = EmployeeBankDetailsSerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
@manager_or_owner_permission_required(EmployeeBankDetails, "employee.add_employeebankdetails")
def post(self, request):
serializer = EmployeeBankDetailsSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@manager_or_owner_permission_required(EmployeeBankDetails, "employee.add_employeebankdetails")
def put(self, request, pk):
try:
bank_detail = EmployeeBankDetails.objects.get(pk=pk)
except EmployeeBankDetails.DoesNotExist:
return Response({"error": "Bank details do not exist"}, status=status.HTTP_404_NOT_FOUND)
serializer = EmployeeBankDetailsSerializer(
bank_detail, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@manager_permission_required("employee.change_employeebankdetails")
def delete(self, request, pk):
try:
bank_detail = EmployeeBankDetails.objects.get(pk=pk)
bank_detail.delete()
except EmployeeBankDetails.DoesNotExist:
return Response({"error": "Bank details do not exist"}, status=status.HTTP_404_NOT_FOUND)
except Exception as E:
return Response({"error": str(E)}, status=400)
return Response(status=status.HTTP_204_NO_CONTENT)
class EmployeeWorkInformationAPIView(APIView):
"""
Manage employee work information with CRUD operations.
Methods:
get(request, pk):
- Retrieves work information for a specific employee identified by `pk`.
post(request):
- Creates a new work information entry for an employee.
put(request, pk):
- Updates existing work information for an employee identified by `pk`.
delete(request, pk):
- Deletes work information for an employee identified by `pk`.
"""
permission_classes = [IsAuthenticated]
def get(self, request, pk):
work_info = EmployeeWorkInformation.objects.get(pk=pk)
serializer = EmployeeWorkInformationSerializer(work_info)
return Response(serializer.data)
@manager_permission_required("employee.add_employeeworkinformation")
def post(self, request):
serializer = EmployeeWorkInformationSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@manager_permission_required("employee.change_employeeworkinformation")
def put(self, request, pk):
try:
work_info = EmployeeWorkInformation.objects.get(pk=pk)
except EmployeeWorkInformation.DoesNotExist:
raise Http404
serializer = EmployeeWorkInformationSerializer(
work_info, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@method_decorator(permission_required("employee.delete_employeeworkinformation"), name='dispatch')
def delete(self, request, pk):
try:
work_info = EmployeeWorkInformation.objects.get(pk=pk)
except EmployeeWorkInformation.DoesNotExist:
raise Http404
work_info.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class EmployeeWorkInfoExportView(APIView):
"""
Endpoint for exporting employee work information.
Methods:
get(request):
- Exports work information data based on user permissions.
"""
permission_classes = [IsAuthenticated]
@manager_permission_required("employee.add_employeeworkinformation")
def get(self, request):
return work_info_export(request)
class EmployeeWorkInfoImportView(APIView):
"""
Endpoint for importing employee work information.
Methods:
get(request):
- Handles the importing of work information data based on user permissions.
"""
permission_classes = [IsAuthenticated]
@manager_permission_required("employee.add_employeeworkinformation")
def get(self, request):
return work_info_import(request)
class EmployeeBulkUpdateView(APIView):
"""
Endpoint for bulk updating employee and work information.
Permissions:
- Requires authentication and "change_employee" permission.
0
Methods:
put(request):
- Updates multiple employees and their work information.
"""
permission_classes = [IsAuthenticated]
@method_decorator(permission_required("employee.change_employee"), name='dispatch')
def put(self, request):
employee_ids = request.data.get('ids', [])
employees = Employee.objects.filter(id__in=employee_ids)
employee_work_info = EmployeeWorkInformation.objects.filter(
employee_id__in=employees)
employee_data = request.data.get('employee_data', {})
work_info_data = request.data.get("employee_work_info", {})
fields_to_remove = [
"badge_id",
"employee_first_name",
"employee_last_name",
"is_active",
"email",
"phone",
"employee_bank_details__account_number",
]
for field in fields_to_remove:
employee_data.pop(field, None)
work_info_data.pop(field, None)
try:
employees.update(**employee_data)
employee_work_info.update(**work_info_data)
except Exception as e:
return Response({"error": str(e)}, status=400)
return Response({"status": "success"}, status=200)
class DisciplinaryActionAPIView(APIView):
"""
Endpoint for managing disciplinary actions.
Permissions:
- Requires authentication.
Methods:
get(request, pk=None):
- Retrieves a specific disciplinary action by `pk` or lists all disciplinary actions with optional filtering.
post(request):
- Creates a new disciplinary action.
put(request, pk):
- Updates an existing disciplinary action by `pk`.
delete(request, pk):
- Deletes a specific disciplinary action by `pk`.
"""
filterset_class = DisciplinaryActionFilter
permission_classes = [IsAuthenticated]
def get_object(self, pk):
try:
return DisciplinaryAction.objects.get(pk=pk)
except DisciplinaryAction.DoesNotExist:
raise Http404
def get(self, request, pk=None):
if pk:
disciplinary_action = self.get_object(pk)
serializer = DisciplinaryActionSerializer(disciplinary_action)
return Response(serializer.data, status=200)
else:
paginator = PageNumberPagination()
disciplinary_actions = DisciplinaryAction.objects.all()
disciplinary_action_filter_queryset = self.filterset_class(
request.GET, queryset=disciplinary_actions).qs
page = paginator.paginate_queryset(
disciplinary_action_filter_queryset, request)
serializer = DisciplinaryActionSerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
def post(self, request):
serializer = DisciplinaryActionSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def put(self, request, pk):
disciplinary_action = self.get_object(pk)
serializer = DisciplinaryActionSerializer(
disciplinary_action, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
disciplinary_action = self.get_object(pk)
disciplinary_action.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class PolicyAPIView(APIView):
"""
Endpoint for managing policies.
Permissions:
- Requires authentication.
Methods:
get(request, pk=None):
- Retrieves a specific policy by `pk` or lists all policies with optional search functionality.
post(request):
- Creates a new policy.
put(request, pk):
- Updates an existing policy by `pk`.
delete(request, pk):
- Deletes a specific policy by `pk`.
"""
permission_classes = [IsAuthenticated]
def get_object(self, pk):
try:
return Policy.objects.get(pk=pk)
except Policy.DoesNotExist:
raise Http404
def get(self, request, pk=None):
if pk:
policy = self.get_object(pk)
serializer = PolicySerializer(policy)
return Response(serializer.data)
else:
search = request.GET.get("search", None)
if search:
policies = Policy.objects.filter(title__icontains=search)
else:
policies = Policy.objects.all()
serializer = PolicySerializer(policies, many=True)
paginator = PageNumberPagination()
page = paginator.paginate_queryset(
policies, request)
serializer = PolicySerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
def post(self, request):
serializer = PolicySerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
def put(self, request, pk):
policy = self.get_object(pk)
serializer = PolicySerializer(policy, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=400)
def delete(self, request, pk):
policy = self.get_object(pk)
policy.delete()
return Response(status=204)
class DocumentRequestAPIView(APIView):
"""
Endpoint for managing document requests.
Permissions:
- Requires authentication.
- Specific actions require manager-level permissions.
Methods:
get(request, pk=None):
- Retrieves a specific document request by `pk` or lists all document requests with pagination.
post(request):
- Creates a new document request and notifies relevant employees.
put(request, pk):
- Updates an existing document request by `pk`.
delete(request, pk):
- Deletes a specific document request by `pk`.
"""
permission_classes = [IsAuthenticated]
def get_object(self, pk):
try:
return DocumentRequest.objects.get(pk=pk)
except DocumentRequest.DoesNotExist:
raise Http404
def get(self, request, pk=None):
if pk:
document_request = self.get_object(pk)
serializer = DocumentRequestSerializer(document_request)
return Response(serializer.data)
else:
document_requests = DocumentRequest.objects.all()
pagination = PageNumberPagination()
page = pagination.paginate_queryset(
document_requests, request)
serializer = DocumentRequestSerializer(
page, many=True)
return pagination.get_paginated_response(serializer.data)
@manager_permission_required("horilla_documents.add_documentrequests")
def post(self, request):
serializer = DocumentRequestSerializer(data=request.data)
if serializer.is_valid():
obj = serializer.save()
try:
employees = [
user.employee_user_id for user in obj.employee_id.all()]
notify.send(
request.user.employee_get,
recipient=employees,
verb=f"{request.user.employee_get} requested a document.",
verb_ar=f"طلب {request.user.employee_get} مستنداً.",
verb_de=f"{request.user.employee_get} hat ein Dokument angefordert.",
verb_es=f"{request.user.employee_get} solicitó un documento.",
verb_fr=f"{request.user.employee_get} a demandé un document.",
redirect="/employee/employee-profile",
icon="chatbox-ellipses",
api_redirect=f"/api/employee/document-request/{obj.id}"
)
except:
pass
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@manager_permission_required("horilla_documents.change_documentrequests")
def put(self, request, pk):
document_request = self.get_object(pk)
serializer = DocumentRequestSerializer(
document_request, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@method_decorator(permission_required("employee.delete_employee", raise_exception=True))
def delete(self, request, pk):
document_request = self.get_object(pk)
document_request.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class DocumentAPIView(APIView):
filterset_class = DocumentRequestFilter
permission_classes = [IsAuthenticated]
def get_object(self, pk):
try:
return Document.objects.get(pk=pk)
except Document.DoesNotExist:
raise Http404
def get(self, request, pk=None):
if pk:
document = self.get_object(pk)
serializer = DocumentSerializer(document)
return Response(serializer.data)
else:
documents = Document.objects.all()
document_requests_filtered = self.filterset_class(
request.GET, queryset=documents).qs
paginator = PageNumberPagination()
page = paginator.paginate_queryset(
document_requests_filtered, request)
serializer = DocumentSerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
@manager_or_owner_permission_required(DocumentRequest, "horilla_documents.add_document")
def post(self, request):
serializer = DocumentSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
try:
notify.send(
request.user.employee_get,
recipient=request.user.employee_get.get_reporting_manager().employee_user_id,
verb=f"{request.user.employee_get} uploaded a document",
verb_ar=f"قام {request.user.employee_get} بتحميل مستند",
verb_de=f"{request.user.employee_get} hat ein Dokument hochgeladen",
verb_es=f"{request.user.employee_get} subió un documento",
verb_fr=f"{request.user.employee_get} a téléchargé un document",
redirect=f"/employee/employee-view/{request.user.employee_get.id}/",
icon="chatbox-ellipses",
api_redirect=f"/api/employee/documents/"
)
except:
pass
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@method_decorator(owner_can_enter("horilla_documents.change_document", Employee))
def put(self, request, pk):
document = self.get_object(pk)
serializer = DocumentSerializer(document, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@method_decorator(owner_can_enter("horilla_documents.delete_document", Employee))
def delete(self, request, pk):
document = self.get_object(pk)
document.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class DocumentRequestApproveRejectView(APIView):
permission_classes = [IsAuthenticated]
@manager_permission_required("horilla_documents.add_document")
def post(self, request, id, status):
document = Document.objects.filter(id=id).first()
document.status = status
document.save()
return Response({"status": "success"}, status=200)
class DocumentBulkApproveRejectAPIView(APIView):
permission_classes = [IsAuthenticated]
@manager_permission_required("horilla_documents.add_document")
def put(self, request):
ids = request.data.get("ids", None)
status = request.data.get("status", None)
status_code = 200
if ids:
documents = Document.objects.filter(id__in=ids)
response = []
for document in documents:
if not document.document:
status_code = 400
response.append(
{"id": document.id, "error": "No documents"})
continue
response.append({"id": document.id, "status": "success"})
document.status = status
document.save()
return Response(response, status=status_code)
class EmployeeBulkArchiveView(APIView):
permission_classes = [IsAuthenticated]
@method_decorator(permission_required("employee.delete_employee", raise_exception=True))
def post(self, request, is_active):
ids = request.data.get("ids")
error = []
for employee_id in ids:
employee = Employee.objects.get(id=employee_id)
employee.is_active = is_active
employee.employee_user_id.is_active = is_active
if employee.get_archive_condition() is False:
employee.save()
error.append({"employee": str(employee),
"error": "Related model found for this employee. "})
return Response(error, status=200)
class EmployeeArchiveView(APIView):
permission_classes = [IsAuthenticated]
@method_decorator(permission_required("employee.delete_employee", raise_exception=True))
def post(self, request, id, is_active):
employee = Employee.objects.get(id=id)
employee.is_active = is_active
employee.employee_user_id.is_active = is_active
response = None
if employee.get_archive_condition() is False:
employee.save()
else:
response = {"employee": str(
employee), "error": employee.get_archive_condition()}
return Response(response, status=200)
class EmployeeSelectorView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
employee = request.user.employee_get
employees = Employee.objects.filter(employee_user_id=request.user)
is_manager = EmployeeWorkInformation.objects.filter(
reporting_manager_id=employee
).exists()
if is_manager:
employees = Employee.objects.filter(
Q(pk=employee.pk) | Q(
employee_work_info__reporting_manager_id=employee)
)
if request.user.has_perm("employee.view_employee"):
employees = Employee.objects.all()
paginator = PageNumberPagination()
page = paginator.paginate_queryset(
employees, request)
serializer = EmployeeSelectorSerializer(page, many=True)
return paginator.get_paginated_response(serializer.data)
class ReportingManagerCheck(APIView):
permission_classes = [IsAuthenticated]
def get(self,request):
if Employee.objects.filter(employee_work_info__reporting_manager_id =request.user.employee_get):
return Response(status=200)
return Response(status=404)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,65 @@
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from ...api_serializers.notifications.serializers import NotificationSerializer
from rest_framework.pagination import PageNumberPagination
# Create your views here.
class NotificationView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request, type):
if type == 'all':
queryset = request.user.notifications.all()
elif type == 'unread':
queryset = request.user.notifications.unread()
pagination = PageNumberPagination()
page = pagination.paginate_queryset(queryset, request)
serializer = NotificationSerializer(page, many=True)
return pagination.get_paginated_response(serializer.data)
class NotificationReadDelView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request, id):
obj = request.user.notifications.filter(id=id).first()
obj.mark_as_read()
serializer = NotificationSerializer(obj)
return Response(serializer.data, status=200)
def delete(self, request, id):
obj = request.user.notifications.filter(id=id).first()
obj.deleted = True
obj.save()
return Response({"status": "deleted"}, status=200)
class NotificationBulkReadDelView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
obj = request.user.notifications.all()
obj.mark_all_as_read()
return Response({"status": "marked as read"}, status=200)
def delete(self, request):
obj = request.user.notifications.all()
obj.mark_all_as_deleted()
return Response({"status": "deleted"}, status=200)
class NotificationBulkDelUnreadMessageView(APIView):
permission_classes = [IsAuthenticated]
def delete(self, request):
obj = request.user.notifications.unread()
obj.mark_all_as_deleted()
return Response({"status": "deleted"}, status=200)

View File

@@ -0,0 +1,348 @@
from collections import defaultdict
import gettext
from django.shortcuts import render
from rest_framework.views import APIView
from base.backends import ConfiguredEmailBackend
from payroll.models.tax_models import TaxBracket
from payroll.threadings.mail import MailSendThread
from payroll.views.views import payslip_pdf
from ... api_serializers.payroll.serializers import AllowanceSerializer, ContractSerializer, DeductionSerializer, LoanAccountSerializer, PayslipSerializer, ReimbursementSerializer, TaxBracketSerializer
from ...api_methods.base.methods import groupby_queryset
from payroll.filters import AllowanceFilter, ContractFilter, DeductionFilter, PayslipFilter
from payroll.models.models import Allowance, Contract, Deduction, LoanAccount, Payslip, Reimbursement
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django.contrib.auth.decorators import permission_required
from django.utils.decorators import method_decorator
class PayslipView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request,id=None):
if id:
payslip = Payslip.objects.filter(id=id).first()
if request.user.has_perm("payroll.view_payslip") or payslip.employee_id == request.user.employee_get:
serializer = PayslipSerializer(payslip)
return Response(serializer.data,status=200)
if request.user.has_perm("payroll.view_payslip"):
payslips = Payslip.objects.all()
else:
payslips = Payslip.objects.filter(
employee_id__employee_user_id=request.user
)
payslip_filter_queryset = PayslipFilter(request.GET, payslips).qs
# groupby workflow
field_name = request.GET.get("groupby_field", None)
if field_name:
url = request.build_absolute_uri()
return groupby_queryset(request, url, field_name, payslip_filter_queryset)
pagination = PageNumberPagination()
page = pagination.paginate_queryset(payslip_filter_queryset, request)
serializer = PayslipSerializer(page, many=True)
return pagination.get_paginated_response(serializer.data)
class PayslipDownloadView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request, id):
if request.user.has_perm("payroll.view_payslip"):
return payslip_pdf(request, id)
if Payslip.objects.filter(id=id, employee_id=request.user.employee_get):
return payslip_pdf(request, id)
else:
raise Response({"error":"You don't have permission"})
class PayslipSendMailView(APIView):
permission_classes = [IsAuthenticated]
@method_decorator(permission_required("payroll.add_payslip", raise_exception=True))
def post(self, request):
email_backend = ConfiguredEmailBackend()
if not getattr(
email_backend, "dynamic_username_with_display_name", None
) or not len(email_backend.dynamic_username_with_display_name):
return Response({"error": "Email server is not configured"}, status=400)
payslip_ids = request.data.get("id", [])
payslips = Payslip.objects.filter(id__in=payslip_ids)
result_dict = defaultdict(
lambda: {"employee_id": None, "instances": [], "count": 0}
)
for payslip in payslips:
employee_id = payslip.employee_id
result_dict[employee_id]["employee_id"] = employee_id
result_dict[employee_id]["instances"].append(payslip)
result_dict[employee_id]["count"] += 1
mail_thread = MailSendThread(
request, result_dict=result_dict, ids=payslip_ids)
mail_thread.start()
return Response({"status": "success"}, status=200)
class ContractView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request,id=None):
if id :
contract = Contract.objects.filter(id=id).first()
serializer = ContractSerializer(contract)
return Response(serializer.data,status=200)
if request.user.has_perm("payroll.view_contract"):
contracts = Contract.objects.all()
else:
contracts = Contract.objects.filter(
employee_id=request.user.employee_get)
filter_queryset = ContractFilter(request.GET, contracts).qs
# groupby workflow
field_name = request.GET.get("groupby_field", None)
if field_name:
url = request.build_absolute_uri()
return groupby_queryset(request, url, field_name, filter_queryset)
pagination = PageNumberPagination()
page = pagination.paginate_queryset(filter_queryset, request)
serializer = ContractSerializer(page, many=True)
return pagination.get_paginated_response(serializer.data)
@method_decorator(permission_required("payroll.add_contract", raise_exception=True))
def post(self, request):
serializer = ContractSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@method_decorator(permission_required("payroll.change_contract", raise_exception=True))
def put(self, request, pk):
contract = Contract.objects.get(id=pk)
serializer = ContractSerializer(instance=contract, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@method_decorator(permission_required("payroll.delete_contract", raise_exception=True))
def delete(self, request, pk):
contract = Contract.objects.get(id=pk)
contract.delete()
return Response({"status": "deleted"}, status=200)
class AllowanceView(APIView):
permission_classes = [IsAuthenticated]
@method_decorator(permission_required("payroll.view_allowance", raise_exception=True))
def get(self, request,pk=None):
if pk:
allowance = Allowance.objects.get(id=pk)
serializer = AllowanceSerializer(instance=allowance)
return Response(serializer.data,status=200)
allowance = Allowance.objects.all()
filter_queryset = AllowanceFilter(request.GET, allowance).qs
pagination = PageNumberPagination()
page = pagination.paginate_queryset(filter_queryset, request)
serializer = AllowanceSerializer(page, many=True)
return pagination.get_paginated_response(serializer.data)
@method_decorator(permission_required("payroll.add_allowance", raise_exception=True))
def post(self, request):
serializer = AllowanceSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@method_decorator(permission_required("payroll.change_allowance", raise_exception=True))
def put(self, request, pk):
contract = Allowance.objects.get(id=pk)
serializer = AllowanceSerializer(instance=contract, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@method_decorator(permission_required("payroll.delete_allowance", raise_exception=True))
def delete(self, request, pk):
contract = Allowance.objects.get(id=pk)
contract.delete()
return Response({"status": "deleted"}, status=200)
class DeductionView(APIView):
permission_classes = [IsAuthenticated]
@method_decorator(permission_required("payroll.view_deduction", raise_exception=True))
def get(self, request,pk=None):
if pk:
deduction = Deduction.objects.get(id=pk)
serializer = DeductionSerializer(instance=deduction)
return Response(serializer.data,status=200)
deduction = Deduction.objects.all()
filter_queryset = DeductionFilter(request.GET, deduction).qs
pagination = PageNumberPagination()
page = pagination.paginate_queryset(filter_queryset, request)
serializer = DeductionSerializer(page, many=True)
return pagination.get_paginated_response(serializer.data)
@method_decorator(permission_required("payroll.add_deduction", raise_exception=True))
def post(self, request):
serializer = DeductionSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@method_decorator(permission_required("payroll.change_deduction", raise_exception=True))
def put(self, request, pk):
contract = Deduction.objects.get(id=pk)
serializer = DeductionSerializer(instance=contract, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@method_decorator(permission_required("payroll.delete_deduction", raise_exception=True))
def delete(self, request, pk):
contract = Deduction.objects.get(id=pk)
contract.delete()
return Response({"status": "deleted"}, status=200)
class LoanAccountView(APIView):
permission_classes = [IsAuthenticated]
@method_decorator(permission_required("payroll.add_loanaccount", raise_exception=True))
def post(self, request):
serializer = LoanAccountSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@method_decorator(permission_required("payroll.view_loanaccount", raise_exception=True))
def get(self, request, pk=None):
if pk:
loan_account = LoanAccount.objects.get(id=pk)
serializer = LoanAccountSerializer(instance=loan_account)
return Response(serializer.data, status=200)
loan_accounts = LoanAccount.objects.all()
pagination = PageNumberPagination()
page = pagination.paginate_queryset(loan_accounts, request)
serializer = LoanAccountSerializer(page, many=True)
return pagination.get_paginated_response(serializer.data)
@method_decorator(permission_required("payroll.change_loanaccount", raise_exception=True))
def put(self, request, pk):
loan_account = LoanAccount.objects.get(id=pk)
serializer = LoanAccountSerializer(loan_account, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@method_decorator(permission_required("payroll.delete_loanaccount", raise_exception=True))
def delete(self, request, pk):
loan_account = LoanAccount.objects.get(id=pk)
loan_account.delete()
return Response(status=200)
class ReimbursementView(APIView):
serializer_class = ReimbursementSerializer
permission_classes = [IsAuthenticated]
def get(self, request, pk=None):
if pk:
reimbursement = Reimbursement.objects.get(id=pk)
serializer = self.serializer_class(reimbursement)
return Response(serializer.data, status=200)
reimbursements = Reimbursement.objects.all()
if request.user.has_perm("payroll.view_reimbursement"):
reimbursements = Reimbursement.objects.all()
else:
reimbursements = Reimbursement.objects.filter(
employee_id=request.user.employee_get)
pagination = PageNumberPagination()
page = pagination.paginate_queryset(reimbursements, request)
serializer = self.serializer_class(page, many=True)
return pagination.get_paginated_response(serializer.data)
def post(self, request):
serializer = self.serializer_class(
data=request.data, context={'request': request})
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@method_decorator(permission_required("payroll.change_reimbursement", raise_exception=True))
def put(self, request, pk):
reimbursement = Reimbursement.objects.get(id=pk)
serializer = self.serializer_class(
instance=reimbursement, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
@method_decorator(permission_required("payroll.delete_reimbursement", raise_exception=True))
def delete(self, request, pk):
reimbursement = Reimbursement.objects.get(id=pk)
reimbursement.delete()
return Response(status=200)
class ReimbusementApproveRejectView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request, pk):
status = request.data.get('status', None)
amount = request.data.get('amount', None)
amount = eval(request.data.get('amount')
) if request.data.get('amount') else 0
amount = max(0, amount)
reimbursement = Reimbursement.objects.filter(id=pk)
if amount:
reimbursement.update(amount=amount)
reimbursement.update(status=status)
return Response({"status": reimbursement.first().status}, status=200)
class TaxBracketView(APIView):
def get(self, request, pk=None):
if pk:
tax_bracket = TaxBracket.objects.get(id=pk)
serializer = TaxBracketSerializer(tax_bracket)
return Response(serializer.data, status=200)
tax_brackets = TaxBracket.objects.all()
serializer = TaxBracketSerializer(instance=tax_brackets, many=True)
return Response(serializer.data, status=200)
def post(self, request):
serializer = TaxBracketSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
def put(self, request, pk):
tax_bracket = TaxBracket.objects.get(id=pk)
serializer = TaxBracketSerializer(
instance=tax_bracket, data=request.data, partial=True)
if serializer.save():
serializer.save()
return Response(serializer.data, status=200)
return Response(serializer.errors, status=400)
def delete(self, request, pk):
tax_bracket = TaxBracket.objects.get(id=pk)
tax_bracket.delete()
return Response(status=200)

6
horilla_api/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class HorillaApiConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'horilla_api'

View File

3
horilla_api/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
horilla_api/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

13
horilla_api/urls.py Normal file
View File

@@ -0,0 +1,13 @@
from django.urls import path,include
urlpatterns = [
path('auth/', include('horilla_api.api_urls.auth.urls')),
path('asset/', include('horilla_api.api_urls.asset.urls')),
path('base/', include('horilla_api.api_urls.base.urls')),
path('employee/', include('horilla_api.api_urls.employee.urls')),
path('notifications/', include('horilla_api.api_urls.notifications.urls')),
path('payroll/', include('horilla_api.api_urls.payroll.urls')),
path('attendance/', include('horilla_api.api_urls.attendance.urls')),
path('leave/', include('horilla_api.api_urls.leave.urls')),
]