Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

revert: fix(Salary Structure Assignment: Preview Salary Slip): Calculation of earnings whose formula is dependent on deductions and so on (backport #2161) #2162

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 2 additions & 28 deletions hrms/payroll/doctype/salary_slip/salary_slip.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# License: GNU General Public License v3. See license.txt


import ast
import unicodedata
from datetime import date

Expand Down Expand Up @@ -792,33 +791,12 @@ def set_gross_pay_and_base_gross_pay():
if self.salary_structure:
self.calculate_component_amounts("deductions")

deduction_abbrs = [d.abbr for d in self.deductions]
self.update_dependent_components_recursively("earnings", deduction_abbrs)

set_gross_pay_and_base_gross_pay()
self.update_dependent_components_recursively("deductions", ["gross_pay", "base_gross_pay"])

set_loan_repayment(self)

self.set_precision_for_component_amounts()
self.set_net_pay()
self.compute_income_tax_breakup()

def update_dependent_components_recursively(
self, component_type: str, updated_var: str | list[str]
) -> None:
def is_var_updated(var: str | list[str]) -> bool:
return var == updated_var if isinstance(updated_var, str) else var in updated_var

other_component_type = "deductions" if component_type == "earnings" else "earnings"

for d in self._salary_structure_doc.get(component_type):
if d.amount_based_on_formula and d.formula:
for var in get_variables_from_formula(d.formula):
if is_var_updated(var):
self.add_structure_component(d, component_type)
self.update_dependent_components_recursively(other_component_type, d.abbr)

def set_net_pay(self):
self.total_deduction = self.get_component_totals("deductions")
self.base_total_deduction = flt(
Expand Down Expand Up @@ -2308,6 +2286,8 @@ def _safe_eval(code: str, eval_globals: dict | None = None, eval_locals: dict |


def _check_attributes(code: str) -> None:
import ast

from frappe.utils.safe_exec import UNSAFE_ATTRIBUTES

unsafe_attrs = set(UNSAFE_ATTRIBUTES).union(["__"]) - {"format"}
Expand Down Expand Up @@ -2346,9 +2326,3 @@ def email_salary_slips(names) -> None:
for name in names:
salary_slip = frappe.get_doc("Salary Slip", name)
salary_slip.email_salary_slip()


def get_variables_from_formula(formula: str) -> list[str]:
# compile expects a string
formula = cstr(formula)
return [node.id for node in ast.walk(ast.parse(formula, mode="eval")) if isinstance(node, ast.Name)]
63 changes: 0 additions & 63 deletions hrms/payroll/doctype/salary_slip/test_salary_slip.py
Original file line number Diff line number Diff line change
Expand Up @@ -1634,69 +1634,6 @@ def test_variable_tax_component(self):
self.assertEqual(test_tds.accounts[0].company, salary_slip.company)
self.assertListEqual(tax_component, ["_Test TDS"])

def test_circular_dependency_in_formula(self):
from hrms.payroll.doctype.salary_structure.test_salary_structure import (
create_salary_structure_assignment,
)

earnings = [
{
"salary_component": "Dependent Earning",
"abbr": "DE",
"type": "Earning",
"depends_on_payment_days": 0,
"amount_based_on_formula": 1,
"formula": "ID * 10",
},
]
make_salary_component(earnings, False, company_list=[])

deductions = [
{
"salary_component": "Independent Deduction",
"abbr": "ID",
"type": "Deduction",
"amount": 500,
},
{
"salary_component": "Dependent Deduction",
"abbr": "DD",
"type": "Deduction",
"amount_based_on_formula": 1,
"formula": "DE / 5\nif DE > 0\n else 0",
},
]
make_salary_component(deductions, False, company_list=[])

details = {
"doctype": "Salary Structure",
"name": "Test Salary Structure for Circular Dependency",
"company": "_Test Company",
"payroll_frequency": "Monthly",
"payment_account": get_random("Account", filters={"account_currency": "USD"}),
"currency": "INR",
}
salary_structure = frappe.get_doc(details)

for entry in earnings:
salary_structure.append("earnings", entry)
for entry in deductions:
salary_structure.append("deductions", entry)

salary_structure.insert()
salary_structure.submit()

emp = make_employee("[email protected]", company="_Test Company")

create_salary_structure_assignment(emp, salary_structure.name, currency="INR")
salary_slip = make_salary_slip(
salary_structure.name, employee=emp, posting_date=getdate(), for_preview=1
)

self.assertEqual(salary_slip.gross_pay, 5000)
self.assertEqual(salary_slip.earnings[0].amount, 5000)
self.assertEqual(salary_slip.deductions[1].amount, 1000)


class TestSalarySlipSafeEval(FrappeTestCase):
def test_safe_eval_for_salary_slip(self):
Expand Down
Loading