Feat: gratuity
This commit is contained in:
parent
aa7ab211be
commit
fc2cb3a85e
@ -813,7 +813,7 @@
|
||||
"idx": 24,
|
||||
"image_field": "image",
|
||||
"links": [],
|
||||
"modified": "2020-10-16 15:02:04.283657",
|
||||
"modified": "2020-12-02 15:58:23.805489",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee",
|
||||
|
0
erpnext/payroll/doctype/gratuity/__init__.py
Normal file
0
erpnext/payroll/doctype/gratuity/__init__.py
Normal file
46
erpnext/payroll/doctype/gratuity/gratuity.js
Normal file
46
erpnext/payroll/doctype/gratuity/gratuity.js
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Gratuity', {
|
||||
refresh: function(frm){
|
||||
if(frm.doc.docstatus === 1 && frm.doc.pay_via_salary_slip === 0 && frm.doc.status === "Unpaid") {
|
||||
frm.add_custom_button(__("Make Payment Entry"), function() {
|
||||
frm.trigger('make_payment_entry');
|
||||
});
|
||||
}
|
||||
},
|
||||
onload: function(frm){
|
||||
frm.set_query('salary_component', function() {
|
||||
return {
|
||||
filters: {
|
||||
type: "Earning"
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
employee: function(frm) {
|
||||
frm.events.calculate_work_experience_and_amount(frm);
|
||||
},
|
||||
gratuity_rule: function(frm){
|
||||
frm.events.calculate_work_experience_and_amount(frm);
|
||||
},
|
||||
calculate_work_experience_and_amount: function(frm) {
|
||||
|
||||
if(frm.doc.employee && frm.doc.gratuity_rule){
|
||||
frappe.call({
|
||||
method:"erpnext.payroll.doctype.gratuity.gratuity.calculate_work_experience_and_amount",
|
||||
args:{
|
||||
employee: frm.doc.employee,
|
||||
gratuity_rule: frm.doc.gratuity_rule
|
||||
}
|
||||
}).then((r) => {
|
||||
frm.set_value("current_work_experience", r.message['current_work_experience']);
|
||||
frm.set_value("amount", r.message['amount']);
|
||||
});
|
||||
}
|
||||
},
|
||||
make_payment_entry: function(frm){
|
||||
console.log("Hello");
|
||||
}
|
||||
|
||||
});
|
192
erpnext/payroll/doctype/gratuity/gratuity.json
Normal file
192
erpnext/payroll/doctype/gratuity/gratuity.json
Normal file
@ -0,0 +1,192 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "HR-GRA-PAY-.#####",
|
||||
"creation": "2020-08-05 20:52:13.024683",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"employee",
|
||||
"employee_name",
|
||||
"department",
|
||||
"designation",
|
||||
"column_break_3",
|
||||
"posting_date",
|
||||
"status",
|
||||
"company",
|
||||
"gratuity_rule",
|
||||
"section_break_5",
|
||||
"pay_via_salary_slip",
|
||||
"payroll_date",
|
||||
"salary_component",
|
||||
"expense_account",
|
||||
"mode_of_payment",
|
||||
"column_break_15",
|
||||
"current_work_experience",
|
||||
"amount",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Employee",
|
||||
"options": "Employee",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "employee.company",
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "pay_via_salary_slip",
|
||||
"fieldtype": "Check",
|
||||
"label": "Pay via Salary Slip"
|
||||
},
|
||||
{
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Posting date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.pay_via_salary_slip == 1",
|
||||
"fieldname": "salary_component",
|
||||
"fieldtype": "Link",
|
||||
"label": "Salary Component",
|
||||
"mandatory_depends_on": "eval: doc.pay_via_salary_slip == 1",
|
||||
"options": "Salary Component"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "current_work_experience",
|
||||
"fieldtype": "Int",
|
||||
"label": "Current Work Experience",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Amount",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "Draft",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Status",
|
||||
"options": "Draft\nUnpaid\nPaid",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.pay_via_salary_slip == 0",
|
||||
"fieldname": "expense_account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Expense Account",
|
||||
"mandatory_depends_on": "eval: doc.pay_via_salary_slip == 0",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.pay_via_salary_slip == 0",
|
||||
"fieldname": "mode_of_payment",
|
||||
"fieldtype": "Link",
|
||||
"label": "Mode of Payment",
|
||||
"mandatory_depends_on": "eval: doc.pay_via_salary_slip == 0",
|
||||
"options": "Mode of Payment"
|
||||
},
|
||||
{
|
||||
"fieldname": "gratuity_rule",
|
||||
"fieldtype": "Link",
|
||||
"label": "Gratuity Rule",
|
||||
"options": "Gratuity Rule",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_5",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Payment Configuration"
|
||||
},
|
||||
{
|
||||
"fetch_from": "employee.employee_name",
|
||||
"fieldname": "employee_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Employee Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fetch_from": "employee.department",
|
||||
"fieldname": "department",
|
||||
"fieldtype": "Link",
|
||||
"label": "Department",
|
||||
"options": "Department",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "employee.designation",
|
||||
"fieldname": "designation",
|
||||
"fieldtype": "Data",
|
||||
"label": "Designation",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"options": "Gratuity",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_15",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.pay_via_salary_slip == 1",
|
||||
"fieldname": "payroll_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Payroll Date",
|
||||
"mandatory_depends_on": "eval: doc.pay_via_salary_slip == 1"
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-06 15:51:16.047698",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Gratuity",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
113
erpnext/payroll/doctype/gratuity/gratuity.py
Normal file
113
erpnext/payroll/doctype/gratuity/gratuity.py
Normal file
@ -0,0 +1,113 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, bold
|
||||
from frappe.model.document import Document
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
class Gratuity(Document):
|
||||
def validate(self):
|
||||
calculate_work_experience_and_amount(self.employee, self.gratuity_rule)
|
||||
|
||||
def on_submit(self):
|
||||
if self.pay_via_salary_slip:
|
||||
additional_salary = frappe.new_doc('Additional Salary')
|
||||
additional_salary.employee = self.employee
|
||||
additional_salary.salary_component = self.salary_component
|
||||
additional_salary.overwrite_salary_structure_amount = 0
|
||||
additional_salary.amount = self.amount
|
||||
additional_salary.payroll_date = self.payroll_date
|
||||
additional_salary.company = self.company
|
||||
additional_salary.ref_doctype = self.doctype
|
||||
additional_salary.ref_docname = self.name
|
||||
additional_salary.submit()
|
||||
self.status = "Paid"
|
||||
else:
|
||||
self.status = "Unpaid"
|
||||
|
||||
@frappe.whitelist()
|
||||
def calculate_work_experience_and_amount(employee, gratuity_rule):
|
||||
current_work_experience = calculate_work_experience(employee, gratuity_rule) or 0
|
||||
gratuity_amount = calculate_gratuity_amount(employee, gratuity_rule, current_work_experience) or 0
|
||||
|
||||
return {'current_work_experience': current_work_experience, "amount": gratuity_amount}
|
||||
|
||||
def calculate_work_experience(employee, gratuity_rule):
|
||||
date_of_joining, relieving_date = frappe.db.get_value('Employee', employee, ['date_of_joining', 'relieving_date'])
|
||||
if not relieving_date:
|
||||
frappe.throw(_("Please set Relieving Date for employee: {0}").format(bold(employee)))
|
||||
|
||||
time_difference = relativedelta(relieving_date, date_of_joining)
|
||||
method = frappe.db.get_value("Gratuity Rule", gratuity_rule, "work_experience_calculation_function")
|
||||
|
||||
current_work_experience = time_difference.years
|
||||
|
||||
if method == "Round off Work Experience":
|
||||
if time_difference.months >= 6 and time_difference.days > 0:
|
||||
current_work_experience += 1
|
||||
|
||||
return current_work_experience
|
||||
|
||||
def calculate_gratuity_amount(employee, gratuity_rule, experience):
|
||||
applicable_earnings_component = frappe.get_all("Gratuity Applicable Component", filters= {'parent': gratuity_rule}, fields=["salary_component"])
|
||||
applicable_earnings_component = [component.salary_component for component in applicable_earnings_component]
|
||||
|
||||
slabs = get_gratuity_rule_slabs(gratuity_rule)
|
||||
|
||||
total_applicable_components_amount = get_total_applicable_component_amount(employee, applicable_earnings_component, gratuity_rule)
|
||||
|
||||
|
||||
fraction_to_be_paid = 0
|
||||
|
||||
for slab in slabs:
|
||||
if experience > slab.get("from", 0) and (slab.to == 0 or experience < slab.to):
|
||||
fraction_to_be_paid = slab.fraction_of_applicable_earnings
|
||||
if fraction_to_be_paid:
|
||||
break
|
||||
|
||||
gratuity_amount = total_applicable_components_amount * experience * fraction_to_be_paid
|
||||
|
||||
return gratuity_amount
|
||||
|
||||
def get_total_applicable_component_amount(employee, applicable_earnings_component, gratuity_rule):
|
||||
calculate_gratuity_amount_based_on = frappe.db.get_value("Gratuity Rule", gratuity_rule, "calculate_gratuity_amount_based_on")
|
||||
if calculate_gratuity_amount_based_on == "Last Month Salary":
|
||||
sal_slip = get_last_salary_slip(employee)
|
||||
|
||||
if not sal_slip:
|
||||
frappe.throw(_("No Salary Slip is found for Employee: {0}").format(bold(employee)))
|
||||
|
||||
component_and_amounts = frappe.get_list("Salary Detail",
|
||||
filters={
|
||||
"docstatus": 1,
|
||||
'parent': sal_slip,
|
||||
"parentfield": "earnings",
|
||||
'salary_component': ('in', applicable_earnings_component)
|
||||
},
|
||||
fields=["amount"])
|
||||
total_applicable_components_amount = 0
|
||||
if not len(component_and_amounts):
|
||||
frappe.throw("No Applicable Component is present in last month salary slip")
|
||||
for data in component_and_amounts:
|
||||
total_applicable_components_amount += data.amount
|
||||
elif calculate_gratuity_amount_based_on == "Actual Salary":
|
||||
pass
|
||||
|
||||
return total_applicable_components_amount
|
||||
|
||||
def get_gratuity_rule_slabs(gratuity_rule):
|
||||
return frappe.get_all("Gratuity Rule Slab", filters= {'parent': gratuity_rule}, fields = ["*"])
|
||||
|
||||
def get_salary_structure(employee):
|
||||
return frappe.get_list("Salary Structure Assignment", filters = {"employee": employee, 'docstatus': 1}, fields=["from_date", "salary_structure"], order_by = "from_date desc")[0].salary_structure
|
||||
|
||||
def get_last_salary_slip(employee):
|
||||
return frappe.get_list("Salary Slip", filters = {"employee": employee, 'docstatus': 1}, order_by = "start_date desc")[0].name
|
||||
|
||||
|
||||
|
||||
|
10
erpnext/payroll/doctype/gratuity/test_gratuity.py
Normal file
10
erpnext/payroll/doctype/gratuity/test_gratuity.py
Normal file
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestGratuity(unittest.TestCase):
|
||||
pass
|
@ -0,0 +1,32 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-08-05 19:00:28.097265",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"salary_component"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "salary_component",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Salary Component ",
|
||||
"options": "Salary Component",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-05 20:17:13.855035",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Gratuity Applicable Component",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class GratuityApplicableComponent(Document):
|
||||
pass
|
0
erpnext/payroll/doctype/gratuity_rule/__init__.py
Normal file
0
erpnext/payroll/doctype/gratuity_rule/__init__.py
Normal file
8
erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js
Normal file
8
erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Gratuity Rule', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
88
erpnext/payroll/doctype/gratuity_rule/gratuity_rule.json
Normal file
88
erpnext/payroll/doctype/gratuity_rule/gratuity_rule.json
Normal file
@ -0,0 +1,88 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "Prompt",
|
||||
"creation": "2020-08-05 19:00:36.103500",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"applicable_earnings_component",
|
||||
"work_experience_calculation_function",
|
||||
"column_break_3",
|
||||
"disable",
|
||||
"calculate_gratuity_amount_based_on",
|
||||
"gratuity_rules_section",
|
||||
"gratuity_rule_slabs"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "disable",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable"
|
||||
},
|
||||
{
|
||||
"fieldname": "calculate_gratuity_amount_based_on",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Calculate Gratuity Amount Based on",
|
||||
"options": "Last Month Salary\nActual Salary",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"description": "Salary components should be part of the Salary Structure.",
|
||||
"fieldname": "applicable_earnings_component",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"label": "Applicable Earnings Component",
|
||||
"options": "Gratuity Applicable Component",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "gratuity_rules_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Gratuity Rules"
|
||||
},
|
||||
{
|
||||
"description": "Leave <b>From</b> and <b>To</b> blank for no upper and lower limit.",
|
||||
"fieldname": "gratuity_rule_slabs",
|
||||
"fieldtype": "Table",
|
||||
"label": "Current Work Experience",
|
||||
"options": "Gratuity Rule Slab",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "Round off Work Experience",
|
||||
"fieldname": "work_experience_calculation_function",
|
||||
"fieldtype": "Select",
|
||||
"label": "Work Experience Calculation method",
|
||||
"options": "Round off Work Experience\nTake Exact Completed Years"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-08-06 12:28:13.757792",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Gratuity Rule",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
10
erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py
Normal file
10
erpnext/payroll/doctype/gratuity_rule/gratuity_rule.py
Normal file
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class GratuityRule(Document):
|
||||
pass
|
10
erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py
Normal file
10
erpnext/payroll/doctype/gratuity_rule/test_gratuity_rule.py
Normal file
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestGratuityRule(unittest.TestCase):
|
||||
pass
|
@ -0,0 +1,45 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-08-05 19:12:49.423500",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"from",
|
||||
"to",
|
||||
"fraction_of_applicable_earnings"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "from",
|
||||
"fieldtype": "Int",
|
||||
"in_list_view": 1,
|
||||
"label": "From(Year)"
|
||||
},
|
||||
{
|
||||
"fieldname": "to",
|
||||
"fieldtype": "Int",
|
||||
"in_list_view": 1,
|
||||
"label": "To(Year)"
|
||||
},
|
||||
{
|
||||
"fieldname": "fraction_of_applicable_earnings",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Fraction of Applicable Earnings ",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-05 20:03:25.955448",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Gratuity Rule Slab",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class GratuityRuleSlab(Document):
|
||||
pass
|
Loading…
x
Reference in New Issue
Block a user