diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py index 6f57bd6156..1d3c4526d4 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py +++ b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py @@ -45,39 +45,19 @@ class EmployeeBenefitClaim(Document): claimed_amount = self.claimed_amount pro_rata_amount = self.get_pro_rata_amount_in_application(payroll_period.name) if not pro_rata_amount: + pro_rata_amount = 0 # Get pro_rata_amount if there is no application, # get salary structure for the date and calculate pro-rata amount - pro_rata_amount = self.get_benefit_pro_rata_ratio_amount() - if not pro_rata_amount: - pro_rata_amount = 0 + sal_struct_name = get_assigned_salary_structure(self.employee, self.claim_date) + if sal_struct_name: + sal_struct = frappe.get_doc("Salary Structure", sal_struct_name) + pro_rata_amount = get_benefit_pro_rata_ratio_amount(self.employee, self.claim_date, sal_struct) claimed_amount += get_previous_claimed_amount(self.employee, payroll_period, non_pro_rata = True) if max_benefits < pro_rata_amount + claimed_amount: frappe.throw(_("Maximum benefit of employee {0} exceeds {1} by the sum {2} of benefit application pro-rata component\ amount and previous claimed amount").format(self.employee, max_benefits, pro_rata_amount+claimed_amount-max_benefits)) - def get_benefit_pro_rata_ratio_amount(self): - sal_struct_name = get_assigned_salary_structure(self.employee, self.claim_date) - if sal_struct_name: - sal_struct = frappe.get_doc("Salary Structure", sal_struct_name) - total_pro_rata_max = 0 - benefit_amount_total = 0 - for sal_struct_row in sal_struct.get("earnings"): - pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"]) - if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: - total_pro_rata_max += max_benefit_amount - if total_pro_rata_max > 0: - for sal_struct_row in sal_struct.get("earnings"): - pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"]) - if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: - component_max = max_benefit_amount - benefit_amount = component_max * sal_struct.max_benefits / total_pro_rata_max - if benefit_amount > component_max: - benefit_amount = component_max - benefit_amount_total += benefit_amount - return benefit_amount_total - return False - def get_pro_rata_amount_in_application(self, payroll_period): application = frappe.db.exists( "Employee Benefit Application", @@ -91,23 +71,111 @@ class EmployeeBenefitClaim(Document): return frappe.db.get_value("Employee Benefit Application", application, "pro_rata_dispensed_amount") return False -def get_benefit_claim_amount(employee, start_date, end_date, struct_row): - benefit_claim_details = frappe.db.sql(""" - select claimed_amount from `tabEmployee Benefit Claim` +def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct): + total_pro_rata_max = 0 + benefit_amount_total = 0 + for sal_struct_row in sal_struct.get("earnings"): + pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"]) + if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: + total_pro_rata_max += max_benefit_amount + if total_pro_rata_max > 0: + for sal_struct_row in sal_struct.get("earnings"): + pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"]) + if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: + component_max = max_benefit_amount + benefit_amount = component_max * sal_struct.max_benefits / total_pro_rata_max + if benefit_amount > component_max: + benefit_amount = component_max + benefit_amount_total += benefit_amount + return benefit_amount_total + +def get_benefit_claim_amount(employee, start_date, end_date, salary_component): + query = """select claimed_amount from `tabEmployee Benefit Claim` where employee=%(employee)s and docstatus = 1 and pay_against_benefit_claim = 1 - and earning_component = %(earning_component)s - and (claim_date between %(start_date)s and %(end_date)s) - """, { + """ + if not start_date: + query += "and claim_date <= %(end_date)s" + else: + query += "and (claim_date between %(start_date)s and %(end_date)s)" + + if salary_component: + query += "and earning_component = %(earning_component)s" + + benefit_claim_details = frappe.db.sql(query, { 'employee': employee, 'start_date': start_date, 'end_date': end_date, - 'earning_component': struct_row.salary_component + 'earning_component': salary_component }, as_dict = True) - if benefit_claim_details: claimed_amount = 0 for claim_detail in benefit_claim_details: claimed_amount += claim_detail.claimed_amount return claimed_amount return False + +def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period): + pro_rata_amount = 0 + claimed_amount = 0 + application = frappe.db.exists( + "Employee Benefit Application", + { + 'employee': employee, + 'payroll_period': payroll_period.name, + 'docstatus': 1 + } + ) + if application: + application_obj = frappe.get_doc("Employee Benefit Application", application) + pro_rata_amount = application_obj.pro_rata_dispensed_amount + application_obj.max_benefits - application_obj.remainig_benefits + else: + pro_rata_amount = get_benefit_pro_rata_ratio_amount(employee, sal_slip_start_date, sal_struct) + + claimed_amount += get_benefit_claim_amount(employee, payroll_period.start_date, payroll_period.end_date, False) + + return claimed_amount + pro_rata_amount + +def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end_date, current_flexi_amount, payroll_period, sal_struct): + max_benefits = get_max_benefits(employee, payroll_period.end_date) + if not max_benefits: + max_benefits = 0 + remainig_benefits = max_benefits - get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period) + if remainig_benefits > 0: + have_remaining = True + # Set the remainig benefits to flexi non pro-rata component in the salary structure + salary_components_array = [] + for d in sal_struct.get("earnings"): + if d.is_flexible_benefit == 1: + salary_component = frappe.get_doc("Salary Component", d.salary_component) + if salary_component.is_pro_rata_applicable != 1: + claimed_amount = get_benefit_claim_amount(employee, payroll_period.start_date, sal_slip_end_date, d.salary_component) + amount_fit_to_component = salary_component.max_benefit_amount - claimed_amount + if amount_fit_to_component > 0: + if remainig_benefits > amount_fit_to_component: + amount = amount_fit_to_component + remainig_benefits -= amount_fit_to_component + else: + amount = remainig_benefits + have_remaining = False + current_claimed_amount = get_benefit_claim_amount(employee, sal_slip_start_date, sal_slip_end_date, d.salary_component) + amount += current_claimed_amount + struct_row = {} + salary_components_dict = {} + struct_row['depends_on_lwp'] = salary_component.depends_on_lwp + struct_row['salary_component'] = salary_component.name + struct_row['abbr'] = salary_component.salary_component_abbr + struct_row['do_not_include_in_total'] = salary_component.do_not_include_in_total + struct_row['is_tax_applicable'] = salary_component.is_tax_applicable, + struct_row['is_flexible_benefit'] = salary_component.is_flexible_benefit, + struct_row['variable_based_on_taxable_salary'] = salary_component.variable_based_on_taxable_salary + salary_components_dict['amount'] = amount + salary_components_dict['struct_row'] = struct_row + salary_components_array.append(salary_components_dict) + if not have_remaining: + break + + if len(salary_components_array) > 0: + return salary_components_array + + return False diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index e0aef9d7f0..c8834b598a 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -16,7 +16,7 @@ from frappe.utils.background_jobs import enqueue from erpnext.hr.doctype.additional_salary.additional_salary import get_additional_salary_component from erpnext.hr.utils import get_payroll_period from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount -from erpnext.hr.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount +from erpnext.hr.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits class SalarySlip(TransactionBase): def __init__(self, *args, **kwargs): @@ -80,6 +80,8 @@ class SalarySlip(TransactionBase): key = "deductions" self.update_component_row(frappe._dict(additional_component.struct_row), amount, key) + self.get_last_payroll_period_benefit() + # Calculate variable_based_on_taxable_salary after all components updated in salary slip for struct_row in self._salary_structure_doc.get("deductions"): if struct_row.variable_based_on_taxable_salary == 1: @@ -87,6 +89,23 @@ class SalarySlip(TransactionBase): if tax_row and amount: self.update_component_row(frappe._dict(tax_row), amount, "deductions") + def get_last_payroll_period_benefit(self): + payroll_period = get_payroll_period(self.start_date, self.end_date, self.company) + if payroll_period: + # Check for last payroll period + if (getdate(payroll_period.end_date) <= getdate(self.end_date)): + current_flexi_amount = 0 + for d in self.get("earnings"): + if d.is_flexible_benefit == 1: + current_flexi_amount += d.amount + last_benefits = get_last_payroll_period_benefits(self.employee, self.start_date, self.end_date,\ + current_flexi_amount, payroll_period, self._salary_structure_doc) + if last_benefits: + for last_benefit in last_benefits: + last_benefit = frappe._dict(last_benefit) + amount = last_benefit.amount + self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings") + def add_employee_flexi_benefits(self, struct_row): if frappe.db.get_value("Salary Component", struct_row.salary_component, "pay_against_benefit_claim") != 1: benefit_component_amount = get_benefit_component_amount(self.employee, self.start_date, self.end_date, \ @@ -94,7 +113,7 @@ class SalarySlip(TransactionBase): if benefit_component_amount: self.update_component_row(struct_row, benefit_component_amount, "earnings") else: - benefit_claim_amount = get_benefit_claim_amount(self.employee, self.start_date, self.end_date, struct_row) + benefit_claim_amount = get_benefit_claim_amount(self.employee, self.start_date, self.end_date, struct_row.salary_component) if benefit_claim_amount: self.update_component_row(struct_row, benefit_claim_amount, "earnings")