diff --git a/erpnext/hr/doctype/attendance/test_attendance.py b/erpnext/hr/doctype/attendance/test_attendance.py index 677a84100d0..b78f5c06206 100644 --- a/erpnext/hr/doctype/attendance/test_attendance.py +++ b/erpnext/hr/doctype/attendance/test_attendance.py @@ -19,7 +19,7 @@ from erpnext.hr.doctype.attendance.attendance import ( mark_attendance, ) 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") diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 99e001ab27a..f36d0f2da86 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -33,6 +33,7 @@ from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import ( create_assignment_for_multiple_employees, ) 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 ( make_holiday_list, make_leave_application, @@ -1105,23 +1106,6 @@ def allocate_leaves(employee, leave_period, leave_type, new_leaves_allocated, el 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): frappe.delete_doc_if_exists("Leave Type", leave_type, force=1) frappe.get_doc( diff --git a/erpnext/hr/report/employee_leave_balance/test_employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/test_employee_leave_balance.py index 5354abf4f6f..d167d1d86c9 100644 --- a/erpnext/hr/report/employee_leave_balance/test_employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/test_employee_leave_balance.py @@ -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.holiday_list.test_holiday_list import set_holiday_list -from erpnext.hr.doctype.leave_application.test_leave_application import ( - get_first_sunday, - make_allocation_record, -) +from erpnext.hr.doctype.leave_application.test_leave_application import make_allocation_record 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.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 ( make_holiday_list, make_leave_application, diff --git a/erpnext/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py index 2fd74b7983b..9eddbea02e2 100644 --- a/erpnext/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py +++ b/erpnext/hr/report/employee_leave_balance_summary/test_employee_leave_balance_summary.py @@ -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.holiday_list.test_holiday_list import set_holiday_list -from erpnext.hr.doctype.leave_application.test_leave_application import ( - get_first_sunday, - make_allocation_record, -) +from erpnext.hr.doctype.leave_application.test_leave_application import make_allocation_record 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.tests.test_utils import get_first_sunday from erpnext.payroll.doctype.salary_slip.test_salary_slip import ( make_holiday_list, make_leave_application, diff --git a/erpnext/hr/tests/test_utils.py b/erpnext/hr/tests/test_utils.py new file mode 100644 index 00000000000..3d7c1ad3669 --- /dev/null +++ b/erpnext/hr/tests/test_utils.py @@ -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 diff --git a/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py index de8f9b6a7ad..f2b86b3ff37 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py +++ b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py @@ -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.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.payroll.doctype.employee_benefit_application.employee_benefit_application import ( calculate_lwp, diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 8103105cfa5..f616aecedaf 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -632,9 +632,19 @@ class SalarySlip(TransactionBase): continue amount = self.eval_condition_and_formula(struct_row, data) - if ( - amount or (struct_row.amount_based_on_formula and amount is not None) - ) and struct_row.statistical_component == 0: + + if struct_row.statistical_component: + # 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) self.update_component_row( struct_row, amount, component_type, data=data, default_amount=default_amount diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index e33f8cce4c4..6e3b57239d4 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -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.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.tests.test_utils import get_first_sunday from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import ( create_exemption_category, create_payroll_period, @@ -55,18 +56,7 @@ class TestSalarySlip(FrappeTestCase): frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0) - month_start_date = get_first_day(nowdate()) - 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] + first_sunday = get_first_sunday() mark_attendance(emp_id, first_sunday, "Absent", ignore_validate=True) # invalid lwp mark_attendance( @@ -273,19 +263,7 @@ class TestSalarySlip(FrappeTestCase): frappe.db.set_value("Leave Type", "Leave Without Pay", "include_holiday", 0) - month_start_date = get_first_day(nowdate()) - 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] - + first_sunday = get_first_sunday() 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) @@ -338,19 +316,7 @@ class TestSalarySlip(FrappeTestCase): frappe.db.set_value("Employee", emp, {"relieving_date": None, "status": "Active"}) # mark attendance - month_start_date = get_first_day(nowdate()) - 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] - + first_sunday = get_first_sunday() mark_attendance( emp, add_days(first_sunday, 1), "Absent", ignore_validate=True ) # counted as absent @@ -359,8 +325,8 @@ class TestSalarySlip(FrappeTestCase): make_salary_structure_for_timesheet(emp) timesheet = make_timesheet(emp, simulate=True, is_billable=1) salary_slip = make_salary_slip_for_timesheet(timesheet.name) - salary_slip.start_date = month_start_date - salary_slip.end_date = month_end_date + salary_slip.start_date = get_first_day(nowdate()) + salary_slip.end_date = get_last_day(nowdate()) salary_slip.save() salary_slip.submit() 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 - month_start_date = get_first_day(nowdate()) - 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] + first_sunday = get_first_sunday() mark_attendance( 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")] 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): activity_type = frappe.get_doc("Activity Type", "_Test Activity Type") 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( - 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 = [ { @@ -1517,7 +1512,7 @@ def make_holiday_list(list_name=None, from_date=None, to_date=None): 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 = [ { "salary_component": "Basic Salary - Payment Days", @@ -1535,6 +1530,27 @@ def make_salary_structure_for_payment_days_based_component_dependency(): "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"])