Skip to content

Commit

Permalink
Merge pull request #843 from frappe/version-14-hotfix
Browse files Browse the repository at this point in the history
  • Loading branch information
ruchamahabal authored Sep 1, 2023
2 parents a8fe159 + 5da3a42 commit db0a2eb
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 27 deletions.
138 changes: 120 additions & 18 deletions hrms/payroll/doctype/payroll_entry/payroll_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,14 @@ def get_salary_components(self, component_type):
frappe.qb.from_(ss)
.join(ssd)
.on(ss.name == ssd.parent)
.select(ssd.salary_component, ssd.amount, ssd.parentfield, ss.salary_structure, ss.employee)
.select(
ssd.salary_component,
ssd.amount,
ssd.parentfield,
ssd.additional_salary,
ss.salary_structure,
ss.employee,
)
.where(
(ssd.parentfield == component_type) & (ss.name.isin(tuple([d.name for d in salary_slips])))
)
Expand All @@ -305,32 +312,104 @@ def get_salary_component_total(
component_dict = {}

for item in salary_components:
if not self.should_add_component_to_accrual_jv(component_type, item):
continue

employee_cost_centers = self.get_payroll_cost_centers_for_employee(
item.employee, item.salary_structure
)
add_component_to_accrual_jv_entry = True
if component_type == "earnings":
is_flexible_benefit, only_tax_impact = frappe.get_cached_value(
"Salary Component", item["salary_component"], ["is_flexible_benefit", "only_tax_impact"]
)
if is_flexible_benefit == 1 and only_tax_impact == 1:
add_component_to_accrual_jv_entry = False
employee_advance = self.get_advance_deduction(component_type, item)

if add_component_to_accrual_jv_entry:
for cost_center, percentage in employee_cost_centers.items():
amount_against_cost_center = flt(item.amount) * percentage / 100
for cost_center, percentage in employee_cost_centers.items():
amount_against_cost_center = flt(item.amount) * percentage / 100

if employee_advance:
self.add_advance_deduction_entry(
item, amount_against_cost_center, cost_center, employee_advance
)
else:
key = (item.salary_component, cost_center)
component_dict[key] = component_dict.get(key, 0) + amount_against_cost_center

if process_payroll_accounting_entry_based_on_employee:
self.set_employee_based_payroll_payable_entries(
component_type, item.employee, amount_against_cost_center
)
if process_payroll_accounting_entry_based_on_employee:
self.set_employee_based_payroll_payable_entries(
component_type, item.employee, amount_against_cost_center
)

account_details = self.get_account(component_dict=component_dict)

return account_details

def should_add_component_to_accrual_jv(self, component_type: str, item: dict) -> bool:
add_component_to_accrual_jv = True
if component_type == "earnings":
is_flexible_benefit, only_tax_impact = frappe.get_cached_value(
"Salary Component", item["salary_component"], ["is_flexible_benefit", "only_tax_impact"]
)
if cint(is_flexible_benefit) and cint(only_tax_impact):
add_component_to_accrual_jv = False

return add_component_to_accrual_jv

def get_advance_deduction(self, component_type: str, item: dict) -> str | None:
if component_type == "deductions" and item.additional_salary:
ref_doctype, ref_docname = frappe.db.get_value(
"Additional Salary",
item.additional_salary,
["ref_doctype", "ref_docname"],
)

if ref_doctype == "Employee Advance":
return ref_docname
return

def add_advance_deduction_entry(
self,
item: dict,
amount: float,
cost_center: str,
employee_advance: str,
) -> None:
self._advance_deduction_entries.append(
{
"employee": item.employee,
"account": self.get_salary_component_account(item.salary_component),
"amount": amount,
"cost_center": cost_center,
"reference_type": "Employee Advance",
"reference_name": employee_advance,
}
)

def set_accounting_entries_for_advance_deductions(
self,
accounts: list,
currencies: list,
company_currency: str,
accounting_dimensions: list,
precision: int,
payable_amount: float,
):
for entry in self._advance_deduction_entries:
payable_amount = self.get_accounting_entries_and_payable_amount(
entry.get("account"),
entry.get("cost_center"),
entry.get("amount"),
currencies,
company_currency,
payable_amount,
accounting_dimensions,
precision,
entry_type="credit",
accounts=accounts,
party=entry.get("employee"),
reference_type="Employee Advance",
reference_name=entry.get("reference_name"),
is_advance="Yes",
)

return payable_amount

def set_employee_based_payroll_payable_entries(
self, component_type, employee, amount, salary_structure=None
):
Expand Down Expand Up @@ -382,10 +461,11 @@ def get_payroll_cost_centers_for_employee(self, employee, salary_structure):
def get_account(self, component_dict=None):
account_dict = {}
for key, amount in component_dict.items():
account = self.get_salary_component_account(key[0])
accouting_key = (account, key[1])
component, cost_center = key
account = self.get_salary_component_account(component)
accounting_key = (account, cost_center)

account_dict[accouting_key] = account_dict.get(accouting_key, 0) + amount
account_dict[accounting_key] = account_dict.get(accounting_key, 0) + amount

return account_dict

Expand All @@ -395,6 +475,7 @@ def make_accrual_jv_entry(self):
"Payroll Settings", "process_payroll_accounting_entry_based_on_employee"
)
self.employee_based_payroll_payable_entries = {}
self._advance_deduction_entries = []

