feat: Full and Final Settlement and Gratuity Fix (#26364)

* feat: Full and Final Settlement

* removed option to pay via salary slip

* feat: Create JV

* test:fnf

* fix: tracking asset movement

* fix: sider and test

* fix: changes Requested

* fix: changes requested

* fix: valication for Asset

* fix: add filter for reference document only if relevant field is present

* fix: doctype cleanup

- add more fields to the list view and standard filter

- set title field

- incorrect field labels

* feat: add list view settings for FNF

* fix: incorrect reference type set in Journal Entry

* fix: validation message

* chore: add Full and Final Statement link to Workspace

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
This commit is contained in:
Anurag Mishra 2021-08-31 17:59:26 +05:30 committed by GitHub
parent 48f2e5ac1d
commit ab47409e91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 835 additions and 84 deletions

View File

@ -66,6 +66,7 @@ class JournalEntry(AccountsController):
self.update_expense_claim()
self.update_inter_company_jv()
self.update_invoice_discounting()
self.update_status_for_full_and_final_statement()
check_if_stock_and_account_balance_synced(self.posting_date,
self.company, self.doctype, self.name)
@ -83,6 +84,7 @@ class JournalEntry(AccountsController):
self.unlink_inter_company_jv()
self.unlink_asset_adjustment_entry()
self.update_invoice_discounting()
self.update_status_for_full_and_final_statement()
def get_title(self):
return self.pay_to_recd_from or self.accounts[0].account
@ -98,6 +100,15 @@ class JournalEntry(AccountsController):
for voucher_no in list(set(order_list)):
frappe.get_doc(voucher_type, voucher_no).set_total_advance_paid()
def update_status_for_full_and_final_statement(self):
for entry in self.accounts:
if entry.reference_type == "Full and Final Statement":
if self.docstatus == 1:
frappe.db.set_value("Full and Final Statement", entry.reference_name, "status", "Paid")
elif self.docstatus == 2:
frappe.db.set_value("Full and Final Statement", entry.reference_name, "status", "Unpaid")
def validate_inter_company_accounts(self):
if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
doc = frappe.get_doc("Journal Entry", self.inter_company_journal_entry_reference)

View File

@ -202,7 +202,7 @@
"fieldname": "reference_type",
"fieldtype": "Select",
"label": "Reference Type",
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees"
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement"
},
{
"fieldname": "reference_name",
@ -280,7 +280,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2020-06-26 14:06:54.833738",
"modified": "2021-08-30 21:27:32.200299",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",

View File

@ -43,8 +43,6 @@ frappe.ui.form.on("Employee Referral", {
});
}
},
create_job_applicant: function(frm) {
frappe.model.open_mapped_doc({

View File

@ -0,0 +1,8 @@
// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Full and Final Asset', {
// refresh: function(frm) {
// }
});

View File

@ -0,0 +1,64 @@
{
"actions": [],
"creation": "2021-06-28 13:36:58.658985",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"reference",
"asset_name",
"date",
"status",
"description"
],
"fields": [
{
"fieldname": "reference",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Reference",
"options": "Asset Movement",
"read_only": 1,
"reqd": 1
},
{
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Status",
"options": "Owned\nReturned",
"reqd": 1
},
{
"fieldname": "description",
"fieldtype": "Small Text",
"label": "Description"
},
{
"fieldname": "asset_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Asset Name",
"read_only": 1
},
{
"fieldname": "date",
"fieldtype": "Datetime",
"in_list_view": 1,
"label": "Date",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-07-15 15:17:31.309834",
"modified_by": "Administrator",
"module": "HR",
"name": "Full and Final Asset",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -0,0 +1,8 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class FullandFinalAsset(Document):
pass

View File

@ -0,0 +1,8 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# import frappe
import unittest
class TestFullandFinalAsset(unittest.TestCase):
pass

View File

@ -0,0 +1,96 @@
{
"actions": [],
"creation": "2021-06-28 13:32:02.167317",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"component",
"reference_document_type",
"reference_document",
"account",
"paid_via_salary_slip",
"column_break_4",
"amount",
"status",
"remark"
],
"fields": [
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"columns": 2,
"default": "Unsettled",
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Status",
"options": "Settled\nUnsettled"
},
{
"fieldname": "remark",
"fieldtype": "Small Text",
"label": "Remark"
},
{
"columns": 2,
"depends_on": "reference_document_type",
"fieldname": "reference_document",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
"label": "Reference Document",
"mandatory_depends_on": "reference_document_type",
"options": "reference_document_type",
"search_index": 1
},
{
"columns": 2,
"fieldname": "component",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Component",
"reqd": 1
},
{
"fieldname": "account",
"fieldtype": "Link",
"label": "Account",
"options": "Account"
},
{
"columns": 2,
"fieldname": "amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Amount"
},
{
"columns": 2,
"fieldname": "reference_document_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Reference Document Type",
"options": "DocType"
},
{
"default": "0",
"fieldname": "paid_via_salary_slip",
"fieldtype": "Check",
"label": "Paid via Salary Slip"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-07-20 16:59:34.447934",
"modified_by": "Administrator",
"module": "HR",
"name": "Full and Final Outstanding Statement",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@ -0,0 +1,8 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class FullandFinalOutstandingStatement(Document):
pass

View File

@ -0,0 +1,115 @@
// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Full and Final Statement', {
refresh: function(frm) {
frm.events.set_queries(frm, "payables");
frm.events.set_queries(frm, "receivables");
if (frm.doc.docstatus == 1 && frm.doc.status == "Unpaid") {
frm.add_custom_button(__("Create Journal Entry"), function () {
frm.events.create_journal_entry(frm);
});
}
},
set_queries: function(frm, type) {
frm.set_query("reference_document_type", type, function () {
let modules = ["HR", "Payroll", "Loan Management"];
return {
filters: {
istable: 0,
issingle: 0,
module: ["In", modules]
}
};
});
let filters = {};
frm.set_query('reference_document', type, function(doc, cdt, cdn) {
let fnf_doc = frappe.get_doc(cdt, cdn);
frappe.model.with_doctype(fnf_doc.reference_document_type, function() {
if (frappe.model.is_tree(fnf_doc.reference_document_type)) {
filters['is_group'] = 0;
}
if (frappe.meta.has_field(fnf_doc.reference_document_type, 'company')) {
filters['company'] = frm.doc.company;
}
if (frappe.meta.has_field(fnf_doc.reference_document_type, 'employee')) {
filters['employee'] = frm.doc.employee;
}
});
return {
filters: filters
};
});
},
employee: function(frm) {
frm.events.get_outstanding_statements(frm);
},
get_outstanding_statements: function(frm) {
if (frm.doc.employee) {
frappe.call({
method: "get_outstanding_statements",
doc: frm.doc,
callback: function() {
frm.refresh();
}
});
}
},
create_journal_entry: function(frm) {
frappe.call({
method: "create_journal_entry",
doc: frm.doc,
callback: function(r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
});
}
});
frappe.ui.form.on("Full and Final Outstanding Statement", {
reference_document: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
if (child.reference_document_type && child.reference_document) {
frappe.call({
method: "erpnext.hr.doctype.full_and_final_statement.full_and_final_statement.get_account_and_amount",
args: {
ref_doctype: child.reference_document_type,
ref_document: child.reference_document
},
callback: function(r) {
if (r.message) {
frappe.model.set_value(cdt, cdn, "account", r.message[0]);
frappe.model.set_value(cdt, cdn, "amount", r.message[1]);
}
}
});
}
},
amount: function(frm) {
var total_payable_amount = 0;
var total_receivable_amount = 0;
frm.doc.payables.forEach(element => {
total_payable_amount = total_payable_amount + element.amount;
});
frm.doc.receivables.forEach(element => {
total_receivable_amount = total_receivable_amount + element.amount;
});
frm.set_value("total_payable_amount", flt(total_payable_amount));
frm.set_value("total_receivable_amount", flt(total_receivable_amount));
}
});

View File

@ -0,0 +1,231 @@
{
"actions": [],
"autoname": "HR-FNF-.YYYY.-.#####",
"creation": "2021-06-28 13:17:36.050459",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"employee",
"employee_name",
"transaction_date",
"column_break_12",
"company",
"status",
"amended_from",
"employee_details_section",
"date_of_joining",
"relieving_date",
"column_break_4",
"designation",
"department",
"section_break_8",
"payables",
"section_break_10",
"receivables",
"totals_section",
"total_payable_amount",
"column_break_21",
"total_receivable_amount",
"section_break_15",
"assets_allocated"
],
"fields": [
{
"fieldname": "employee",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Employee",
"options": "Employee",
"reqd": 1
},
{
"fetch_from": "employee.employee_name",
"fieldname": "employee_name",
"fieldtype": "Data",
"label": "Employee Name",
"read_only": 1
},
{
"fetch_from": "employee.designation",
"fieldname": "designation",
"fieldtype": "Link",
"label": "Designation",
"options": "Designation",
"read_only": 1
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"default": "Unpaid",
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
"options": "Paid\nUnpaid",
"read_only": 1
},
{
"fetch_from": "employee.department",
"fieldname": "department",
"fieldtype": "Link",
"label": "Department",
"options": "Department",
"read_only": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Full and Final Statement",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "section_break_8",
"fieldtype": "Section Break",
"label": "Payables"
},
{
"fieldname": "section_break_10",
"fieldtype": "Section Break",
"label": "Receivables"
},
{
"fieldname": "assets_allocated",
"fieldtype": "Table",
"options": "Full and Final Asset"
},
{
"fetch_from": "employee.relieving_date",
"fieldname": "relieving_date",
"fieldtype": "Date",
"label": "Relieving Date ",
"read_only": 1,
"reqd": 1
},
{
"fetch_from": "employee.date_of_joining",
"fieldname": "date_of_joining",
"fieldtype": "Date",
"label": "Date of Joining",
"read_only": 1
},
{
"fieldname": "section_break_15",
"fieldtype": "Section Break",
"label": "Assets Allocated"
},
{
"fetch_from": "employee.company",
"fieldname": "company",
"fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Company",
"options": "Company",
"read_only": 1
},
{
"fieldname": "column_break_12",
"fieldtype": "Column Break"
},
{
"fieldname": "payables",
"fieldtype": "Table",
"options": "Full and Final Outstanding Statement"
},
{
"fieldname": "receivables",
"fieldtype": "Table",
"options": "Full and Final Outstanding Statement"
},
{
"fieldname": "employee_details_section",
"fieldtype": "Section Break",
"label": "Employee Details"
},
{
"fieldname": "transaction_date",
"fieldtype": "Date",
"in_standard_filter": 1,
"label": "Transaction Date",
"reqd": 1
},
{
"fieldname": "totals_section",
"fieldtype": "Section Break",
"label": "Totals"
},
{
"fieldname": "total_payable_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Total Payable Amount",
"read_only": 1
},
{
"fieldname": "column_break_21",
"fieldtype": "Column Break"
},
{
"fieldname": "total_receivable_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Total Receivable Amount",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2021-08-30 21:11:09.892560",
"modified_by": "Administrator",
"module": "HR",
"name": "Full and Final Statement",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"share": 1,
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "employee_name",
"track_changes": 1
}

View File

@ -0,0 +1,176 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.utils import get_link_to_form, today, flt
from frappe.model.document import Document
class FullandFinalStatement(Document):
def validate(self):
self.get_outstanding_statements()
if self.docstatus == 1:
self.validate_settlement("payables")
self.validate_settlement("receivables")
self.validate_asset()
def validate_settlement(self, component_type):
for data in self.get(component_type, []):
if data.status == "Unsettled":
frappe.throw(_("Settle all Payables and Receivables before submission"))
def validate_asset(self):
for data in self.assets_allocated:
if data.status == "Owned":
frappe.throw(_("All allocated assets should be returned before submission"))
@frappe.whitelist()
def get_outstanding_statements(self):
if self.relieving_date:
if not len(self.get("payables", [])):
components = self.get_payable_component()
self.create_component_row(components, "payables")
if not len(self.get("receivables", [])):
components = self.get_receivable_component()
self.create_component_row(components, "receivables")
if not len(self.get("assets_allocated", [])):
for data in self.get_assets_movement():
self.append("assets_allocated", data)
else:
frappe.throw(_("Set Relieving Date for Employee: {0}").format(get_link_to_form("Employee", self.employee)))
def create_component_row(self, components, component_type):
for component in components:
self.append(component_type, {
"status": "Unsettled",
"reference_document_type": component if component != "Bonus" else "Additional Salary",
"component": component
})
def get_payable_component(self):
return [
"Salary Slip",
"Gratuity",
"Expense Claim",
"Bonus",
"Leave Encashment",
]
def get_receivable_component(self):
return [
"Loan",
"Employee Advance",
]
def get_assets_movement(self):
asset_movements = frappe.get_all("Asset Movement Item",
filters = {"docstatus": 1},
fields = ["asset", "from_employee", "to_employee", "parent", "asset_name"],
or_filters = {
"from_employee": self.employee,
"to_employee": self.employee
}
)
data = []
inward_movements = []
outward_movements = []
for movement in asset_movements:
if movement.to_employee and movement.to_employee == self.employee:
inward_movements.append(movement)
if movement.from_employee and movement.from_employee == self.employee:
outward_movements.append(movement)
for movement in inward_movements:
outwards_count = [movement.asset for movement in outward_movements].count(movement.asset)
inwards_counts = [movement.asset for movement in inward_movements].count(movement.asset)
if inwards_counts > outwards_count:
data.append({
"reference": movement.parent,
"asset_name": movement.asset_name,
"date": frappe.db.get_value("Asset Movement", movement.parent, "transaction_date"),
"status": "Owned"
})
return data
@frappe.whitelist()
def create_journal_entry(self):
precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
jv = frappe.new_doc("Journal Entry")
jv.company = self.company
jv.voucher_type = "Bank Entry"
jv.posting_date = today()
difference = self.total_payable_amount - self.total_receivable_amount
for data in self.payables:
if data.amount > 0 and not data.paid_via_salary_slip:
account_dict = {
"account": data.account,
"debit_in_account_currency": flt(data.amount, precision)
}
if data.reference_document_type == "Expense Claim":
account_dict["party_type"] = "Employee"
account_dict["party"] = self.employee
jv.append("accounts", account_dict)
for data in self.receivables:
if data.amount > 0:
account_dict = {
"account": data.account,
"credit_in_account_currency": flt(data.amount, precision)
}
if data.reference_document_type == "Employee Advance":
account_dict["party_type"] = "Employee"
account_dict["party"] = self.employee
jv.append("accounts", account_dict)
jv.append("accounts", {
"credit_in_account_currency": difference if difference > 0 else 0,
"debit_in_account_currency": -(difference) if difference < 0 else 0,
"reference_type": self.doctype,
"reference_name": self.name
})
return jv
@frappe.whitelist()
def get_account_and_amount(ref_doctype, ref_document):
if not ref_doctype or not ref_document:
return None
if ref_doctype == "Salary Slip":
salary_details = frappe.db.get_value("Salary Slip", ref_document, ["payroll_entry", "net_pay"], as_dict=1)
amount = salary_details.net_pay
payable_account = frappe.db.get_value("Payroll Entry", salary_details.payroll_entry, "payroll_payable_account") if salary_details.payroll_entry else None
return [payable_account, amount]
if ref_doctype == "Gratuity":
payable_account, amount = frappe.db.get_value("Gratuity", ref_document, ["payable_account", "amount"])
return [payable_account, amount]
if ref_doctype == "Expense Claim":
details = frappe.db.get_value("Expense Claim", ref_document,
["payable_account", "grand_total", "total_amount_reimbursed", "total_advance_amount"], as_dict=True)
payable_account = details.payable_account
amount = details.grand_total - (details.total_amount_reimbursed + details.total_advance_amount)
return [payable_account, amount]
if ref_doctype == "Loan":
details = frappe.db.get_value("Loan", ref_document,
["payment_account", "total_payment", "total_amount_paid"], as_dict=1)
payment_account = details.payment_account
amount = details.total_payment - details.total_amount_paid
return [payment_account, amount]
if ref_doctype == "Employee Advance":
details = frappe.db.get_value("Employee Advance", ref_document,
["advance_account","paid_amount", "claimed_amount", "return_amount"], as_dict = 1)
payment_account = details.advance_account
amount = details.paid_amount - (details.claimed_amount + details.return_amount)
return [payment_account, amount]

View File

@ -0,0 +1,11 @@
frappe.listview_settings["Full and Final Statement"] = {
get_indicator: function(doc) {
var colors = {
"Draft": "red",
"Unpaid": "orange",
"Paid": "green",
"Cancelled": "red"
};
return [__(doc.status), colors[doc.status], "status,=," + doc.status];
}
};

View File

@ -0,0 +1,71 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import frappe
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.assets.doctype.asset.test_asset import create_asset_data
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from frappe.utils import today, add_days
import unittest
class TestFullandFinalStatement(unittest.TestCase):
def setUp(self):
create_asset_data()
def tearDown(self):
frappe.db.sql("Delete from `tabFull and Final Statement`")
frappe.db.sql("Delete from `tabAsset`")
frappe.db.sql("Delete from `tabAsset Movement`")
def test_check_bootstraped_data_asset_movement_and_jv_creation(self):
employee = make_employee("test_fnf@example.com", company="_Test Company")
movement = create_asset_movement(employee)
frappe.db.set_value("Employee", employee, "relieving_date", add_days(today(), 30))
fnf = create_full_and_final_statement(employee)
payables_bootstraped_component = ["Salary Slip", "Gratuity",
"Expense Claim", "Bonus", "Leave Encashment"]
receivable_bootstraped_component = ["Loan", "Employee Advance"]
#checking payable s and receivables bootstraped value
self.assertEqual([payable.component for payable in fnf.payables], payables_bootstraped_component)
self.assertEqual([receivable.component for receivable in fnf.receivables], receivable_bootstraped_component)
#checking allocated asset
self.assertIn(movement, [asset.reference for asset in fnf.assets_allocated])
def create_full_and_final_statement(employee):
fnf = frappe.new_doc("Full and Final Statement")
fnf.employee = employee
fnf.transaction_date = today()
fnf.save()
return fnf
def create_asset_movement(employee):
asset_name = create_asset()
movement = frappe.new_doc("Asset Movement")
movement.company = "_Test Company"
movement.purpose = "Issue"
movement.transaction_date = today()
movement.append("assets", {
"asset": asset_name,
"to_employee": employee
})
movement.save()
movement.submit()
return movement.name
def create_asset():
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=100000.0, location="Test Location")
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
asset = frappe.get_doc("Asset", asset_name)
asset.calculate_depreciation = 0
asset.available_for_use_date = today()
asset.submit()
return asset_name

View File

@ -223,6 +223,17 @@
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Employee",
"hidden": 0,
"is_query_report": 0,
"label": "Full and Final Statement",
"link_count": 0,
"link_to": "Full and Final Statement",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
@ -931,7 +942,7 @@
"type": "Link"
}
],
"modified": "2021-08-05 12:15:59.842918",
"modified": "2021-08-31 12:18:59.842918",
"modified_by": "Administrator",
"module": "HR",
"name": "HR",

