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