From 81ab7cf5d6261404600d80156e6601d9e4ca696e Mon Sep 17 00:00:00 2001 From: krantheman Date: Fri, 19 Apr 2024 16:48:18 +0530 Subject: [PATCH 1/6] fix(Income Tax Computation): eval locals for Income Tax Slab (cherry picked from commit 180fdb11f043db41f165e45aa2843e1d782757fe) # Conflicts: # hrms/payroll/report/income_tax_computation/income_tax_computation.py --- .../income_tax_computation.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/hrms/payroll/report/income_tax_computation/income_tax_computation.py b/hrms/payroll/report/income_tax_computation/income_tax_computation.py index bc6fb326a6..61e2e9c6b2 100644 --- a/hrms/payroll/report/income_tax_computation/income_tax_computation.py +++ b/hrms/payroll/report/income_tax_computation/income_tax_computation.py @@ -423,9 +423,20 @@ def get_applicable_tax(self): tax_slab = emp_details.get("income_tax_slab") if tax_slab: tax_slab = frappe.get_cached_doc("Income Tax Slab", tax_slab) - employee_dict = frappe.get_doc("Employee", emp).as_dict() + salary_slip = frappe.new_doc("Salary Slip") + salary_slip.employee = emp + salary_slip.salary_structure = emp_details.salary_structure + salary_slip.process_salary_structure() + eval_locals, __ = salary_slip.get_data_for_eval() tax_amount = calculate_tax_by_tax_slab( +<<<<<<< HEAD emp_details["total_taxable_amount"], tax_slab, eval_globals=None, eval_locals=employee_dict +======= + emp_details["total_taxable_amount"], + tax_slab, + eval_globals=None, + eval_locals=eval_locals, +>>>>>>> 180fdb11f (fix(Income Tax Computation): eval locals for Income Tax Slab) ) else: tax_amount = 0.0 From 25cbc849f8f08058446a8880e2f36d1ecb105c2a Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 26 Apr 2024 22:32:51 +0530 Subject: [PATCH 2/6] fix: get salary slip from the correct payroll period for data evaluation (cherry picked from commit bd55a52f44d7f7e749a312ac1bd860e72317c02a) --- .../income_tax_computation.py | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/hrms/payroll/report/income_tax_computation/income_tax_computation.py b/hrms/payroll/report/income_tax_computation/income_tax_computation.py index 61e2e9c6b2..ae20388e60 100644 --- a/hrms/payroll/report/income_tax_computation/income_tax_computation.py +++ b/hrms/payroll/report/income_tax_computation/income_tax_computation.py @@ -160,7 +160,7 @@ def get_last_salary_slip(self, employee): "docstatus": 1, "start_date": ["between", [self.payroll_period_start_date, self.payroll_period_end_date]], }, - ["start_date", "end_date", "salary_structure", "payroll_frequency"], + ["name", "start_date", "end_date", "salary_structure", "payroll_frequency"], order_by="start_date desc", as_dict=1, ) @@ -423,18 +423,14 @@ def get_applicable_tax(self): tax_slab = emp_details.get("income_tax_slab") if tax_slab: tax_slab = frappe.get_cached_doc("Income Tax Slab", tax_slab) - salary_slip = frappe.new_doc("Salary Slip") - salary_slip.employee = emp - salary_slip.salary_structure = emp_details.salary_structure - salary_slip.process_salary_structure() - eval_locals, __ = salary_slip.get_data_for_eval() + eval_globals, eval_locals = self.get_data_for_eval(emp, emp_details) tax_amount = calculate_tax_by_tax_slab( <<<<<<< HEAD emp_details["total_taxable_amount"], tax_slab, eval_globals=None, eval_locals=employee_dict ======= emp_details["total_taxable_amount"], tax_slab, - eval_globals=None, + eval_globals=eval_globals, eval_locals=eval_locals, >>>>>>> 180fdb11f (fix(Income Tax Computation): eval locals for Income Tax Slab) ) @@ -445,6 +441,28 @@ def get_applicable_tax(self): tax_amount = rounded(tax_amount) emp_details["applicable_tax"] = tax_amount + def get_data_for_eval(self, emp: str, emp_details: dict) -> tuple: + last_ss = self.get_last_salary_slip(emp) + + if last_ss: + salary_slip = frappe.get_cached_doc("Salary Slip", last_ss.name) + else: + salary_slip = frappe.new_doc("Salary Slip") + salary_slip.employee = emp + salary_slip.salary_structure = emp_details.salary_structure + salary_slip.start_date = self.payroll_period_start_date + salary_slip.payroll_frequency = frappe.db.get_value( + "Salary Structure", emp_details.salary_structure, "payroll_frequency" + ) + salary_slip.end_date = get_start_end_dates( + salary_slip.payroll_frequency, salary_slip.start_date + ).end_date + salary_slip.process_salary_structure() + + eval_locals, __ = salary_slip.get_data_for_eval() + + return salary_slip.whitelisted_globals, eval_locals + def get_total_deducted_tax(self): self.add_column("Total Tax Deducted") From 7d416c3bf67e92195e5ee6716757ee1cafda7fe5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 26 Apr 2024 23:02:52 +0530 Subject: [PATCH 3/6] chore: fix conflicts --- .../report/income_tax_computation/income_tax_computation.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hrms/payroll/report/income_tax_computation/income_tax_computation.py b/hrms/payroll/report/income_tax_computation/income_tax_computation.py index ae20388e60..2bbce0385f 100644 --- a/hrms/payroll/report/income_tax_computation/income_tax_computation.py +++ b/hrms/payroll/report/income_tax_computation/income_tax_computation.py @@ -425,14 +425,10 @@ def get_applicable_tax(self): tax_slab = frappe.get_cached_doc("Income Tax Slab", tax_slab) eval_globals, eval_locals = self.get_data_for_eval(emp, emp_details) tax_amount = calculate_tax_by_tax_slab( -<<<<<<< HEAD - emp_details["total_taxable_amount"], tax_slab, eval_globals=None, eval_locals=employee_dict -======= emp_details["total_taxable_amount"], tax_slab, eval_globals=eval_globals, eval_locals=eval_locals, ->>>>>>> 180fdb11f (fix(Income Tax Computation): eval locals for Income Tax Slab) ) else: tax_amount = 0.0 From a6af0f1850984073088c2a4b0b2c59e6e2e952d2 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sat, 27 Apr 2024 21:43:43 +0530 Subject: [PATCH 4/6] fix(HRA calculation): fetch active salary assignment assigned before payroll period if none in period (cherry picked from commit 005cfd1cd67464ab20eed82fc5bdfd3cbd9db084) # Conflicts: # hrms/hr/utils.py --- hrms/hr/utils.py | 16 ++++++++++++++++ hrms/regional/india/utils.py | 8 +++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/hrms/hr/utils.py b/hrms/hr/utils.py index 09b7fbe294..d8800a2ed5 100644 --- a/hrms/hr/utils.py +++ b/hrms/hr/utils.py @@ -518,16 +518,32 @@ def check_effective_date(from_date, today, frequency, allocate_on_day): def get_salary_assignments(employee, payroll_period): +<<<<<<< HEAD start_date, end_date = frappe.db.get_value( "Payroll Period", payroll_period, ["start_date", "end_date"] ) assignments = frappe.db.get_all( +======= + start_date, end_date = frappe.db.get_value("Payroll Period", payroll_period, ["start_date", "end_date"]) + assignments = frappe.get_all( +>>>>>>> 005cfd1cd (fix(HRA calculation): fetch active salary assignment assigned before payroll period if none in period) "Salary Structure Assignment", filters={"employee": employee, "docstatus": 1, "from_date": ["between", (start_date, end_date)]}, fields=["*"], order_by="from_date", ) + if not assignments: + # if no assignments found for the given period + # get the last one assigned before the period that is still active + assignments = frappe.get_all( + "Salary Structure Assignment", + filters={"employee": employee, "docstatus": 1, "from_date": ["<=", start_date]}, + fields=["*"], + order_by="from_date desc", + limit=1, + ) + return assignments diff --git a/hrms/regional/india/utils.py b/hrms/regional/india/utils.py index c666c9071e..4797225e24 100644 --- a/hrms/regional/india/utils.py +++ b/hrms/regional/india/utils.py @@ -30,7 +30,13 @@ def calculate_annual_eligible_hra_exemption(doc): _("Salary Structure must be submitted before submission of {0}").format(doc.doctype) ) - assignment_dates = [assignment.from_date for assignment in assignments] + period_start_date = frappe.db.get_value("Payroll Period", doc.payroll_period, "start_date") + + assignment_dates = [] + for assignment in assignments: + # if assignment is before payroll period, use period start date to get the correct days + assignment.from_date = max(assignment.from_date, period_start_date) + assignment_dates.append(assignment.from_date) for idx, assignment in enumerate(assignments): if has_hra_component(assignment.salary_structure, hra_component): From 323f65c606c786188e4b371929c12ae453631a40 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sat, 27 Apr 2024 21:44:00 +0530 Subject: [PATCH 5/6] test: single SSA before payroll period for HRA calculation (cherry picked from commit 11a23c6233905d48cc09147ccdc5e165e44b13cb) --- .../test_employee_tax_exemption_declaration.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hrms/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/hrms/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py index 0c7c8ac841..e19e16b702 100644 --- a/hrms/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py +++ b/hrms/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py @@ -132,7 +132,8 @@ def test_india_hra_exemption(self): frappe.flags.country = "India" employee = frappe.get_value("Employee", {"user_id": "employee@taxexemption.com"}, "name") - setup_hra_exemption_prerequisites("Monthly", employee) + # structure assigned before payroll period should still be considered as active + setup_hra_exemption_prerequisites("Monthly", employee, from_date=add_months(PAYROLL_PERIOD_START, -1)) declaration = frappe.get_doc( { @@ -321,7 +322,7 @@ def test_india_hra_exemption_with_bimonthly_payroll_frequency(self): # reset frappe.flags.country = current_country - def test_india_hra_exemption_with_multiple_salary_structure_assignments(self): + def test_india_hra_exemption_with_multiple_assignments(self): from hrms.payroll.doctype.salary_slip.test_salary_slip import create_tax_slab from hrms.payroll.doctype.salary_structure.test_salary_structure import ( create_salary_structure_assignment, @@ -471,7 +472,7 @@ def create_exemption_category(): ).insert() -def setup_hra_exemption_prerequisites(frequency, employee=None): +def setup_hra_exemption_prerequisites(frequency, employee=None, from_date=None): from hrms.payroll.doctype.salary_slip.test_salary_slip import create_tax_slab from hrms.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure @@ -499,6 +500,7 @@ def setup_hra_exemption_prerequisites(frequency, employee=None): company="_Test Company", currency="INR", payroll_period=payroll_period, + from_date=from_date, ) frappe.db.set_value( From acfb1ead177eb0ab084763eaa7b3a5814e0cebec Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sat, 27 Apr 2024 22:07:05 +0530 Subject: [PATCH 6/6] chore: fix conflicts --- hrms/hr/utils.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/hrms/hr/utils.py b/hrms/hr/utils.py index d8800a2ed5..5b2b5df8b6 100644 --- a/hrms/hr/utils.py +++ b/hrms/hr/utils.py @@ -518,15 +518,8 @@ def check_effective_date(from_date, today, frequency, allocate_on_day): def get_salary_assignments(employee, payroll_period): -<<<<<<< HEAD - start_date, end_date = frappe.db.get_value( - "Payroll Period", payroll_period, ["start_date", "end_date"] - ) - assignments = frappe.db.get_all( -======= start_date, end_date = frappe.db.get_value("Payroll Period", payroll_period, ["start_date", "end_date"]) assignments = frappe.get_all( ->>>>>>> 005cfd1cd (fix(HRA calculation): fetch active salary assignment assigned before payroll period if none in period) "Salary Structure Assignment", filters={"employee": employee, "docstatus": 1, "from_date": ["between", (start_date, end_date)]}, fields=["*"],