mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-21 05:59:18 +00:00
Merge pull request #32187 from ruchamahabal/stat-comp-pd
fix: calculate amount based on payment days for statistical components too
This commit is contained in:
@@ -19,7 +19,7 @@ from erpnext.hr.doctype.attendance.attendance import (
|
|||||||
mark_attendance,
|
mark_attendance,
|
||||||
)
|
)
|
||||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
from erpnext.hr.doctype.leave_application.test_leave_application import get_first_sunday
|
from erpnext.hr.tests.test_utils import get_first_sunday
|
||||||
|
|
||||||
test_records = frappe.get_test_records("Attendance")
|
test_records = frappe.get_test_records("Attendance")
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import (
|
|||||||
create_assignment_for_multiple_employees,
|
create_assignment_for_multiple_employees,
|
||||||
)
|
)
|
||||||
from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
|
from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
|
||||||
|
from erpnext.hr.tests.test_utils import get_first_sunday
|
||||||
from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
|
from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
|
||||||
make_holiday_list,
|
make_holiday_list,
|
||||||
make_leave_application,
|
make_leave_application,
|
||||||
@@ -1105,23 +1106,6 @@ def allocate_leaves(employee, leave_period, leave_type, new_leaves_allocated, el
|
|||||||
allocate_leave.submit()
|
allocate_leave.submit()
|
||||||
|
|
||||||
|
|
||||||
def get_first_sunday(holiday_list, for_date=None):
|
|
||||||
date = for_date or getdate()
|
|
||||||
month_start_date = get_first_day(date)
|
|
||||||
month_end_date = get_last_day(date)
|
|
||||||
first_sunday = frappe.db.sql(
|
|
||||||
"""
|
|
||||||
select holiday_date from `tabHoliday`
|
|
||||||
where parent = %s
|
|
||||||
and holiday_date between %s and %s
|
|
||||||
order by holiday_date
|
|
||||||
""",
|
|
||||||
(holiday_list, month_start_date, month_end_date),
|
|
||||||
)[0][0]
|
|
||||||
|
|
||||||
return first_sunday
|
|
||||||
|
|
||||||
|
|
||||||
def make_policy_assignment(employee, leave_type, leave_period):
|
def make_policy_assignment(employee, leave_type, leave_period):
|
||||||
frappe.delete_doc_if_exists("Leave Type", leave_type, force=1)
|
frappe.delete_doc_if_exists("Leave Type", leave_type, force=1)
|
||||||
frappe.get_doc(
|
frappe.get_doc(
|
||||||
|
|||||||
@@ -9,13 +9,11 @@ from frappe.utils import add_days, add_months, flt, get_year_ending, get_year_st
|
|||||||
|
|
||||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
|
from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
|
||||||
from erpnext.hr.doctype.leave_application.test_leave_application import (
|
from erpnext.hr.doctype.leave_application.test_leave_application import make_allocation_record
|
||||||
get_first_sunday,
|
|
||||||
make_allocation_record,
|
|
||||||
)
|
|
||||||
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation
|
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation
|
||||||
from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
|
from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
|
||||||
from erpnext.hr.report.employee_leave_balance.employee_leave_balance import execute
|
from erpnext.hr.report.employee_leave_balance.employee_leave_balance import execute
|
||||||
|
from erpnext.hr.tests.test_utils import get_first_sunday
|
||||||
from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
|
from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
|
||||||
make_holiday_list,
|
make_holiday_list,
|
||||||
make_leave_application,
|
make_leave_application,
|
||||||
|
|||||||
@@ -9,12 +9,10 @@ from frappe.utils import add_days, flt, get_year_ending, get_year_start, getdate
|
|||||||
|
|
||||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
|
from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
|
||||||
from erpnext.hr.doctype.leave_application.test_leave_application import (
|
from erpnext.hr.doctype.leave_application.test_leave_application import make_allocation_record
|
||||||
get_first_sunday,
|
|
||||||
make_allocation_record,
|
|
||||||
)
|
|
||||||
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation
|
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation
|
||||||
from erpnext.hr.report.employee_leave_balance_summary.employee_leave_balance_summary import execute
|
from erpnext.hr.report.employee_leave_balance_summary.employee_leave_balance_summary import execute
|
||||||
|
from erpnext.hr.tests.test_utils import get_first_sunday
|
||||||
from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
|
from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
|
||||||
make_holiday_list,
|
make_holiday_list,
|
||||||
make_leave_application,
|
make_leave_application,
|
||||||
|
|||||||
19
erpnext/hr/tests/test_utils.py
Normal file
19
erpnext/hr/tests/test_utils.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe.utils import get_first_day, get_last_day, getdate
|
||||||
|
|
||||||
|
|
||||||
|
def get_first_sunday(holiday_list="Salary Slip Test Holiday List", for_date=None):
|
||||||
|
date = for_date or getdate()
|
||||||
|
month_start_date = get_first_day(date)
|
||||||
|
month_end_date = get_last_day(date)
|
||||||
|
first_sunday = frappe.db.sql(
|
||||||
|
"""
|
||||||
|
select holiday_date from `tabHoliday`
|
||||||
|
where parent = %s
|
||||||
|
and holiday_date between %s and %s
|
||||||
|
order by holiday_date
|
||||||
|
""",
|
||||||
|
(holiday_list, month_start_date, month_end_date),
|
||||||
|
)[0][0]
|
||||||
|
|
||||||
|
return first_sunday
|
||||||
@@ -9,7 +9,7 @@ from frappe.utils import add_days, date_diff, get_year_ending, get_year_start, g
|
|||||||
|
|
||||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
|
from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
|
||||||
from erpnext.hr.doctype.leave_application.test_leave_application import get_first_sunday
|
from erpnext.hr.tests.test_utils import get_first_sunday
|
||||||
from erpnext.hr.utils import get_holiday_dates_for_employee
|
from erpnext.hr.utils import get_holiday_dates_for_employee
|
||||||
from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import (
|
from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import (
|
||||||
calculate_lwp,
|
calculate_lwp,
|
||||||
|
|||||||
@@ -632,9 +632,19 @@ class SalarySlip(TransactionBase):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
amount = self.eval_condition_and_formula(struct_row, data)
|
amount = self.eval_condition_and_formula(struct_row, data)
|
||||||
if (
|
|
||||||
amount or (struct_row.amount_based_on_formula and amount is not None)
|
if struct_row.statistical_component:
|
||||||
) and struct_row.statistical_component == 0:
|
# update statitical component amount in reference data based on payment days
|
||||||
|
# since row for statistical component is not added to salary slip
|
||||||
|
if struct_row.depends_on_payment_days:
|
||||||
|
joining_date, relieving_date = self.get_joining_and_relieving_dates()
|
||||||
|
default_data[struct_row.abbr] = amount
|
||||||
|
data[struct_row.abbr] = flt(
|
||||||
|
(flt(amount) * flt(self.payment_days) / cint(self.total_working_days)),
|
||||||
|
struct_row.precision("amount"),
|
||||||
|
)
|
||||||
|
|
||||||
|
elif amount or struct_row.amount_based_on_formula and amount is not None:
|
||||||
default_amount = self.eval_condition_and_formula(struct_row, default_data)
|
default_amount = self.eval_condition_and_formula(struct_row, default_data)
|
||||||
self.update_component_row(
|
self.update_component_row(
|
||||||
struct_row, amount, component_type, data=data, default_amount=default_amount
|
struct_row, amount, component_type, data=data, default_amount=default_amount
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ from erpnext.hr.doctype.attendance.attendance import mark_attendance
|
|||||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
|
from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
|
||||||
from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
|
from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
|
||||||
|
from erpnext.hr.tests.test_utils import get_first_sunday
|
||||||
from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import (
|
from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import (
|
||||||
create_exemption_category,
|
create_exemption_category,
|
||||||
create_payroll_period,
|
create_payroll_period,
|
||||||
@@ -55,18 +56,7 @@ class TestSalarySlip(FrappeTestCase):
|
|||||||
|
|
||||||
frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0)
|
frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0)
|
||||||
|
|
||||||
month_start_date = get_first_day(nowdate())
|
first_sunday = get_first_sunday()
|
||||||
month_end_date = get_last_day(nowdate())
|
|
||||||
|
|
||||||
first_sunday = frappe.db.sql(
|
|
||||||
"""
|
|
||||||
select holiday_date from `tabHoliday`
|
|
||||||
where parent = 'Salary Slip Test Holiday List'
|
|
||||||
and holiday_date between %s and %s
|
|
||||||
order by holiday_date
|
|
||||||
""",
|
|
||||||
(month_start_date, month_end_date),
|
|
||||||
)[0][0]
|
|
||||||
|
|
||||||
mark_attendance(emp_id, first_sunday, "Absent", ignore_validate=True) # invalid lwp
|
mark_attendance(emp_id, first_sunday, "Absent", ignore_validate=True) # invalid lwp
|
||||||
mark_attendance(
|
mark_attendance(
|
||||||
@@ -273,19 +263,7 @@ class TestSalarySlip(FrappeTestCase):
|
|||||||
|
|
||||||
frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0)
|
frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0)
|
||||||
|
|
||||||
month_start_date = get_first_day(nowdate())
|
first_sunday = get_first_sunday()
|
||||||
month_end_date = get_last_day(nowdate())
|
|
||||||
|
|
||||||
first_sunday = frappe.db.sql(
|
|
||||||
"""
|
|
||||||
select holiday_date from `tabHoliday`
|
|
||||||
where parent = 'Salary Slip Test Holiday List'
|
|
||||||
and holiday_date between %s and %s
|
|
||||||
order by holiday_date
|
|
||||||
""",
|
|
||||||
(month_start_date, month_end_date),
|
|
||||||
)[0][0]
|
|
||||||
|
|
||||||
make_leave_application(emp_id, first_sunday, add_days(first_sunday, 3), "Leave Without Pay")
|
make_leave_application(emp_id, first_sunday, add_days(first_sunday, 3), "Leave Without Pay")
|
||||||
|
|
||||||
leave_type_ppl = create_leave_type(leave_type_name="Test Partially Paid Leave", is_ppl=1)
|
leave_type_ppl = create_leave_type(leave_type_name="Test Partially Paid Leave", is_ppl=1)
|
||||||
@@ -338,19 +316,7 @@ class TestSalarySlip(FrappeTestCase):
|
|||||||
frappe.db.set_value("Employee", emp, {"relieving_date": None, "status": "Active"})
|
frappe.db.set_value("Employee", emp, {"relieving_date": None, "status": "Active"})
|
||||||
|
|
||||||
# mark attendance
|
# mark attendance
|
||||||
month_start_date = get_first_day(nowdate())
|
first_sunday = get_first_sunday()
|
||||||
month_end_date = get_last_day(nowdate())
|
|
||||||
|
|
||||||
first_sunday = frappe.db.sql(
|
|
||||||
"""
|
|
||||||
select holiday_date from `tabHoliday`
|
|
||||||
where parent = 'Salary Slip Test Holiday List'
|
|
||||||
and holiday_date between %s and %s
|
|
||||||
order by holiday_date
|
|
||||||
""",
|
|
||||||
(month_start_date, month_end_date),
|
|
||||||
)[0][0]
|
|
||||||
|
|
||||||
mark_attendance(
|
mark_attendance(
|
||||||
emp, add_days(first_sunday, 1), "Absent", ignore_validate=True
|
emp, add_days(first_sunday, 1), "Absent", ignore_validate=True
|
||||||
) # counted as absent
|
) # counted as absent
|
||||||
@@ -359,8 +325,8 @@ class TestSalarySlip(FrappeTestCase):
|
|||||||
make_salary_structure_for_timesheet(emp)
|
make_salary_structure_for_timesheet(emp)
|
||||||
timesheet = make_timesheet(emp, simulate=True, is_billable=1)
|
timesheet = make_timesheet(emp, simulate=True, is_billable=1)
|
||||||
salary_slip = make_salary_slip_for_timesheet(timesheet.name)
|
salary_slip = make_salary_slip_for_timesheet(timesheet.name)
|
||||||
salary_slip.start_date = month_start_date
|
salary_slip.start_date = get_first_day(nowdate())
|
||||||
salary_slip.end_date = month_end_date
|
salary_slip.end_date = get_last_day(nowdate())
|
||||||
salary_slip.save()
|
salary_slip.save()
|
||||||
salary_slip.submit()
|
salary_slip.submit()
|
||||||
salary_slip.reload()
|
salary_slip.reload()
|
||||||
@@ -402,18 +368,7 @@ class TestSalarySlip(FrappeTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# mark employee absent for a day since this case works fine if payment days are equal to working days
|
# mark employee absent for a day since this case works fine if payment days are equal to working days
|
||||||
month_start_date = get_first_day(nowdate())
|
first_sunday = get_first_sunday()
|
||||||
month_end_date = get_last_day(nowdate())
|
|
||||||
|
|
||||||
first_sunday = frappe.db.sql(
|
|
||||||
"""
|
|
||||||
select holiday_date from `tabHoliday`
|
|
||||||
where parent = 'Salary Slip Test Holiday List'
|
|
||||||
and holiday_date between %s and %s
|
|
||||||
order by holiday_date
|
|
||||||
""",
|
|
||||||
(month_start_date, month_end_date),
|
|
||||||
)[0][0]
|
|
||||||
|
|
||||||
mark_attendance(
|
mark_attendance(
|
||||||
employee, add_days(first_sunday, 1), "Absent", ignore_validate=True
|
employee, add_days(first_sunday, 1), "Absent", ignore_validate=True
|
||||||
@@ -1032,6 +987,42 @@ class TestSalarySlip(FrappeTestCase):
|
|||||||
components = [row.salary_component for row in new_ss.get("earnings")]
|
components = [row.salary_component for row in new_ss.get("earnings")]
|
||||||
self.assertNotIn("Statistical Component", components)
|
self.assertNotIn("Statistical Component", components)
|
||||||
|
|
||||||
|
@change_settings(
|
||||||
|
"Payroll Settings",
|
||||||
|
{"payroll_based_on": "Attendance", "consider_unmarked_attendance_as": "Present"},
|
||||||
|
)
|
||||||
|
def test_statistical_component_based_on_payment_days(self):
|
||||||
|
"""
|
||||||
|
Tests whether component using statistical component in the formula
|
||||||
|
gets the updated value based on payment days
|
||||||
|
"""
|
||||||
|
from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
|
||||||
|
create_salary_structure_assignment,
|
||||||
|
)
|
||||||
|
|
||||||
|
emp = make_employee("test_statistical_component@salary.com")
|
||||||
|
first_sunday = get_first_sunday()
|
||||||
|
mark_attendance(emp, add_days(first_sunday, 1), "Absent", ignore_validate=True)
|
||||||
|
salary_structure = make_salary_structure_for_payment_days_based_component_dependency(
|
||||||
|
test_statistical_comp=True
|
||||||
|
)
|
||||||
|
create_salary_structure_assignment(
|
||||||
|
emp, salary_structure.name, company="_Test Company", currency="INR"
|
||||||
|
)
|
||||||
|
# make salary slip and assert payment days
|
||||||
|
ss = make_salary_slip_for_payment_days_dependency_test(
|
||||||
|
"test_statistical_component@salary.com", salary_structure.name
|
||||||
|
)
|
||||||
|
|
||||||
|
amount = precision = None
|
||||||
|
for entry in ss.earnings:
|
||||||
|
if entry.salary_component == "Dependency Component":
|
||||||
|
amount = entry.amount
|
||||||
|
precision = entry.precision("amount")
|
||||||
|
break
|
||||||
|
|
||||||
|
self.assertEqual(amount, flt((1000 * ss.payment_days / ss.total_working_days) * 0.5, precision))
|
||||||
|
|
||||||
def make_activity_for_employee(self):
|
def make_activity_for_employee(self):
|
||||||
activity_type = frappe.get_doc("Activity Type", "_Test Activity Type")
|
activity_type = frappe.get_doc("Activity Type", "_Test Activity Type")
|
||||||
activity_type.billing_rate = 50
|
activity_type.billing_rate = 50
|
||||||
@@ -1151,7 +1142,11 @@ def create_account(account_name, company, parent_account, account_type=None):
|
|||||||
|
|
||||||
|
|
||||||
def make_earning_salary_component(
|
def make_earning_salary_component(
|
||||||
setup=False, test_tax=False, company_list=None, include_flexi_benefits=False
|
setup=False,
|
||||||
|
test_tax=False,
|
||||||
|
company_list=None,
|
||||||
|
include_flexi_benefits=False,
|
||||||
|
test_statistical_comp=False,
|
||||||
):
|
):
|
||||||
data = [
|
data = [
|
||||||
{
|
{
|
||||||
@@ -1517,7 +1512,7 @@ def make_holiday_list(list_name=None, from_date=None, to_date=None):
|
|||||||
return holiday_list
|
return holiday_list
|
||||||
|
|
||||||
|
|
||||||
def make_salary_structure_for_payment_days_based_component_dependency():
|
def make_salary_structure_for_payment_days_based_component_dependency(test_statistical_comp=False):
|
||||||
earnings = [
|
earnings = [
|
||||||
{
|
{
|
||||||
"salary_component": "Basic Salary - Payment Days",
|
"salary_component": "Basic Salary - Payment Days",
|
||||||
@@ -1535,6 +1530,27 @@ def make_salary_structure_for_payment_days_based_component_dependency():
|
|||||||
"formula": "base * 0.20",
|
"formula": "base * 0.20",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
if test_statistical_comp:
|
||||||
|
earnings.extend(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"salary_component": "Statistical Component",
|
||||||
|
"abbr": "SC",
|
||||||
|
"type": "Earning",
|
||||||
|
"statistical_component": 1,
|
||||||
|
"amount": 1000,
|
||||||
|
"depends_on_payment_days": 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"salary_component": "Dependency Component",
|
||||||
|
"abbr": "DC",
|
||||||
|
"type": "Earning",
|
||||||
|
"amount_based_on_formula": 1,
|
||||||
|
"formula": "SC * 0.5",
|
||||||
|
"depends_on_payment_days": 0,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
make_salary_component(earnings, False, company_list=["_Test Company"])
|
make_salary_component(earnings, False, company_list=["_Test Company"])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user