From 9faae257ac40c36349ba86d6308f79a47ee7d27d Mon Sep 17 00:00:00 2001 From: kunhimohamed Date: Mon, 30 Mar 2026 19:57:32 +0400 Subject: [PATCH 1/2] fix(customer credit balance report): detail view of outstanding amount, gl wise, sales order wise, delivery note wise --- .../customer_credit_balance.py | 82 +++++++++++++++++-- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py index aaa9df5bf617..e5febb031366 100644 --- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py +++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py @@ -4,7 +4,7 @@ import frappe from frappe import _ from frappe.utils import flt -from erpnext.selling.doctype.customer.customer import get_customer_outstanding, get_credit_limit +from erpnext.selling.doctype.customer.customer import get_credit_limit def execute(filters=None): if not filters: filters = {} @@ -19,20 +19,26 @@ def execute(filters=None): for d in customer_list: row = [] - outstanding_amt = get_customer_outstanding(d.name, filters.get("company"), - ignore_outstanding_sales_order=d.bypass_credit_limit_check_at_sales_order) + outstanding_based_on_gle, outstanding_based_on_so, outstanding_based_on_dn = get_customer_outstanding(d.name, filters.get("company"), + ignore_outstanding_sales_order=d.bypass_credit_limit_check) + + total_outstanding_amount = outstanding_based_on_gle+outstanding_based_on_so+outstanding_based_on_dn credit_limit = get_credit_limit(d.name, filters.get("company")) - bal = flt(credit_limit) - flt(outstanding_amt) + bal = flt(credit_limit) - flt(total_outstanding_amount) if customer_naming_type == "Naming Series": - row = [d.name, d.customer_name, credit_limit, outstanding_amt, bal, + row = [ + d.name, d.customer_name, credit_limit, + outstanding_based_on_gle, outstanding_based_on_so, + outstanding_based_on_dn, total_outstanding_amount, bal, d.bypass_credit_limit_check, d.is_frozen, d.disabled] else: - row = [d.name, credit_limit, outstanding_amt, bal, - d.bypass_credit_limit_check_at_sales_order, d.is_frozen, d.disabled] + row = [d.name, credit_limit, outstanding_based_on_gle, outstanding_based_on_so, + outstanding_based_on_dn, total_outstanding_amount, bal, + d.bypass_credit_limit_check, d.is_frozen, d.disabled] if credit_limit: data.append(row) @@ -43,7 +49,10 @@ def get_columns(customer_naming_type): columns = [ _("Customer") + ":Link/Customer:120", _("Credit Limit") + ":Currency:120", - _("Outstanding Amt") + ":Currency:100", + _("General Ledger Outstanding Amt") + ":Currency:100", + _("Sales Order Outstanding Amt") + ":Currency:100", + _("Delivery Note Outstanding Amt") + ":Currency:100", + _("Total Outstanding Amt") + ":Currency:100", _("Credit Balance") + ":Currency:120", _("Bypass credit check at Sales Order ") + ":Check:80", _("Is Frozen") + ":Check:80", @@ -71,3 +80,60 @@ def get_details(filters): AND ccl.company = '{0}' {1} """.format( filters.get("company"),conditions), as_dict=1) #nosec + +def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None): + # Outstanding based on GL Entries + + cond = "" + if cost_center: + lft, rgt = frappe.get_cached_value("Cost Center", + cost_center, ['lft', 'rgt']) + + cond = """ and cost_center in (select name from `tabCost Center` where + lft >= {0} and rgt <= {1})""".format(lft, rgt) + + outstanding_based_on_gle = frappe.db.sql(""" + select sum(debit) - sum(credit) + from `tabGL Entry` where party_type = 'Customer' + and party = %s and company=%s {0}""".format(cond), (customer, company)) + + outstanding_based_on_gle = flt(outstanding_based_on_gle[0][0]) if outstanding_based_on_gle else 0 + + # Outstanding based on Sales Order + outstanding_based_on_so = 0.0 + + # if credit limit check is bypassed at sales order level, + # we should not consider outstanding Sales Orders, when customer credit balance report is run + if not ignore_outstanding_sales_order: + outstanding_based_on_so = frappe.db.sql(""" + select sum(base_grand_total*(100 - per_completed)/100) + from `tabSales Order` + where customer=%s and docstatus = 1 and company=%s + and billing_status = 'To Bill' and status != 'Closed'""", (customer, company)) + + outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0.0 + + # Outstanding based on Delivery Note, which are not created against Sales Order + unmarked_delivery_note_items = frappe.db.sql("""select + dn_item.name, dn_item.amount, dn.base_net_total, dn.base_grand_total + from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item + where + dn.name = dn_item.parent + and dn.customer=%s and dn.company=%s + and dn.docstatus = 1 and dn.status not in ('Closed', 'Stopped') + and ifnull(dn_item.sales_order, '') = '' + and ifnull(dn_item.sales_invoice, '') = '' + """, (customer, company), as_dict=True) + + outstanding_based_on_dn = 0.0 + + for dn_item in unmarked_delivery_note_items: + si_amount = frappe.db.sql("""select sum(amount) + from `tabSales Invoice Item` + where delivery_note_item = %s and docstatus = 1""", dn_item.name)[0][0] + + if flt(dn_item.amount) > flt(si_amount) and dn_item.base_net_total: + outstanding_based_on_dn += ((flt(dn_item.amount) - flt(si_amount)) \ + / dn_item.base_net_total) * dn_item.base_grand_total + + return outstanding_based_on_gle, outstanding_based_on_so, outstanding_based_on_dn From f94d0faba6c77f7153d18ee7ca057ef69946e235 Mon Sep 17 00:00:00 2001 From: kunhimohamed Date: Mon, 30 Mar 2026 20:28:35 +0400 Subject: [PATCH 2/2] fix(inheritance): reuse the method by the method argument change --- erpnext/selling/doctype/customer/customer.py | 8 ++- .../customer_credit_balance.py | 61 +------------------ 2 files changed, 7 insertions(+), 62 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index dea6b7bac86d..a1f97fbfbef0 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -476,7 +476,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, -def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None): +def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None, no_sum=False): # Outstanding based on GL Entries cond = "" @@ -530,8 +530,10 @@ def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=F if flt(dn_item.amount) > flt(si_amount) and dn_item.base_net_total: outstanding_based_on_dn += ((flt(dn_item.amount) - flt(si_amount)) \ / dn_item.base_net_total) * dn_item.base_grand_total - - return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn + if not no_sum: + return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn + else: + return outstanding_based_on_gle, outstanding_based_on_so, outstanding_based_on_dn def get_credit_limit(customer, company): diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py index e5febb031366..70599094ee6a 100644 --- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py +++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.py @@ -4,7 +4,7 @@ import frappe from frappe import _ from frappe.utils import flt -from erpnext.selling.doctype.customer.customer import get_credit_limit +from erpnext.selling.doctype.customer.customer import get_customer_outstanding, get_credit_limit def execute(filters=None): if not filters: filters = {} @@ -20,7 +20,7 @@ def execute(filters=None): row = [] outstanding_based_on_gle, outstanding_based_on_so, outstanding_based_on_dn = get_customer_outstanding(d.name, filters.get("company"), - ignore_outstanding_sales_order=d.bypass_credit_limit_check) + ignore_outstanding_sales_order=d.bypass_credit_limit_check, no_sum=True) total_outstanding_amount = outstanding_based_on_gle+outstanding_based_on_so+outstanding_based_on_dn @@ -80,60 +80,3 @@ def get_details(filters): AND ccl.company = '{0}' {1} """.format( filters.get("company"),conditions), as_dict=1) #nosec - -def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None): - # Outstanding based on GL Entries - - cond = "" - if cost_center: - lft, rgt = frappe.get_cached_value("Cost Center", - cost_center, ['lft', 'rgt']) - - cond = """ and cost_center in (select name from `tabCost Center` where - lft >= {0} and rgt <= {1})""".format(lft, rgt) - - outstanding_based_on_gle = frappe.db.sql(""" - select sum(debit) - sum(credit) - from `tabGL Entry` where party_type = 'Customer' - and party = %s and company=%s {0}""".format(cond), (customer, company)) - - outstanding_based_on_gle = flt(outstanding_based_on_gle[0][0]) if outstanding_based_on_gle else 0 - - # Outstanding based on Sales Order - outstanding_based_on_so = 0.0 - - # if credit limit check is bypassed at sales order level, - # we should not consider outstanding Sales Orders, when customer credit balance report is run - if not ignore_outstanding_sales_order: - outstanding_based_on_so = frappe.db.sql(""" - select sum(base_grand_total*(100 - per_completed)/100) - from `tabSales Order` - where customer=%s and docstatus = 1 and company=%s - and billing_status = 'To Bill' and status != 'Closed'""", (customer, company)) - - outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0.0 - - # Outstanding based on Delivery Note, which are not created against Sales Order - unmarked_delivery_note_items = frappe.db.sql("""select - dn_item.name, dn_item.amount, dn.base_net_total, dn.base_grand_total - from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item - where - dn.name = dn_item.parent - and dn.customer=%s and dn.company=%s - and dn.docstatus = 1 and dn.status not in ('Closed', 'Stopped') - and ifnull(dn_item.sales_order, '') = '' - and ifnull(dn_item.sales_invoice, '') = '' - """, (customer, company), as_dict=True) - - outstanding_based_on_dn = 0.0 - - for dn_item in unmarked_delivery_note_items: - si_amount = frappe.db.sql("""select sum(amount) - from `tabSales Invoice Item` - where delivery_note_item = %s and docstatus = 1""", dn_item.name)[0][0] - - if flt(dn_item.amount) > flt(si_amount) and dn_item.base_net_total: - outstanding_based_on_dn += ((flt(dn_item.amount) - flt(si_amount)) \ - / dn_item.base_net_total) * dn_item.base_grand_total - - return outstanding_based_on_gle, outstanding_based_on_so, outstanding_based_on_dn