Files
ihrm/horilla_api/api_urls/payroll/serializers.py
2024-10-10 15:45:44 +05:30

321 lines
11 KiB
Python

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