diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py index 8498b3d277d..bc7dcee55e4 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.py +++ b/erpnext/hr/doctype/additional_salary/additional_salary.py @@ -39,19 +39,21 @@ class AdditionalSalary(Document): return amount_per_day * no_of_days @frappe.whitelist() -def get_additional_salary_component(employee, start_date, end_date): +def get_additional_salary_component(employee, start_date, end_date, component_type): additional_components = frappe.db.sql(""" select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date from `tabAdditional Salary` where employee=%(employee)s and docstatus = 1 and payroll_date between %(from_date)s and %(to_date)s + and type = %(component_type)s group by salary_component, overwrite_salary_structure_amount order by salary_component, overwrite_salary_structure_amount """, { 'employee': employee, 'from_date': start_date, - 'to_date': end_date + 'to_date': end_date, + 'component_type': "Earning" if component_type == "earnings" else "Deduction" }, as_dict=1) additional_components_list = [] diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index eee79747108..d03a3dd9a36 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -299,9 +299,11 @@ class SalarySlip(TransactionBase): def calculate_net_pay(self): if self.salary_structure: - self.calculate_component_amounts() - + self.calculate_component_amounts("earnings") self.gross_pay = self.get_component_totals("earnings") + + if self.salary_structure: + self.calculate_component_amounts("deductions") self.total_deduction = self.get_component_totals("deductions") self.set_loan_repayment() @@ -309,25 +311,27 @@ class SalarySlip(TransactionBase): self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment)) self.rounded_total = rounded(self.net_pay) - def calculate_component_amounts(self): + def calculate_component_amounts(self, component_type): if not getattr(self, '_salary_structure_doc', None): self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure) payroll_period = get_payroll_period(self.start_date, self.end_date, self.company) - self.add_structure_components() - self.add_employee_benefits(payroll_period) - self.add_additional_salary_components() - self.add_tax_components(payroll_period) - self.set_component_amounts_based_on_payment_days() + self.add_structure_components(component_type) + self.add_additional_salary_components(component_type) + if component_type == "earnings": + self.add_employee_benefits(payroll_period) + else: + self.add_tax_components(payroll_period) - def add_structure_components(self): + self.set_component_amounts_based_on_payment_days(component_type) + + def add_structure_components(self, component_type): data = self.get_data_for_eval() - for key in ('earnings', 'deductions'): - for struct_row in self._salary_structure_doc.get(key): - amount = self.eval_condition_and_formula(struct_row, data) - if amount and struct_row.statistical_component == 0: - self.update_component_row(struct_row, amount, key) + for struct_row in self._salary_structure_doc.get(component_type): + amount = self.eval_condition_and_formula(struct_row, data) + if amount and struct_row.statistical_component == 0: + self.update_component_row(struct_row, amount, component_type) def get_data_for_eval(self): '''Returns data for evaluating formula''' @@ -400,14 +404,15 @@ class SalarySlip(TransactionBase): amount = last_benefit.amount self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings") - def add_additional_salary_components(self): - additional_components = get_additional_salary_component(self.employee, self.start_date, self.end_date) + def add_additional_salary_components(self, component_type): + additional_components = get_additional_salary_component(self.employee, + self.start_date, self.end_date, component_type) if additional_components: for additional_component in additional_components: amount = additional_component.amount overwrite = additional_component.overwrite - key = "earnings" if additional_component.type == "Earning" else "deductions" - self.update_component_row(frappe._dict(additional_component.struct_row), amount, key, overwrite=overwrite) + self.update_component_row(frappe._dict(additional_component.struct_row), amount, + component_type, overwrite=overwrite) def add_tax_components(self, payroll_period): # Calculate variable_based_on_taxable_salary after all components updated in salary slip @@ -736,7 +741,7 @@ class SalarySlip(TransactionBase): total += d.amount return total - def set_component_amounts_based_on_payment_days(self): + def set_component_amounts_based_on_payment_days(self, component_type): joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee, ["date_of_joining", "relieving_date"]) @@ -746,9 +751,8 @@ class SalarySlip(TransactionBase): if not joining_date: frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name))) - for component_type in ("earnings", "deductions"): - for d in self.get(component_type): - d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0] + for d in self.get(component_type): + d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0] def set_loan_repayment(self): self.set('loans', []) diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index 78150946c8e..6ca6dfd2c02 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -25,7 +25,6 @@ class TestSalaryStructure(unittest.TestCase): make_employee("test_employee@salary.com") make_employee("test_employee_2@salary.com") - def make_holiday_list(self): if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"): holiday_list = frappe.get_doc({ @@ -38,6 +37,29 @@ class TestSalaryStructure(unittest.TestCase): holiday_list.get_weekly_off_dates() holiday_list.save() + def test_salary_structure_deduction_based_on_gross_pay(self): + + emp = make_employee("test_employee_3@salary.com") + + sal_struct = make_salary_structure("Salary Structure 2", "Monthly", dont_submit = True) + + sal_struct.earnings = [sal_struct.earnings[0]] + sal_struct.earnings[0].amount_based_on_formula = 1 + sal_struct.earnings[0].formula = "base" + + sal_struct.deductions = [sal_struct.deductions[0]] + + sal_struct.deductions[0].amount_based_on_formula = 1 + sal_struct.deductions[0].condition = "gross_pay > 100" + sal_struct.deductions[0].formula = "gross_pay * 0.2" + + sal_struct.submit() + + assignment = create_salary_structure_assignment(emp, "Salary Structure 2") + ss = make_salary_slip(sal_struct.name, employee = emp) + + self.assertEqual(assignment.base * 0.2, ss.deductions[0].amount) + def test_amount_totals(self): frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"})