Files
ihrm/horilla_api/api_urls/payroll/serializers.py

280 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