earnings = (
self.get_salary_component_total(
Expand Down Expand Up @@ -462,6 +543,15 @@ def make_accrual_jv_entry(self):
accounts=accounts,
)

payable_amount = self.set_accounting_entries_for_advance_deductions(
accounts,
currencies,
company_currency,
accounting_dimensions,
precision,
payable_amount,
)

# Payable amount
if process_payroll_accounting_entry_based_on_employee:
"""
Expand Down Expand Up @@ -538,6 +628,9 @@ def get_accounting_entries_and_payable_amount(
entry_type="credit",
party=None,
accounts=None,
reference_type=None,
reference_name=None,
is_advance=None,
):
exchange_rate, amt = self.get_amount_and_exchange_rate_for_journal_entry(
account, amount, company_currency, currencies
Expand Down Expand Up @@ -581,6 +674,15 @@ def get_accounting_entries_and_payable_amount(
}
)

if reference_type:
row.update(
{
"reference_type": reference_type,
"reference_name": reference_name,
"is_advance": is_advance,
}
)

self.update_accounting_dimensions(
row,
accounting_dimensions,
Expand Down
65 changes: 65 additions & 0 deletions hrms/payroll/doctype/payroll_entry/test_payroll_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,19 @@
)
from erpnext.setup.doctype.employee.test_employee import make_employee

from hrms.hr.doctype.employee_advance.employee_advance import (
create_return_through_additional_salary,
)
from hrms.hr.doctype.employee_advance.test_employee_advance import (
make_employee_advance,
make_journal_entry_for_advance,
)
from hrms.payroll.doctype.payroll_entry.payroll_entry import (
PayrollEntry,
get_end_date,
get_start_end_dates,
)
from hrms.payroll.doctype.salary_component.test_salary_component import create_salary_component
from hrms.payroll.doctype.salary_slip.test_salary_slip import (
create_account,
make_deduction_salary_component,
Expand Down Expand Up @@ -493,6 +501,63 @@ def test_payroll_accrual_journal_entry_without_employee_tagging(self):
self.assertEqual(account.party_type, None)
self.assertEqual(account.party, None)

def test_advance_deduction_in_accrual_journal_entry(self):
company_doc = frappe.get_doc("Company", "_Test Company")
employee = make_employee("[email protected]", company=company_doc.name)

setup_salary_structure(employee, company_doc)

# create employee advance
advance = make_employee_advance(employee, {"repay_unclaimed_amount_from_salary": 1})
journal_entry = make_journal_entry_for_advance(advance)
journal_entry.submit()
advance.reload()

# return advance through additional salary (deduction)
component = create_salary_component("Advance Salary - Deduction", **{"type": "Deduction"})
component.append(
"accounts",
{"company": company_doc.name, "account": "Employee Advances - _TC"},
)
component.save()

additional_salary = create_return_through_additional_salary(advance)
additional_salary.salary_component = component.name
additional_salary.payroll_date = nowdate()
additional_salary.amount = advance.paid_amount
additional_salary.submit()

# payroll entry
dates = get_start_end_dates("Monthly", nowdate())
payroll_entry = make_payroll_entry(
start_date=dates.start_date,
end_date=dates.end_date,
payable_account=company_doc.default_payroll_payable_account,
currency=company_doc.default_currency,
company=company_doc.name,
cost_center="Main - _TC",
)

# check advance deduction entry correctly mapped in accrual entry
deduction_entry = frappe.get_all(
"Journal Entry Account",
fields=["account", "party", "debit", "credit"],
filters={
"reference_type": "Employee Advance",
"reference_name": advance.name,
"is_advance": "Yes",
},
)[0]

expected_entry = {
"account": "Employee Advances - _TC",
"party": employee,
"debit": 0.0,
"credit": advance.paid_amount,
}

self.assertEqual(deduction_entry, expected_entry)

@change_settings("Payroll Settings", {"process_payroll_accounting_entry_based_on_employee": 1})
def test_employee_wise_bank_entry_with_cost_centers(self):
department = create_department("Cost Center Test")
Expand Down
20 changes: 11 additions & 9 deletions hrms/payroll/doctype/salary_component/test_salary_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ class TestSalaryComponent(FrappeTestCase):


def create_salary_component(component_name, **args):
if not frappe.db.exists("Salary Component", component_name):
frappe.get_doc(
{
"doctype": "Salary Component",
"salary_component": component_name,
"type": args.get("type") or "Earning",
"is_tax_applicable": args.get("is_tax_applicable") or 1,
}
).insert()
if frappe.db.exists("Salary Component", component_name):
return frappe.get_doc("Salary Component", component_name)

return frappe.get_doc(
{
"doctype": "Salary Component",
"salary_component": component_name,
"type": args.get("type") or "Earning",
"is_tax_applicable": args.get("is_tax_applicable") or 1,
}
).insert()

0 comments on commit db0a2eb

Please sign in to comment.