Merge pull request #24362 from ruchamahabal/year-to-date-in-salary-components

feat(Payroll): compute Year to Date for Salary Slip components
This commit is contained in:
Deepesh Garg 2021-01-19 09:40:47 +05:30 committed by GitHub
commit 6fb66f9428
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 119 additions and 18 deletions

View File

@ -9,6 +9,7 @@
"abbr",
"column_break_3",
"amount",
"year_to_date",
"section_break_5",
"additional_salary",
"statistical_component",
@ -226,11 +227,19 @@
{
"fieldname": "column_break_24",
"fieldtype": "Column Break"
},
{
"description": "Total salary booked against this component for this employee from the beginning of the year (payroll period or fiscal year) up to the current salary slip's end date.",
"fieldname": "year_to_date",
"fieldtype": "Currency",
"label": "Year To Date",
"options": "currency",
"read_only": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-11-25 13:12:41.081106",
"modified": "2021-01-14 13:39:15.847158",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Detail",

View File

@ -138,11 +138,11 @@ frappe.ui.form.on("Salary Slip", {
},
change_grid_labels: function(frm) {
frm.set_currency_labels(["amount", "default_amount", "additional_amount", "tax_on_flexible_benefit",
"tax_on_additional_salary"], frm.doc.currency, "earnings");
let fields = ["amount", "year_to_date", "default_amount", "additional_amount", "tax_on_flexible_benefit",
"tax_on_additional_salary"];
frm.set_currency_labels(["amount", "default_amount", "additional_amount", "tax_on_flexible_benefit",
"tax_on_additional_salary"], frm.doc.currency, "deductions");
frm.set_currency_labels(fields, frm.doc.currency, "earnings");
frm.set_currency_labels(fields, frm.doc.currency, "deductions");
},
refresh: function(frm) {

View File

@ -584,6 +584,7 @@
"fieldtype": "Column Break"
},
{
"description": "Total salary booked for this employee from the beginning of the year (payroll period or fiscal year) up to the current salary slip's end date.",
"fieldname": "year_to_date",
"fieldtype": "Currency",
"label": "Year To Date",
@ -591,6 +592,7 @@
"read_only": 1
},
{
"description": "Total salary booked for this employee from the beginning of the month up to the current salary slip's end date.",
"fieldname": "month_to_date",
"fieldtype": "Currency",
"label": "Month To Date",
@ -616,7 +618,7 @@
"idx": 9,
"is_submittable": 1,
"links": [],
"modified": "2020-12-21 23:43:44.959840",
"modified": "2021-01-14 13:37:38.180920",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Slip",

View File

@ -52,6 +52,7 @@ class SalarySlip(TransactionBase):
self.calculate_net_pay()
self.compute_year_to_date()
self.compute_month_to_date()
self.compute_component_wise_year_to_date()
if frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet"):
max_working_hours = frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet")
@ -1138,16 +1139,7 @@ class SalarySlip(TransactionBase):
def compute_year_to_date(self):
year_to_date = 0
payroll_period = get_payroll_period(self.start_date, self.end_date, self.company)
if payroll_period:
period_start_date = payroll_period.start_date
period_end_date = payroll_period.end_date
else:
# get dates based on fiscal year if no payroll period exists
fiscal_year = get_fiscal_year(date=self.start_date, company=self.company, as_dict=1)
period_start_date = fiscal_year.year_start_date
period_end_date = fiscal_year.year_end_date
period_start_date, period_end_date = self.get_year_to_date_period()
salary_slip_sum = frappe.get_list('Salary Slip',
fields = ['sum(net_pay) as sum'],
@ -1180,6 +1172,47 @@ class SalarySlip(TransactionBase):
month_to_date += self.net_pay
self.month_to_date = month_to_date
def compute_component_wise_year_to_date(self):
period_start_date, period_end_date = self.get_year_to_date_period()
for key in ('earnings', 'deductions'):
for component in self.get(key):
year_to_date = 0
component_sum = frappe.db.sql("""
SELECT sum(detail.amount) as sum
FROM `tabSalary Detail` as detail
INNER JOIN `tabSalary Slip` as salary_slip
ON detail.parent = salary_slip.name
WHERE
salary_slip.employee_name = %(employee_name)s
AND detail.salary_component = %(component)s
AND salary_slip.start_date >= %(period_start_date)s
AND salary_slip.end_date < %(period_end_date)s
AND salary_slip.name != %(docname)s
AND salary_slip.docstatus = 1""",
{'employee_name': self.employee_name, 'component': component.salary_component, 'period_start_date': period_start_date,
'period_end_date': period_end_date, 'docname': self.name}
)
year_to_date = flt(component_sum[0][0]) if component_sum else 0.0
year_to_date += component.amount
component.year_to_date = year_to_date
def get_year_to_date_period(self):
payroll_period = get_payroll_period(self.start_date, self.end_date, self.company)
if payroll_period:
period_start_date = payroll_period.start_date
period_end_date = payroll_period.end_date
else:
# get dates based on fiscal year if no payroll period exists
fiscal_year = get_fiscal_year(date=self.start_date, company=self.company, as_dict=1)
period_start_date = fiscal_year.year_start_date
period_end_date = fiscal_year.year_end_date
return period_start_date, period_end_date
def unlink_ref_doc_from_salary_slip(ref_no):
linked_ss = frappe.db.sql_list("""select name from `tabSalary Slip`
where journal_entry=%s and docstatus < 2""", (ref_no))

View File

@ -321,6 +321,38 @@ class TestSalarySlip(unittest.TestCase):
year_to_date += flt(slip.net_pay)
self.assertEqual(slip.year_to_date, year_to_date)
def test_component_wise_year_to_date_computation(self):
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
applicant = make_employee("test_ytd@salary.com", company="_Test Company")
payroll_period = create_payroll_period(name="_Test Payroll Period 1", company="_Test Company")
create_tax_slab(payroll_period, allow_tax_exemption=True, currency="INR", effective_date=getdate("2019-04-01"),
company="_Test Company")
salary_structure = make_salary_structure("Monthly Salary Structure Test for Salary Slip YTD",
"Monthly", employee=applicant, company="_Test Company", currency="INR", payroll_period=payroll_period)
# clear salary slip for this employee
frappe.db.sql("DELETE FROM `tabSalary Slip` where employee_name = 'test_ytd@salary.com'")
create_salary_slips_for_payroll_period(applicant, salary_structure.name,
payroll_period, deduct_random=False, num=3)
salary_slips = frappe.get_all("Salary Slip", fields=["name"], filters={"employee_name":
"test_ytd@salary.com"}, order_by = "posting_date")
year_to_date = dict()
for slip in salary_slips:
doc = frappe.get_doc("Salary Slip", slip.name)
for entry in doc.get("earnings"):
if not year_to_date.get(entry.salary_component):
year_to_date[entry.salary_component] = 0
year_to_date[entry.salary_component] += entry.amount
self.assertEqual(year_to_date[entry.salary_component], entry.year_to_date)
def test_tax_for_payroll_period(self):
data = {}
# test the impact of tax exemption declaration, tax exemption proof submission
@ -714,10 +746,10 @@ def create_tax_slab(payroll_period, effective_date = None, allow_tax_exemption =
else:
return income_tax_slab_name
def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True):
def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True, num=12):
deducted_dates = []
i = 0
while i < 12:
while i < num:
slip = frappe.get_doc({"doctype": "Salary Slip", "employee": employee,
"salary_structure": salary_structure, "frequency": "Monthly"})
if i == 0:

View File

View File

@ -0,0 +1,25 @@
{
"absolute_value": 0,
"align_labels_right": 0,
"creation": "2021-01-14 09:56:42.393623",
"custom_format": 0,
"default_print_language": "en",
"disabled": 0,
"doc_type": "Salary Slip",
"docstatus": 0,
"doctype": "Print Format",
"font": "Default",
"format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \" <h3 style=\\\"text-align: right;\\\"><span style=\\\"line-height: 1.42857;\\\">{{doc.name}}</span></h3>\\n<div>\\n <hr style=\\\"text-align: center;\\\">\\n</div> \"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"employee\", \"print_hide\": 0, \"label\": \"Employee\"}, {\"fieldname\": \"company\", \"print_hide\": 0, \"label\": \"Company\"}, {\"fieldname\": \"employee_name\", \"print_hide\": 0, \"label\": \"Employee Name\"}, {\"fieldname\": \"department\", \"print_hide\": 0, \"label\": \"Department\"}, {\"fieldname\": \"designation\", \"print_hide\": 0, \"label\": \"Designation\"}, {\"fieldname\": \"branch\", \"print_hide\": 0, \"label\": \"Branch\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"start_date\", \"print_hide\": 0, \"label\": \"Start Date\"}, {\"fieldname\": \"end_date\", \"print_hide\": 0, \"label\": \"End Date\"}, {\"fieldname\": \"total_working_days\", \"print_hide\": 0, \"label\": \"Working Days\"}, {\"fieldname\": \"leave_without_pay\", \"print_hide\": 0, \"label\": \"Leave Without Pay\"}, {\"fieldname\": \"payment_days\", \"print_hide\": 0, \"label\": \"Payment Days\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"earnings\", \"print_hide\": 0, \"label\": \"Earnings\", \"visible_columns\": [{\"fieldname\": \"salary_component\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"amount\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"year_to_date\", \"print_width\": \"\", \"print_hide\": 0}]}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"deductions\", \"print_hide\": 0, \"label\": \"Deductions\", \"visible_columns\": [{\"fieldname\": \"salary_component\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"amount\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"year_to_date\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\", \"print_hide\": 0}]}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"gross_pay\", \"print_hide\": 0, \"label\": \"Gross Pay\"}, {\"fieldname\": \"total_deduction\", \"print_hide\": 0, \"label\": \"Total Deduction\"}, {\"fieldname\": \"net_pay\", \"print_hide\": 0, \"label\": \"Net Pay\"}, {\"fieldname\": \"rounded_total\", \"print_hide\": 0, \"label\": \"Rounded Total\"}, {\"fieldname\": \"total_in_words\", \"print_hide\": 0, \"label\": \"Total in words\"}, {\"fieldtype\": \"Section Break\", \"label\": \"net pay info\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"year_to_date\", \"print_hide\": 0, \"label\": \"Year To Date\"}, {\"fieldname\": \"month_to_date\", \"print_hide\": 0, \"label\": \"Month To Date\"}]",
"idx": 0,
"line_breaks": 0,
"modified": "2021-01-14 10:03:45.283725",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Slip with Year to Date",
"owner": "Administrator",
"print_format_builder": 0,
"print_format_type": "Jinja",
"raw_printing": 0,
"show_section_headings": 0,
"standard": "Yes"
}