321 lines
11 KiB
Python
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
|