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.
This commit is contained in:
Nabin Hait
2026-06-09 23:28:12 +05:30
parent cc8ce03232
commit f099dbad35
2 changed files with 85 additions and 63 deletions

View File

@@ -409,18 +409,16 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
} }
get_outstanding(doctype, docname, company, child) { 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({ return frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_outstanding", 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) { callback: function (r) {
if (r.message) { if (r.message) {
$.each(r.message, function (field, value) { $.each(r.message, function (field, value) {

View File

@@ -1101,68 +1101,92 @@ def get_against_jv(
@frappe.whitelist() @frappe.whitelist()
def get_outstanding(args: str | dict) -> dict: def get_outstanding(
"""Return the outstanding amount and side to set when referencing a JV / Invoice.""" 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"): if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1) frappe.msgprint(_("No Permission"), raise_exception=1)
if isinstance(args, str): if legacy_payload := kwargs.get("args"):
args = json.loads(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")) if doctype == "Journal Entry":
due_date = None return _get_journal_entry_outstanding(docname, account, party)
if args.get("doctype") == "Journal Entry": if doctype in ("Sales Invoice", "Purchase Invoice"):
jea = frappe.qb.DocType("Journal Entry Account") return _get_invoice_outstanding(doctype, docname, company, account_currency)
query = (
frappe.qb.from_(jea)
.select(Sum(jea.debit_in_account_currency) - Sum(jea.credit_in_account_currency)) def _get_journal_entry_outstanding(docname: str, account: str | None, party: str | None) -> dict:
.where( """Unreferenced debit-minus-credit balance for an account on a Journal Entry."""
(jea.parent == args.get("docname")) jea = frappe.qb.DocType("Journal Entry Account")
& (jea.account == args.get("account")) query = (
& (jea.reference_type.isnull() | (jea.reference_type == "")) 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() result = query.run()
against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0 balance = flt(result[0][0]) if result else 0
amount_field = "credit_in_account_currency" if against_jv_amount > 0 else "debit_in_account_currency" amount_field = "credit_in_account_currency" if balance > 0 else "debit_in_account_currency"
return {amount_field: abs(against_jv_amount)} return {amount_field: abs(balance)}
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( def _get_invoice_outstanding(doctype: str, docname: str, company: str, account_currency: str | None) -> dict:
args["doctype"], """Outstanding amount, side, party and exchange rate for a Sales/Purchase Invoice."""
args["docname"], party_type = "Customer" if doctype == "Sales Invoice" else "Supplier"
["outstanding_amount", "conversion_rate", scrub(party_type), "due_date"], invoice = frappe.db.get_value(
as_dict=1, 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") return {
amount_field: abs(flt(invoice.outstanding_amount)),
exchange_rate = invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1 "exchange_rate": exchange_rate,
"party_type": party_type,
if args["doctype"] == "Sales Invoice": "party": invoice.get(scrub(party_type)),
amount_field = ( "reference_due_date": invoice.get("due_date"),
"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,
}
@frappe.whitelist() @frappe.whitelist()