[ADD] HORILLA API: Add Horilla API into master code base
This commit is contained in:
@@ -8,4 +8,5 @@ from horilla import (
|
||||
horilla_context_processors,
|
||||
horilla_middlewares,
|
||||
horilla_settings,
|
||||
rest_conf,
|
||||
)
|
||||
|
||||
49
horilla/rest_conf.py
Normal file
49
horilla/rest_conf.py
Normal 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)
|
||||
@@ -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
0
horilla_api/__init__.py
Normal file
3
horilla_api/admin.py
Normal file
3
horilla_api/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
101
horilla_api/api_decorators/base/decorators.py
Normal file
101
horilla_api/api_decorators/base/decorators.py
Normal 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
|
||||
19
horilla_api/api_decorators/employee/decorators.py
Normal file
19
horilla_api/api_decorators/employee/decorators.py
Normal 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
|
||||
11
horilla_api/api_filters/asset/filters.py
Normal file
11
horilla_api/api_filters/asset/filters.py
Normal 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__"
|
||||
66
horilla_api/api_methods/base/methods.py
Normal file
66
horilla_api/api_methods/base/methods.py
Normal 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)
|
||||
31
horilla_api/api_methods/employee/methods.py
Normal file
31
horilla_api/api_methods/employee/methods.py
Normal 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
|
||||
137
horilla_api/api_serializers/asset/serializers.py
Normal file
137
horilla_api/api_serializers/asset/serializers.py
Normal 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__'
|
||||
|
||||
|
||||
200
horilla_api/api_serializers/attendance/serializers.py
Normal file
200
horilla_api/api_serializers/attendance/serializers.py
Normal 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__"
|
||||
12
horilla_api/api_serializers/auth/serializers.py
Normal file
12
horilla_api/api_serializers/auth/serializers.py
Normal 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()
|
||||
398
horilla_api/api_serializers/base/serializers.py
Normal file
398
horilla_api/api_serializers/base/serializers.py
Normal 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']
|
||||
128
horilla_api/api_serializers/employee/serializers.py
Normal file
128
horilla_api/api_serializers/employee/serializers.py
Normal 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']
|
||||
504
horilla_api/api_serializers/leave/serializers.py
Normal file
504
horilla_api/api_serializers/leave/serializers.py
Normal 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
|
||||
8
horilla_api/api_serializers/notifications/serializers.py
Normal file
8
horilla_api/api_serializers/notifications/serializers.py
Normal 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']
|
||||
279
horilla_api/api_serializers/payroll/serializers.py
Normal file
279
horilla_api/api_serializers/payroll/serializers.py
Normal 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
|
||||
0
horilla_api/api_urls/__init__.py
Normal file
0
horilla_api/api_urls/__init__.py
Normal file
13
horilla_api/api_urls/asset/urls.py
Normal file
13
horilla_api/api_urls/asset/urls.py
Normal 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'),
|
||||
]
|
||||
53
horilla_api/api_urls/attendance/urls.py
Normal file
53
horilla_api/api_urls/attendance/urls.py
Normal 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()),
|
||||
]
|
||||
7
horilla_api/api_urls/auth/urls.py
Normal file
7
horilla_api/api_urls/auth/urls.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.urls import path
|
||||
from ...api_views.auth.views import LoginAPIView
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('login/', LoginAPIView.as_view())
|
||||
]
|
||||
66
horilla_api/api_urls/base/urls.py
Normal file
66
horilla_api/api_urls/base/urls.py
Normal 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'),
|
||||
|
||||
|
||||
]
|
||||
44
horilla_api/api_urls/employee/urls.py
Normal file
44
horilla_api/api_urls/employee/urls.py
Normal 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'),
|
||||
]
|
||||
47
horilla_api/api_urls/leave/urls.py
Normal file
47
horilla_api/api_urls/leave/urls.py
Normal 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()),
|
||||
]
|
||||
11
horilla_api/api_urls/notifications/urls.py
Normal file
11
horilla_api/api_urls/notifications/urls.py
Normal 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()),
|
||||
]
|
||||
279
horilla_api/api_urls/payroll/serializers.py
Normal file
279
horilla_api/api_urls/payroll/serializers.py
Normal 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
|
||||
23
horilla_api/api_urls/payroll/urls.py
Normal file
23
horilla_api/api_urls/payroll/urls.py
Normal 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=''),
|
||||
]
|
||||
301
horilla_api/api_views/asset/views.py
Normal file
301
horilla_api/api_views/asset/views.py
Normal 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)
|
||||
|
||||
23
horilla_api/api_views/attendance/permission_views.py
Normal file
23
horilla_api/api_views/attendance/permission_views.py
Normal 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)
|
||||
|
||||
|
||||
917
horilla_api/api_views/attendance/views.py
Normal file
917
horilla_api/api_views/attendance/views.py
Normal 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")
|
||||
27
horilla_api/api_views/auth/views.py
Normal file
27
horilla_api/api_views/auth/views.py
Normal 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'})
|
||||
|
||||
1282
horilla_api/api_views/base/views.py
Normal file
1282
horilla_api/api_views/base/views.py
Normal file
File diff suppressed because it is too large
Load Diff
749
horilla_api/api_views/employee/views.py
Normal file
749
horilla_api/api_views/employee/views.py
Normal 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)
|
||||
|
||||
|
||||
|
||||
1102
horilla_api/api_views/leave/views.py
Normal file
1102
horilla_api/api_views/leave/views.py
Normal file
File diff suppressed because it is too large
Load Diff
65
horilla_api/api_views/notifications/views.py
Normal file
65
horilla_api/api_views/notifications/views.py
Normal 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)
|
||||
|
||||
348
horilla_api/api_views/payroll/views.py
Normal file
348
horilla_api/api_views/payroll/views.py
Normal 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
6
horilla_api/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class HorillaApiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'horilla_api'
|
||||
0
horilla_api/migrations/__init__.py
Normal file
0
horilla_api/migrations/__init__.py
Normal file
3
horilla_api/models.py
Normal file
3
horilla_api/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
3
horilla_api/tests.py
Normal file
3
horilla_api/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
13
horilla_api/urls.py
Normal file
13
horilla_api/urls.py
Normal 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')),
|
||||
]
|
||||
Reference in New Issue
Block a user