Merge pull request #31160 from ruchamahabal/fix-gratuity-status-update
This commit is contained in:
commit
4df76f3956
@ -76,9 +76,8 @@
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"options": "Draft\nUnpaid\nPaid",
|
"options": "Draft\nUnpaid\nPaid\nSubmitted\nCancelled",
|
||||||
"read_only": 1,
|
"read_only": 1
|
||||||
"reqd": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval: !doc.pay_via_salary_slip",
|
"depends_on": "eval: !doc.pay_via_salary_slip",
|
||||||
@ -194,7 +193,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2022-02-02 14:00:45.536152",
|
"modified": "2022-05-27 13:56:14.349183",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Gratuity",
|
"name": "Gratuity",
|
||||||
|
|||||||
12
erpnext/payroll/doctype/gratuity/gratuity_list.js
Normal file
12
erpnext/payroll/doctype/gratuity/gratuity_list.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
frappe.listview_settings["Gratuity"] = {
|
||||||
|
get_indicator: function(doc) {
|
||||||
|
let status_color = {
|
||||||
|
"Draft": "red",
|
||||||
|
"Submitted": "blue",
|
||||||
|
"Cancelled": "red",
|
||||||
|
"Paid": "green",
|
||||||
|
"Unpaid": "orange",
|
||||||
|
};
|
||||||
|
return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -4,57 +4,69 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import add_days, flt, get_datetime, getdate
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
from frappe.utils import add_days, add_months, floor, flt, get_datetime, get_first_day, 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.expense_claim.test_expense_claim import get_payable_account
|
from erpnext.hr.doctype.expense_claim.test_expense_claim import get_payable_account
|
||||||
|
from erpnext.hr.doctype.holiday_list.test_holiday_list import set_holiday_list
|
||||||
from erpnext.payroll.doctype.gratuity.gratuity import get_last_salary_slip
|
from erpnext.payroll.doctype.gratuity.gratuity import get_last_salary_slip
|
||||||
from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
|
from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
|
||||||
make_deduction_salary_component,
|
make_deduction_salary_component,
|
||||||
make_earning_salary_component,
|
make_earning_salary_component,
|
||||||
make_employee_salary_slip,
|
make_employee_salary_slip,
|
||||||
|
make_holiday_list,
|
||||||
)
|
)
|
||||||
|
from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
|
||||||
from erpnext.regional.united_arab_emirates.setup import create_gratuity_rule
|
from erpnext.regional.united_arab_emirates.setup import create_gratuity_rule
|
||||||
|
|
||||||
test_dependencies = ["Salary Component", "Salary Slip", "Account"]
|
test_dependencies = ["Salary Component", "Salary Slip", "Account"]
|
||||||
|
|
||||||
|
|
||||||
class TestGratuity(unittest.TestCase):
|
class TestGratuity(FrappeTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
frappe.db.delete("Gratuity")
|
frappe.db.delete("Gratuity")
|
||||||
|
frappe.db.delete("Salary Slip")
|
||||||
frappe.db.delete("Additional Salary", {"ref_doctype": "Gratuity"})
|
frappe.db.delete("Additional Salary", {"ref_doctype": "Gratuity"})
|
||||||
|
|
||||||
make_earning_salary_component(
|
make_earning_salary_component(
|
||||||
setup=True, test_tax=True, company_list=["_Test Company"], include_flexi_benefits=True
|
setup=True, test_tax=True, company_list=["_Test Company"], include_flexi_benefits=True
|
||||||
)
|
)
|
||||||
make_deduction_salary_component(setup=True, test_tax=True, company_list=["_Test Company"])
|
make_deduction_salary_component(setup=True, test_tax=True, company_list=["_Test Company"])
|
||||||
|
make_holiday_list()
|
||||||
|
|
||||||
|
@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
|
||||||
def test_get_last_salary_slip_should_return_none_for_new_employee(self):
|
def test_get_last_salary_slip_should_return_none_for_new_employee(self):
|
||||||
new_employee = make_employee("new_employee@salary.com", company="_Test Company")
|
new_employee = make_employee("new_employee@salary.com", company="_Test Company")
|
||||||
salary_slip = get_last_salary_slip(new_employee)
|
salary_slip = get_last_salary_slip(new_employee)
|
||||||
assert salary_slip is None
|
self.assertIsNone(salary_slip)
|
||||||
|
|
||||||
def test_check_gratuity_amount_based_on_current_slab_and_additional_salary_creation(self):
|
@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
|
||||||
employee, sal_slip = create_employee_and_get_last_salary_slip()
|
def test_gratuity_based_on_current_slab_via_additional_salary(self):
|
||||||
|
"""
|
||||||
|
Range | Fraction
|
||||||
|
5-0 | 1
|
||||||
|
"""
|
||||||
|
doj = add_days(getdate(), -(6 * 365))
|
||||||
|
relieving_date = getdate()
|
||||||
|
|
||||||
|
employee = make_employee(
|
||||||
|
"test_employee_gratuity@salary.com",
|
||||||
|
company="_Test Company",
|
||||||
|
date_of_joining=doj,
|
||||||
|
relieving_date=relieving_date,
|
||||||
|
)
|
||||||
|
sal_slip = create_salary_slip("test_employee_gratuity@salary.com")
|
||||||
|
|
||||||
rule = get_gratuity_rule("Rule Under Unlimited Contract on termination (UAE)")
|
rule = get_gratuity_rule("Rule Under Unlimited Contract on termination (UAE)")
|
||||||
gratuity = create_gratuity(pay_via_salary_slip=1, employee=employee, rule=rule.name)
|
gratuity = create_gratuity(pay_via_salary_slip=1, employee=employee, rule=rule.name)
|
||||||
|
|
||||||
# work experience calculation
|
# work experience calculation
|
||||||
date_of_joining, relieving_date = frappe.db.get_value(
|
employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(doj)).days
|
||||||
"Employee", employee, ["date_of_joining", "relieving_date"]
|
experience = floor(employee_total_workings_days / rule.total_working_days_per_year)
|
||||||
)
|
self.assertEqual(gratuity.current_work_experience, experience)
|
||||||
employee_total_workings_days = (
|
|
||||||
get_datetime(relieving_date) - get_datetime(date_of_joining)
|
|
||||||
).days
|
|
||||||
|
|
||||||
experience = employee_total_workings_days / rule.total_working_days_per_year
|
# amount calculation
|
||||||
gratuity.reload()
|
|
||||||
from math import floor
|
|
||||||
|
|
||||||
self.assertEqual(floor(experience), gratuity.current_work_experience)
|
|
||||||
|
|
||||||
# amount Calculation
|
|
||||||
component_amount = frappe.get_all(
|
component_amount = frappe.get_all(
|
||||||
"Salary Detail",
|
"Salary Detail",
|
||||||
filters={
|
filters={
|
||||||
@ -64,20 +76,44 @@ class TestGratuity(unittest.TestCase):
|
|||||||
"salary_component": "Basic Salary",
|
"salary_component": "Basic Salary",
|
||||||
},
|
},
|
||||||
fields=["amount"],
|
fields=["amount"],
|
||||||
|
limit=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
""" 5 - 0 fraction is 1 """
|
|
||||||
|
|
||||||
gratuity_amount = component_amount[0].amount * experience
|
gratuity_amount = component_amount[0].amount * experience
|
||||||
gratuity.reload()
|
|
||||||
|
|
||||||
self.assertEqual(flt(gratuity_amount, 2), flt(gratuity.amount, 2))
|
self.assertEqual(flt(gratuity_amount, 2), flt(gratuity.amount, 2))
|
||||||
|
|
||||||
# additional salary creation (Pay via salary slip)
|
# additional salary creation (Pay via salary slip)
|
||||||
self.assertTrue(frappe.db.exists("Additional Salary", {"ref_docname": gratuity.name}))
|
self.assertTrue(frappe.db.exists("Additional Salary", {"ref_docname": gratuity.name}))
|
||||||
|
|
||||||
def test_check_gratuity_amount_based_on_all_previous_slabs(self):
|
# gratuity should be marked "Paid" on the next salary slip submission
|
||||||
employee, sal_slip = create_employee_and_get_last_salary_slip()
|
salary_slip = make_salary_slip("Test Gratuity", employee=employee)
|
||||||
|
salary_slip.posting_date = getdate()
|
||||||
|
salary_slip.insert()
|
||||||
|
salary_slip.submit()
|
||||||
|
|
||||||
|
gratuity.reload()
|
||||||
|
self.assertEqual(gratuity.status, "Paid")
|
||||||
|
|
||||||
|
@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
|
||||||
|
def test_gratuity_based_on_all_previous_slabs_via_payment_entry(self):
|
||||||
|
"""
|
||||||
|
Range | Fraction
|
||||||
|
0-1 | 0
|
||||||
|
1-5 | 0.7
|
||||||
|
5-0 | 1
|
||||||
|
"""
|
||||||
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||||
|
|
||||||
|
doj = add_days(getdate(), -(6 * 365))
|
||||||
|
relieving_date = getdate()
|
||||||
|
|
||||||
|
employee = make_employee(
|
||||||
|
"test_employee_gratuity@salary.com",
|
||||||
|
company="_Test Company",
|
||||||
|
date_of_joining=doj,
|
||||||
|
relieving_date=relieving_date,
|
||||||
|
)
|
||||||
|
|
||||||
|
sal_slip = create_salary_slip("test_employee_gratuity@salary.com")
|
||||||
rule = get_gratuity_rule("Rule Under Limited Contract (UAE)")
|
rule = get_gratuity_rule("Rule Under Limited Contract (UAE)")
|
||||||
set_mode_of_payment_account()
|
set_mode_of_payment_account()
|
||||||
|
|
||||||
@ -86,22 +122,11 @@ class TestGratuity(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# work experience calculation
|
# work experience calculation
|
||||||
date_of_joining, relieving_date = frappe.db.get_value(
|
employee_total_workings_days = (get_datetime(relieving_date) - get_datetime(doj)).days
|
||||||
"Employee", employee, ["date_of_joining", "relieving_date"]
|
experience = floor(employee_total_workings_days / rule.total_working_days_per_year)
|
||||||
)
|
self.assertEqual(gratuity.current_work_experience, experience)
|
||||||
employee_total_workings_days = (
|
|
||||||
get_datetime(relieving_date) - get_datetime(date_of_joining)
|
|
||||||
).days
|
|
||||||
|
|
||||||
experience = employee_total_workings_days / rule.total_working_days_per_year
|
# amount calculation
|
||||||
|
|
||||||
gratuity.reload()
|
|
||||||
|
|
||||||
from math import floor
|
|
||||||
|
|
||||||
self.assertEqual(floor(experience), gratuity.current_work_experience)
|
|
||||||
|
|
||||||
# amount Calculation
|
|
||||||
component_amount = frappe.get_all(
|
component_amount = frappe.get_all(
|
||||||
"Salary Detail",
|
"Salary Detail",
|
||||||
filters={
|
filters={
|
||||||
@ -111,35 +136,22 @@ class TestGratuity(unittest.TestCase):
|
|||||||
"salary_component": "Basic Salary",
|
"salary_component": "Basic Salary",
|
||||||
},
|
},
|
||||||
fields=["amount"],
|
fields=["amount"],
|
||||||
|
limit=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
""" range | Fraction
|
|
||||||
0-1 | 0
|
|
||||||
1-5 | 0.7
|
|
||||||
5-0 | 1
|
|
||||||
"""
|
|
||||||
|
|
||||||
gratuity_amount = ((0 * 1) + (4 * 0.7) + (1 * 1)) * component_amount[0].amount
|
gratuity_amount = ((0 * 1) + (4 * 0.7) + (1 * 1)) * component_amount[0].amount
|
||||||
gratuity.reload()
|
|
||||||
|
|
||||||
self.assertEqual(flt(gratuity_amount, 2), flt(gratuity.amount, 2))
|
self.assertEqual(flt(gratuity_amount, 2), flt(gratuity.amount, 2))
|
||||||
self.assertEqual(gratuity.status, "Unpaid")
|
self.assertEqual(gratuity.status, "Unpaid")
|
||||||
|
|
||||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
pe = get_payment_entry("Gratuity", gratuity.name)
|
||||||
|
pe.reference_no = "123467"
|
||||||
|
pe.reference_date = getdate()
|
||||||
|
pe.submit()
|
||||||
|
|
||||||
pay_entry = get_payment_entry("Gratuity", gratuity.name)
|
|
||||||
pay_entry.reference_no = "123467"
|
|
||||||
pay_entry.reference_date = getdate()
|
|
||||||
pay_entry.save()
|
|
||||||
pay_entry.submit()
|
|
||||||
gratuity.reload()
|
gratuity.reload()
|
||||||
|
|
||||||
self.assertEqual(gratuity.status, "Paid")
|
self.assertEqual(gratuity.status, "Paid")
|
||||||
self.assertEqual(flt(gratuity.paid_amount, 2), flt(gratuity.amount, 2))
|
self.assertEqual(flt(gratuity.paid_amount, 2), flt(gratuity.amount, 2))
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
frappe.db.rollback()
|
|
||||||
|
|
||||||
|
|
||||||
def get_gratuity_rule(name):
|
def get_gratuity_rule(name):
|
||||||
rule = frappe.db.exists("Gratuity Rule", name)
|
rule = frappe.db.exists("Gratuity Rule", name)
|
||||||
@ -149,7 +161,6 @@ def get_gratuity_rule(name):
|
|||||||
rule.applicable_earnings_component = []
|
rule.applicable_earnings_component = []
|
||||||
rule.append("applicable_earnings_component", {"salary_component": "Basic Salary"})
|
rule.append("applicable_earnings_component", {"salary_component": "Basic Salary"})
|
||||||
rule.save()
|
rule.save()
|
||||||
rule.reload()
|
|
||||||
|
|
||||||
return rule
|
return rule
|
||||||
|
|
||||||
@ -204,23 +215,17 @@ def create_account():
|
|||||||
).insert(ignore_permissions=True)
|
).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
def create_employee_and_get_last_salary_slip():
|
def create_salary_slip(employee):
|
||||||
employee = make_employee("test_employee@salary.com", company="_Test Company")
|
|
||||||
frappe.db.set_value("Employee", employee, "relieving_date", getdate())
|
|
||||||
frappe.db.set_value("Employee", employee, "date_of_joining", add_days(getdate(), -(6 * 365)))
|
|
||||||
if not frappe.db.exists("Salary Slip", {"employee": employee}):
|
if not frappe.db.exists("Salary Slip", {"employee": employee}):
|
||||||
salary_slip = make_employee_salary_slip("test_employee@salary.com", "Monthly")
|
posting_date = get_first_day(add_months(getdate(), -1))
|
||||||
|
salary_slip = make_employee_salary_slip(
|
||||||
|
employee, "Monthly", "Test Gratuity", posting_date=posting_date
|
||||||
|
)
|
||||||
|
salary_slip.start_date = posting_date
|
||||||
|
salary_slip.end_date = None
|
||||||
salary_slip.submit()
|
salary_slip.submit()
|
||||||
salary_slip = salary_slip.name
|
salary_slip = salary_slip.name
|
||||||
else:
|
else:
|
||||||
salary_slip = get_last_salary_slip(employee)
|
salary_slip = get_last_salary_slip(employee)
|
||||||
|
|
||||||
if not frappe.db.get_value("Employee", "test_employee@salary.com", "holiday_list"):
|
return salary_slip
|
||||||
from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
|
|
||||||
|
|
||||||
make_holiday_list()
|
|
||||||
frappe.db.set_value(
|
|
||||||
"Company", "_Test Company", "default_holiday_list", "Salary Slip Test Holiday List"
|
|
||||||
)
|
|
||||||
|
|
||||||
return employee, salary_slip
|
|
||||||
|
|||||||
@ -116,10 +116,10 @@ class SalarySlip(TransactionBase):
|
|||||||
self.update_payment_status_for_gratuity()
|
self.update_payment_status_for_gratuity()
|
||||||
|
|
||||||
def update_payment_status_for_gratuity(self):
|
def update_payment_status_for_gratuity(self):
|
||||||
add_salary = frappe.db.get_all(
|
additional_salary = frappe.db.get_all(
|
||||||
"Additional Salary",
|
"Additional Salary",
|
||||||
filters={
|
filters={
|
||||||
"payroll_date": ("BETWEEN", [self.start_date, self.end_date]),
|
"payroll_date": ("between", [self.start_date, self.end_date]),
|
||||||
"employee": self.employee,
|
"employee": self.employee,
|
||||||
"ref_doctype": "Gratuity",
|
"ref_doctype": "Gratuity",
|
||||||
"docstatus": 1,
|
"docstatus": 1,
|
||||||
@ -128,10 +128,10 @@ class SalarySlip(TransactionBase):
|
|||||||
limit=1,
|
limit=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
if len(add_salary):
|
if additional_salary:
|
||||||
status = "Paid" if self.docstatus == 1 else "Unpaid"
|
status = "Paid" if self.docstatus == 1 else "Unpaid"
|
||||||
if add_salary[0].name in [data.additional_salary for data in self.earnings]:
|
if additional_salary[0].name in [entry.additional_salary for entry in self.earnings]:
|
||||||
frappe.db.set_value("Gratuity", add_salary.ref_docname, "status", status)
|
frappe.db.set_value("Gratuity", additional_salary[0].ref_docname, "status", status)
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|||||||
@ -997,7 +997,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
return [no_of_days_in_month[1], no_of_holidays_in_month]
|
return [no_of_days_in_month[1], no_of_holidays_in_month]
|
||||||
|
|
||||||
|
|
||||||
def make_employee_salary_slip(user, payroll_frequency, salary_structure=None):
|
def make_employee_salary_slip(user, payroll_frequency, salary_structure=None, posting_date=None):
|
||||||
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
|
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
|
||||||
|
|
||||||
if not salary_structure:
|
if not salary_structure:
|
||||||
@ -1008,7 +1008,11 @@ def make_employee_salary_slip(user, payroll_frequency, salary_structure=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
salary_structure_doc = make_salary_structure(
|
salary_structure_doc = make_salary_structure(
|
||||||
salary_structure, payroll_frequency, employee=employee.name, company=employee.company
|
salary_structure,
|
||||||
|
payroll_frequency,
|
||||||
|
employee=employee.name,
|
||||||
|
company=employee.company,
|
||||||
|
from_date=posting_date,
|
||||||
)
|
)
|
||||||
salary_slip_name = frappe.db.get_value(
|
salary_slip_name = frappe.db.get_value(
|
||||||
"Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})}
|
"Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})}
|
||||||
@ -1018,7 +1022,7 @@ def make_employee_salary_slip(user, payroll_frequency, salary_structure=None):
|
|||||||
salary_slip = make_salary_slip(salary_structure_doc.name, employee=employee.name)
|
salary_slip = make_salary_slip(salary_structure_doc.name, employee=employee.name)
|
||||||
salary_slip.employee_name = employee.employee_name
|
salary_slip.employee_name = employee.employee_name
|
||||||
salary_slip.payroll_frequency = payroll_frequency
|
salary_slip.payroll_frequency = payroll_frequency
|
||||||
salary_slip.posting_date = nowdate()
|
salary_slip.posting_date = posting_date or nowdate()
|
||||||
salary_slip.insert()
|
salary_slip.insert()
|
||||||
else:
|
else:
|
||||||
salary_slip = frappe.get_doc("Salary Slip", salary_slip_name)
|
salary_slip = frappe.get_doc("Salary Slip", salary_slip_name)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user