From f099dbad353661ef38cbffbf23ab46b36eddc8e0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 9 Jun 2026 23:28:12 +0530 Subject: [PATCH] refactor(journal_entry): give get_outstanding an explicit parameter list Replace the single opaque `args` parameter of the whitelisted get_outstanding with explicit named parameters (the supported interface), splitting the body into _get_journal_entry_outstanding / _get_invoice_outstanding. The legacy `args` payload is still accepted via kwargs for backward compatibility with custom apps. Resolves the overusing-args semgrep finding. --- .../doctype/journal_entry/journal_entry.js | 18 ++- .../doctype/journal_entry/journal_entry.py | 130 +++++++++++------- 2 files changed, 85 insertions(+), 63 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 1293a18ca0b..472f35fdc79 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -409,18 +409,16 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro } get_outstanding(doctype, docname, company, child) { - var args = { - doctype: doctype, - docname: docname, - party: child.party, - account: child.account, - account_currency: child.account_currency, - company: company, - }; - return frappe.call({ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_outstanding", - args: { args: args }, + args: { + doctype: doctype, + docname: docname, + company: company, + account: child.account, + party: child.party, + account_currency: child.account_currency, + }, callback: function (r) { if (r.message) { $.each(r.message, function (field, value) { diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index dde87dcbf36..70e6164beb2 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -1101,68 +1101,92 @@ def get_against_jv( @frappe.whitelist() -def get_outstanding(args: str | dict) -> dict: - """Return the outstanding amount and side to set when referencing a JV / Invoice.""" +def get_outstanding( + doctype: str | None = None, + docname: str | None = None, + company: str | None = None, + account: str | None = None, + party: str | None = None, + account_currency: str | None = None, + **kwargs, +) -> dict | None: + """Return the outstanding amount and side to set when referencing a JV / Invoice. + + The named parameters are the supported interface. The legacy `args` payload dict + (captured via kwargs) is still accepted for backward compatibility with callers, + including custom apps, and is unpacked into the named parameters below. + """ if not frappe.has_permission("Account"): frappe.msgprint(_("No Permission"), raise_exception=1) - if isinstance(args, str): - args = json.loads(args) + if legacy_payload := kwargs.get("args"): + if isinstance(legacy_payload, str): + legacy_payload = json.loads(legacy_payload) + doctype = legacy_payload.get("doctype") + docname = legacy_payload.get("docname") + company = legacy_payload.get("company") + account = legacy_payload.get("account") + party = legacy_payload.get("party") + account_currency = legacy_payload.get("account_currency") - company_currency = erpnext.get_company_currency(args.get("company")) - due_date = None + if doctype == "Journal Entry": + return _get_journal_entry_outstanding(docname, account, party) - if args.get("doctype") == "Journal Entry": - jea = frappe.qb.DocType("Journal Entry Account") - query = ( - frappe.qb.from_(jea) - .select(Sum(jea.debit_in_account_currency) - Sum(jea.credit_in_account_currency)) - .where( - (jea.parent == args.get("docname")) - & (jea.account == args.get("account")) - & (jea.reference_type.isnull() | (jea.reference_type == "")) - ) + if doctype in ("Sales Invoice", "Purchase Invoice"): + return _get_invoice_outstanding(doctype, docname, company, account_currency) + + +def _get_journal_entry_outstanding(docname: str, account: str | None, party: str | None) -> dict: + """Unreferenced debit-minus-credit balance for an account on a Journal Entry.""" + jea = frappe.qb.DocType("Journal Entry Account") + query = ( + frappe.qb.from_(jea) + .select(Sum(jea.debit_in_account_currency) - Sum(jea.credit_in_account_currency)) + .where( + (jea.parent == docname) + & (jea.account == account) + & (jea.reference_type.isnull() | (jea.reference_type == "")) ) - if args.get("party"): - query = query.where(jea.party == args.get("party")) + ) + if party: + query = query.where(jea.party == party) - against_jv_amount = query.run() - against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0 - amount_field = "credit_in_account_currency" if against_jv_amount > 0 else "debit_in_account_currency" - return {amount_field: abs(against_jv_amount)} - elif args.get("doctype") in ("Sales Invoice", "Purchase Invoice"): - party_type = "Customer" if args.get("doctype") == "Sales Invoice" else "Supplier" - invoice = frappe.db.get_value( - args["doctype"], - args["docname"], - ["outstanding_amount", "conversion_rate", scrub(party_type), "due_date"], - as_dict=1, + result = query.run() + balance = flt(result[0][0]) if result else 0 + amount_field = "credit_in_account_currency" if balance > 0 else "debit_in_account_currency" + return {amount_field: abs(balance)} + + +def _get_invoice_outstanding(doctype: str, docname: str, company: str, account_currency: str | None) -> dict: + """Outstanding amount, side, party and exchange rate for a Sales/Purchase Invoice.""" + party_type = "Customer" if doctype == "Sales Invoice" else "Supplier" + invoice = frappe.db.get_value( + doctype, + docname, + ["outstanding_amount", "conversion_rate", scrub(party_type), "due_date"], + as_dict=1, + ) + + company_currency = erpnext.get_company_currency(company) + exchange_rate = invoice.conversion_rate if account_currency != company_currency else 1 + + outstanding_is_positive = flt(invoice.outstanding_amount) > 0 + if doctype == "Sales Invoice": + amount_field = ( + "credit_in_account_currency" if outstanding_is_positive else "debit_in_account_currency" + ) + else: + amount_field = ( + "debit_in_account_currency" if outstanding_is_positive else "credit_in_account_currency" ) - due_date = invoice.get("due_date") - - exchange_rate = invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1 - - if args["doctype"] == "Sales Invoice": - amount_field = ( - "credit_in_account_currency" - if flt(invoice.outstanding_amount) > 0 - else "debit_in_account_currency" - ) - else: - amount_field = ( - "debit_in_account_currency" - if flt(invoice.outstanding_amount) > 0 - else "credit_in_account_currency" - ) - - return { - amount_field: abs(flt(invoice.outstanding_amount)), - "exchange_rate": exchange_rate, - "party_type": party_type, - "party": invoice.get(scrub(party_type)), - "reference_due_date": due_date, - } + return { + amount_field: abs(flt(invoice.outstanding_amount)), + "exchange_rate": exchange_rate, + "party_type": party_type, + "party": invoice.get(scrub(party_type)), + "reference_due_date": invoice.get("due_date"), + } @frappe.whitelist()