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:
parent
48f2e5ac1d
commit
ab47409e91
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -43,8 +43,6 @@ frappe.ui.form.on("Employee Referral", {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
},
|
||||
create_job_applicant: function(frm) {
|
||||
frappe.model.open_mapped_doc({
|
||||
|
0
erpnext/hr/doctype/full_and_final_asset/__init__.py
Normal file
0
erpnext/hr/doctype/full_and_final_asset/__init__.py
Normal 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) {
|
||||
|
||||
// }
|
||||
});
|
@ -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
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
||||
}
|
@ -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
|
@ -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));
|
||||
}
|
||||
});
|
@ -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
|
||||
}
|
@ -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]
|
@ -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];
|
||||
}
|
||||
};
|
@ -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
|
@ -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",
|
||||
|
@ -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',
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -11,10 +11,6 @@ def get_data():
|
||||
{
|
||||
'label': _('Payment'),
|
||||
'items': ['Payment Entry']
|
||||
},
|
||||
{
|
||||
'label': _('Additional Salary'),
|
||||
'items': ['Additional Salary']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user