chore: Extract Expense Claim, FnF, Gratuity, Employee Advance related code from accounting
- Added hooks `advance_payment_doctypes`, `invoice_doctypes`, `period_closing_doctypes` for other apps to extend accounting functionality
- Removed `set_query` code from `journal_entry.js` and `payment_entry.js`
- removed `update_status_for_full_and_final_statement` trigger on JE submission/cancellation
- refactored `payment_entry.py`: split functions for validating reference doctypes for easy overriding, removed hrms references from `get_reference_details` and `get_payment_entry`
- removed dead code: functions `get_bill_no_and_update_amounts`, `get_total_amount_exchange_rate_base_on_currency`, `get_total_amount_exchange_rate_for_employee_advance`, `get_amounts_based_on_ref_doc`, `get_amounts_based_on_reference_doctype`
diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py
index 0c15d6a..1f2d12f 100644
--- a/erpnext/accounts/doctype/accounting_period/accounting_period.py
+++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py
@@ -49,15 +49,8 @@
@frappe.whitelist()
def get_doctypes_for_closing(self):
docs_for_closing = []
- doctypes = [
- "Sales Invoice",
- "Purchase Invoice",
- "Journal Entry",
- "Payroll Entry",
- "Bank Clearance",
- "Asset",
- "Stock Entry",
- ]
+ doctypes = frappe.get_hooks("period_closing_doctypes")
+
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
for closed_doctype in closed_doctypes:
docs_for_closing.append(closed_doctype)
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 3cc28a3..b539bfe 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -240,25 +240,6 @@
me.frm.set_query("reference_name", "accounts", function(doc, cdt, cdn) {
var jvd = frappe.get_doc(cdt, cdn);
- // expense claim
- if(jvd.reference_type==="Expense Claim") {
- return {
- filters: {
- 'total_sanctioned_amount': ['>', 0],
- 'status': ['!=', 'Paid'],
- 'docstatus': 1
- }
- };
- }
-
- if(jvd.reference_type==="Employee Advance") {
- return {
- filters: {
- 'docstatus': 1
- }
- };
- }
-
// journal entry
if(jvd.reference_type==="Journal Entry") {
frappe.model.validate_missing(jvd, "account");
@@ -271,13 +252,6 @@
};
}
- // payroll entry
- if(jvd.reference_type==="Payroll Entry") {
- return {
- query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.get_payroll_entries_for_jv",
- };
- }
-
var out = {
filters: [
[jvd.reference_type, "docstatus", "=", 1]
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 0260eae..5690911 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -83,7 +83,6 @@
self.update_advance_paid()
self.update_inter_company_jv()
self.update_invoice_discounting()
- self.update_status_for_full_and_final_statement()
def on_cancel(self):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
@@ -97,7 +96,6 @@
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
@@ -106,21 +104,13 @@
advance_paid = frappe._dict()
for d in self.get("accounts"):
if d.is_advance:
- if d.reference_type in ("Sales Order", "Purchase Order", "Employee Advance"):
+ if d.reference_type in frappe.get_hooks("advance_payment_doctypes"):
advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
for voucher_type, order_list in advance_paid.items():
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"
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index d7fae6d..0f53079 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -110,8 +110,6 @@
var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"];
} else if (frm.doc.party_type == "Supplier") {
var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
- } else if (frm.doc.party_type == "Employee") {
- var doctypes = ["Expense Claim", "Journal Entry"];
} else {
var doctypes = ["Journal Entry"];
}
@@ -140,17 +138,12 @@
const child = locals[cdt][cdn];
const filters = {"docstatus": 1, "company": doc.company};
const party_type_doctypes = ['Sales Invoice', 'Sales Order', 'Purchase Invoice',
- 'Purchase Order', 'Expense Claim', 'Fees', 'Dunning'];
+ 'Purchase Order', 'Dunning'];
if (in_list(party_type_doctypes, child.reference_doctype)) {
filters[doc.party_type.toLowerCase()] = doc.party;
}
- if(child.reference_doctype == "Expense Claim") {
- filters["docstatus"] = 1;
- filters["is_paid"] = 0;
- }
-
return {
filters: filters
};
@@ -730,7 +723,7 @@
c.payment_term = d.payment_term;
c.allocated_amount = d.allocated_amount;
- if(!in_list(["Sales Order", "Purchase Order", "Expense Claim", "Fees"], d.voucher_type)) {
+ if(!in_list(frm.events.get_order_doctypes(frm), d.voucher_type)) {
if(flt(d.outstanding_amount) > 0)
total_positive_outstanding += flt(d.outstanding_amount);
else
@@ -745,7 +738,7 @@
} else {
c.exchange_rate = 1;
}
- if (in_list(['Sales Invoice', 'Purchase Invoice', "Expense Claim", "Fees"], d.reference_doctype)){
+ if (in_list(frm.events.get_invoice_doctypes(frm), d.reference_doctype)){
c.due_date = d.due_date;
}
});
@@ -776,6 +769,14 @@
});
},
+ get_order_doctypes: function(frm) {
+ return ["Sales Order", "Purchase Order"];
+ },
+
+ get_invoice_doctypes: function(frm) {
+ return ["Sales Invoice", "Purchase Invoice"];
+ },
+
allocate_party_amount_against_ref_docs: function(frm, paid_amount, paid_amount_change) {
var total_positive_outstanding_including_order = 0;
var total_negative_outstanding = 0;
@@ -946,14 +947,6 @@
frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx]));
return false;
}
-
- if(frm.doc.party_type=="Employee" &&
- !in_list(["Expense Claim", "Journal Entry"], row.reference_doctype)
- ) {
- frappe.model.set_value(row.doctype, row.name, "against_voucher_type", null);
- frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry", [row.idx]));
- return false;
- }
}
if (row) {
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index c9746ab..c8a5746 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -293,14 +293,7 @@
frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field)))
def validate_reference_documents(self):
- if self.party_type == "Customer":
- valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning")
- elif self.party_type == "Supplier":
- valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
- elif self.party_type == "Employee":
- valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance", "Gratuity")
- elif self.party_type == "Shareholder":
- valid_reference_doctypes = "Journal Entry"
+ valid_reference_doctypes = self.get_valid_reference_doctypes()
for d in self.get("references"):
if not d.allocated_amount:
@@ -326,7 +319,7 @@
else:
self.validate_journal_entry()
- if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim", "Fees"):
+ if d.reference_doctype in frappe.get_hooks("invoice_doctypes"):
if self.party_type == "Customer":
ref_party_account = (
get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to
@@ -352,6 +345,16 @@
if ref_doc.docstatus != 1:
frappe.throw(_("{0} {1} must be submitted").format(d.reference_doctype, d.reference_name))
+ def get_valid_reference_doctypes(self):
+ if self.party_type == "Customer":
+ return ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning")
+ elif self.party_type == "Supplier":
+ return ("Purchase Order", "Purchase Invoice", "Journal Entry")
+ elif self.party_type == "Shareholder":
+ return ("Journal Entry",)
+ elif self.party_type == "Employee":
+ return ("Journal Entry",)
+
def validate_paid_invoices(self):
no_oustanding_refs = {}
@@ -974,12 +977,7 @@
def update_advance_paid(self):
if self.payment_type in ("Receive", "Pay") and self.party:
for d in self.get("references"):
- if d.allocated_amount and d.reference_doctype in (
- "Sales Order",
- "Purchase Order",
- "Employee Advance",
- "Gratuity",
- ):
+ if d.allocated_amount and d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"):
frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
def on_recurring(self, reference_doc, auto_repeat_doc):
@@ -1235,7 +1233,7 @@
for d in outstanding_invoices:
d["exchange_rate"] = 1
if party_account_currency != company_currency:
- if d.voucher_type in ("Sales Invoice", "Purchase Invoice", "Expense Claim"):
+ if d.voucher_type in frappe.get_hooks("invoice_doctypes"):
d["exchange_rate"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "conversion_rate")
elif d.voucher_type == "Journal Entry":
d["exchange_rate"] = get_exchange_rate(
@@ -1562,20 +1560,17 @@
@frappe.whitelist()
def get_reference_details(reference_doctype, reference_name, party_account_currency):
- total_amount = outstanding_amount = exchange_rate = bill_no = None
+ total_amount = outstanding_amount = exchange_rate = None
+
ref_doc = frappe.get_doc(reference_doctype, reference_name)
company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(
ref_doc.company
)
- if reference_doctype == "Fees":
- total_amount = ref_doc.get("grand_total")
+ if reference_doctype == "Dunning":
+ total_amount = outstanding_amount = ref_doc.get("dunning_amount")
exchange_rate = 1
- outstanding_amount = ref_doc.get("outstanding_amount")
- elif reference_doctype == "Dunning":
- total_amount = ref_doc.get("dunning_amount")
- exchange_rate = 1
- outstanding_amount = ref_doc.get("dunning_amount")
+
elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
total_amount = ref_doc.get("total_amount")
if ref_doc.multi_currency:
@@ -1585,16 +1580,8 @@
else:
exchange_rate = 1
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
+
elif reference_doctype != "Journal Entry":
- if ref_doc.doctype == "Expense Claim":
- total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
- elif ref_doc.doctype == "Employee Advance":
- total_amount = ref_doc.advance_amount
- exchange_rate = ref_doc.get("exchange_rate")
- if party_account_currency != ref_doc.currency:
- total_amount = flt(total_amount) * flt(exchange_rate)
- elif ref_doc.doctype == "Gratuity":
- total_amount = ref_doc.amount
if not total_amount:
if party_account_currency == company_currency:
total_amount = ref_doc.base_grand_total
@@ -1607,26 +1594,12 @@
exchange_rate = ref_doc.get("conversion_rate") or get_exchange_rate(
party_account_currency, company_currency, ref_doc.posting_date
)
+
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
outstanding_amount = ref_doc.get("outstanding_amount")
- bill_no = ref_doc.get("bill_no")
- elif reference_doctype == "Expense Claim":
- outstanding_amount = (
- flt(ref_doc.get("total_sanctioned_amount"))
- + flt(ref_doc.get("total_taxes_and_charges"))
- - flt(ref_doc.get("total_amount_reimbursed"))
- - flt(ref_doc.get("total_advance_amount"))
- )
- elif reference_doctype == "Employee Advance":
- outstanding_amount = flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)
- if party_account_currency != ref_doc.currency:
- outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
- if party_account_currency == company_currency:
- exchange_rate = 1
- elif reference_doctype == "Gratuity":
- outstanding_amount = ref_doc.amount - flt(ref_doc.paid_amount)
else:
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
+
else:
# Get the exchange rate based on the posting date of the ref doc.
exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
@@ -1637,114 +1610,11 @@
"total_amount": flt(total_amount),
"outstanding_amount": flt(outstanding_amount),
"exchange_rate": flt(exchange_rate),
- "bill_no": bill_no,
+ "bill_no": ref_doc.get("bill_no"),
}
)
-def get_amounts_based_on_reference_doctype(
- reference_doctype, ref_doc, party_account_currency, company_currency, reference_name
-):
- total_amount = outstanding_amount = exchange_rate = None
- if reference_doctype == "Fees":
- total_amount = ref_doc.get("grand_total")
- exchange_rate = 1
- outstanding_amount = ref_doc.get("outstanding_amount")
- elif reference_doctype == "Dunning":
- total_amount = ref_doc.get("dunning_amount")
- exchange_rate = 1
- outstanding_amount = ref_doc.get("dunning_amount")
- elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
- total_amount = ref_doc.get("total_amount")
- if ref_doc.multi_currency:
- exchange_rate = get_exchange_rate(
- party_account_currency, company_currency, ref_doc.posting_date
- )
- else:
- exchange_rate = 1
- outstanding_amount = get_outstanding_on_journal_entry(reference_name)
-
- return total_amount, outstanding_amount, exchange_rate
-
-
-def get_amounts_based_on_ref_doc(
- reference_doctype, ref_doc, party_account_currency, company_currency
-):
- total_amount = outstanding_amount = exchange_rate = None
- if ref_doc.doctype == "Expense Claim":
- total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
- elif ref_doc.doctype == "Employee Advance":
- total_amount, exchange_rate = get_total_amount_exchange_rate_for_employee_advance(
- party_account_currency, ref_doc
- )
-
- if not total_amount:
- total_amount, exchange_rate = get_total_amount_exchange_rate_base_on_currency(
- party_account_currency, company_currency, ref_doc
- )
-
- if not exchange_rate:
- # Get the exchange rate from the original ref doc
- # or get it based on the posting date of the ref doc
- exchange_rate = ref_doc.get("conversion_rate") or get_exchange_rate(
- party_account_currency, company_currency, ref_doc.posting_date
- )
-
- outstanding_amount, exchange_rate, bill_no = get_bill_no_and_update_amounts(
- reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency
- )
-
- return total_amount, outstanding_amount, exchange_rate, bill_no
-
-
-def get_total_amount_exchange_rate_for_employee_advance(party_account_currency, ref_doc):
- total_amount = ref_doc.advance_amount
- exchange_rate = ref_doc.get("exchange_rate")
- if party_account_currency != ref_doc.currency:
- total_amount = flt(total_amount) * flt(exchange_rate)
-
- return total_amount, exchange_rate
-
-
-def get_total_amount_exchange_rate_base_on_currency(
- party_account_currency, company_currency, ref_doc
-):
- exchange_rate = None
- if party_account_currency == company_currency:
- total_amount = ref_doc.base_grand_total
- exchange_rate = 1
- else:
- total_amount = ref_doc.grand_total
-
- return total_amount, exchange_rate
-
-
-def get_bill_no_and_update_amounts(
- reference_doctype, ref_doc, total_amount, exchange_rate, party_account_currency, company_currency
-):
- outstanding_amount = bill_no = None
- if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
- outstanding_amount = ref_doc.get("outstanding_amount")
- bill_no = ref_doc.get("bill_no")
- elif reference_doctype == "Expense Claim":
- outstanding_amount = (
- flt(ref_doc.get("total_sanctioned_amount"))
- + flt(ref_doc.get("total_taxes_and_charges"))
- - flt(ref_doc.get("total_amount_reimbursed"))
- - flt(ref_doc.get("total_advance_amount"))
- )
- elif reference_doctype == "Employee Advance":
- outstanding_amount = flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)
- if party_account_currency != ref_doc.currency:
- outstanding_amount = flt(outstanding_amount) * flt(exchange_rate)
- if party_account_currency == company_currency:
- exchange_rate = 1
- else:
- outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
-
- return outstanding_amount, exchange_rate, bill_no
-
-
@frappe.whitelist()
def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
reference_doc = None
@@ -1863,8 +1733,6 @@
pe.set_missing_values()
if party_account and bank:
- if dt == "Employee Advance":
- reference_doc = doc
pe.set_exchange_rate(ref_doc=reference_doc)
pe.set_amounts()
if discount_amount:
@@ -1899,8 +1767,6 @@
party_type = "Customer"
elif dt in ("Purchase Invoice", "Purchase Order"):
party_type = "Supplier"
- elif dt in ("Expense Claim", "Employee Advance", "Gratuity"):
- party_type = "Employee"
return party_type
@@ -1911,12 +1777,6 @@
party_account = doc.credit_to
elif dt == "Fees":
party_account = doc.receivable_account
- elif dt == "Employee Advance":
- party_account = doc.advance_account
- elif dt == "Expense Claim":
- party_account = doc.payable_account
- elif dt == "Gratuity":
- party_account = doc.payable_account
else:
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
return party_account
@@ -1951,24 +1811,12 @@
else:
grand_total = doc.rounded_total or doc.grand_total
outstanding_amount = doc.outstanding_amount
- elif dt in ("Expense Claim"):
- grand_total = doc.total_sanctioned_amount + doc.total_taxes_and_charges
- outstanding_amount = doc.grand_total - doc.total_amount_reimbursed
- elif dt == "Employee Advance":
- grand_total = flt(doc.advance_amount)
- outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount)
- if party_account_currency != doc.currency:
- grand_total = flt(doc.advance_amount) * flt(doc.exchange_rate)
- outstanding_amount = (flt(doc.advance_amount) - flt(doc.paid_amount)) * flt(doc.exchange_rate)
elif dt == "Fees":
grand_total = doc.grand_total
outstanding_amount = doc.outstanding_amount
elif dt == "Dunning":
grand_total = doc.grand_total
outstanding_amount = doc.grand_total
- elif dt == "Gratuity":
- grand_total = doc.amount
- outstanding_amount = flt(doc.amount) - flt(doc.paid_amount)
else:
if party_account_currency == doc.company_currency:
grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total)
@@ -1990,8 +1838,6 @@
received_amount = bank_amount
else:
received_amount = paid_amount * doc.get("conversion_rate", 1)
- if dt == "Employee Advance":
- received_amount = paid_amount * doc.get("exchange_rate", 1)
else:
received_amount = abs(outstanding_amount)
if bank_amount:
@@ -1999,8 +1845,6 @@
else:
# if party account currency and bank currency is different then populate paid amount as well
paid_amount = received_amount * doc.get("conversion_rate", 1)
- if dt == "Employee Advance":
- paid_amount = received_amount * doc.get("exchange_rate", 1)
return paid_amount, received_amount
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index d629b70..a2dc205 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -472,6 +472,19 @@
communication_doctypes = ["Customer", "Supplier"]
+advance_payment_doctypes = ["Sales Order", "Purchase Order"]
+
+invoice_doctypes = ["Sales Invoice", "Purchase Invoice"]
+
+period_closing_doctypes = [
+ "Sales Invoice",
+ "Purchase Invoice",
+ "Journal Entry",
+ "Bank Clearance",
+ "Asset",
+ "Stock Entry",
+]
+
accounting_dimension_doctypes = [
"GL Entry",
"Payment Ledger Entry",
@@ -479,12 +492,8 @@
"Purchase Invoice",
"Payment Entry",
"Asset",
- "Expense Claim",
- "Expense Claim Detail",
- "Expense Taxes and Charges",
"Stock Entry",
"Budget",
- "Payroll Entry",
"Delivery Note",
"Sales Invoice Item",
"Purchase Invoice Item",