View File

@ -3,13 +3,6 @@
frappe.ui.form.on('Gratuity', {
setup: function (frm) {
frm.set_query('salary_component', function () {
return {
filters: {
type: "Earning"
}
};
});
frm.set_query("expense_account", function () {
return {
filters: {
@ -31,7 +24,7 @@ frappe.ui.form.on('Gratuity', {
});
},
refresh: function (frm) {
if (frm.doc.docstatus === 1 && frm.doc.pay_via_salary_slip === 0 && frm.doc.status === "Unpaid") {
if (frm.doc.docstatus == 1 && frm.doc.status == "Unpaid") {
frm.add_custom_button(__("Create Payment Entry"), function () {
return frappe.call({
method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry',

View File

@ -16,9 +16,6 @@
"company",
"gratuity_rule",
"section_break_5",
"pay_via_salary_slip",
"payroll_date",
"salary_component",
"payable_account",
"expense_account",
"mode_of_payment",
@ -49,26 +46,12 @@
"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",
@ -95,20 +78,18 @@
"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"
"options": "Account",
"reqd": 1
},
{
"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"
"options": "Mode of Payment",
"reqd": 1
},
{
"fieldname": "gratuity_rule",
@ -161,13 +142,6 @@
"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"
},
{
"default": "0",
"depends_on": "eval:doc.pay_via_salary_slip == 0",
@ -177,26 +151,23 @@
"read_only": 1
},
{
"depends_on": "eval: doc.pay_via_salary_slip == 0",
"fieldname": "payable_account",
"fieldtype": "Link",
"label": "Payable Account",
"mandatory_depends_on": "eval: doc.pay_via_salary_slip == 0",
"options": "Account"
"options": "Account",
"reqd": 1
},
{
"depends_on": "eval: doc.pay_via_salary_slip == 0",
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"mandatory_depends_on": "eval: doc.pay_via_salary_slip == 0",
"options": "Cost Center"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-11-02 18:21:11.971488",
"modified": "2021-07-02 15:05:57.396398",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Gratuity",

View File

@ -19,10 +19,7 @@ class Gratuity(AccountsController):
self.status = "Unpaid"
def on_submit(self):
if self.pay_via_salary_slip:
self.create_additional_salary()
else:
self.create_gl_entries()
self.create_gl_entries()
def on_cancel(self):
self.ignore_linked_doctypes = ['GL Entry']
@ -65,19 +62,6 @@ class Gratuity(AccountsController):
return gl_entry
def create_additional_salary(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()
def set_total_advance_paid(self):
paid_amount = frappe.db.sql("""
select ifnull(sum(debit_in_account_currency), 0) as paid_amount

View File

@ -11,10 +11,6 @@ def get_data():
{
'label': _('Payment'),
'items': ['Payment Entry']
},
{
'label': _('Additional Salary'),
'items': ['Additional Salary']
}
]
}

View File

@ -22,19 +22,18 @@ class TestGratuity(unittest.TestCase):
def setUp(self):
frappe.db.sql("DELETE FROM `tabGratuity`")
frappe.db.sql("DELETE FROM `tabAdditional Salary` WHERE ref_doctype = 'Gratuity'")
def test_get_last_salary_slip_should_return_none_for_new_employee(self):
new_employee = make_employee("new_employee@salary.com", company='_Test Company')
salary_slip = get_last_salary_slip(new_employee)
assert salary_slip is None
def test_check_gratuity_amount_based_on_current_slab_and_additional_salary_creation(self):
def test_check_gratuity_amount_based_on_current_slab(self):
employee, sal_slip = create_employee_and_get_last_salary_slip()
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(employee=employee, rule=rule.name)
#work experience calculation
date_of_joining, relieving_date = frappe.db.get_value('Employee', employee, ['date_of_joining', 'relieving_date'])
@ -62,9 +61,6 @@ class TestGratuity(unittest.TestCase):
self.assertEqual(flt(gratuity_amount, 2), flt(gratuity.amount, 2))
#additional salary creation (Pay via salary slip)
self.assertTrue(frappe.db.exists("Additional Salary", {"ref_docname": gratuity.name}))
def test_check_gratuity_amount_based_on_all_previous_slabs(self):
employee, sal_slip = create_employee_and_get_last_salary_slip()
rule = get_gratuity_rule("Rule Under Limited Contract (UAE)")
@ -142,14 +138,9 @@ def create_gratuity(**args):
gratuity.employee = args.employee
gratuity.posting_date = getdate()
gratuity.gratuity_rule = args.rule or "Rule Under Limited Contract (UAE)"
gratuity.pay_via_salary_slip = args.pay_via_salary_slip or 0
if gratuity.pay_via_salary_slip:
gratuity.payroll_date = getdate()
gratuity.salary_component = "Performance Bonus"
else:
gratuity.expense_account = args.expense_account or 'Payment Account - _TC'
gratuity.payable_account = args.payable_account or get_payable_account("_Test Company")
gratuity.mode_of_payment = args.mode_of_payment or 'Cash'
gratuity.expense_account = args.expense_account or 'Payment Account - _TC'
gratuity.payable_account = args.payable_account or get_payable_account("_Test Company")
gratuity.mode_of_payment = args.mode_of_payment or 'Cash'
gratuity.save()
gratuity.submit()