refactor(customer): replace SQL with query builder in get_customer_ou… (#55209)

This commit is contained in:
Loic Oberle
2026-05-26 10:00:23 +02:00
committed by GitHub
parent bbb7b6f8e0
commit b8327e4031

View File

@@ -667,85 +667,97 @@ def send_emails(
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):
# Outstanding based on GL Entries from frappe.query_builder import Criterion
cond = "" from frappe.query_builder.functions import Coalesce, IfNull, Sum
GLEntry = frappe.qb.DocType("GL Entry")
gle_query = (
frappe.qb.from_(GLEntry)
.select(Sum(GLEntry.debit) - Sum(GLEntry.credit))
.where(GLEntry.party_type == "Customer")
.where(GLEntry.party == customer)
.where(GLEntry.company == company)
.where(GLEntry.is_cancelled == 0)
)
if cost_center: if cost_center:
lft, rgt = frappe.get_cached_value("Cost Center", cost_center, ["lft", "rgt"]) lft, rgt = frappe.get_cached_value("Cost Center", cost_center, ["lft", "rgt"])
CostCenter = frappe.qb.DocType("Cost Center")
cost_center_subquery = (
frappe.qb.from_(CostCenter)
.select(CostCenter.name)
.where(CostCenter.lft >= lft)
.where(CostCenter.rgt <= rgt)
)
gle_query = gle_query.where(GLEntry.cost_center.isin(cost_center_subquery))
cond = f""" and cost_center in (select name from `tabCost Center` where gle_res = gle_query.run()
lft >= {lft} and rgt <= {rgt})""" outstanding_based_on_gle = flt(gle_res[0][0]) if gle_res and gle_res[0][0] is not None else 0.0
outstanding_based_on_gle = frappe.db.sql( outstanding_based_on_so = 0.0
f"""
select sum(debit) - sum(credit)
from `tabGL Entry` where party_type = 'Customer'
and is_cancelled = 0 and party = %s
and company=%s {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
# 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: if not ignore_outstanding_sales_order:
outstanding_based_on_so = frappe.db.sql( SalesOrder = frappe.qb.DocType("Sales Order")
""" so_query = (
select sum(base_grand_total*(100 - per_billed)/100) frappe.qb.from_(SalesOrder)
from `tabSales Order` .select(Sum(SalesOrder.base_grand_total * (100 - SalesOrder.per_billed) / 100))
where customer=%s and docstatus = 1 and company=%s .where(SalesOrder.customer == customer)
and per_billed < 100 and status != 'Closed'""", .where(SalesOrder.company == company)
(customer, company), .where(SalesOrder.docstatus == 1)
.where(SalesOrder.per_billed < 100)
.where(SalesOrder.status != "Closed")
) )
so_res = so_query.run()
outstanding_based_on_so = flt(so_res[0][0]) if so_res and so_res[0][0] is not None else 0.0
outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0 DeliveryNote = frappe.qb.DocType("Delivery Note")
DeliveryNoteItem = frappe.qb.DocType("Delivery Note Item")
SalesInvoiceItem = frappe.qb.DocType("Sales Invoice Item")
# Outstanding based on Delivery Note, which are not created against Sales Order si_subquery = (
outstanding_based_on_dn = 0 frappe.qb.from_(SalesInvoiceItem)
.select(SalesInvoiceItem.dn_detail, Sum(SalesInvoiceItem.amount).as_("billed_amount"))
unmarked_delivery_note_items = frappe.db.sql( .where(SalesInvoiceItem.docstatus == 1)
"""select .groupby(SalesInvoiceItem.dn_detail)
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.against_sales_order, '') = ''
and ifnull(dn_item.against_sales_invoice, '') = ''
""",
(customer, company),
as_dict=True,
) )
if not unmarked_delivery_note_items: dn_query = (
return outstanding_based_on_gle + outstanding_based_on_so frappe.qb.from_(DeliveryNote)
.join(DeliveryNoteItem)
si_amounts = frappe.db.sql( .on(DeliveryNote.name == DeliveryNoteItem.parent)
""" .left_join(si_subquery)
SELECT .on(DeliveryNoteItem.name == si_subquery.dn_detail)
dn_detail, sum(amount) from `tabSales Invoice Item` .select(
WHERE Sum(
docstatus = 1 (
and dn_detail in ({}) (DeliveryNoteItem.amount - IfNull(si_subquery.billed_amount, 0.0))
GROUP BY dn_detail""".format( / DeliveryNote.base_net_total
", ".join(frappe.db.escape(dn_item.name) for dn_item in unmarked_delivery_note_items) )
* DeliveryNote.base_grand_total
)
)
.where(DeliveryNote.customer == customer)
.where(DeliveryNote.company == company)
.where(DeliveryNote.docstatus == 1)
.where(DeliveryNote.base_net_total > 0)
.where(DeliveryNote.status.notin(["Closed", "Stopped"]))
.where(DeliveryNoteItem.amount > IfNull(si_subquery.billed_amount, 0.0))
.where(
Criterion.any(
[DeliveryNoteItem.against_sales_order.isnull(), DeliveryNoteItem.against_sales_order == ""]
)
)
.where(
Criterion.any(
[
DeliveryNoteItem.against_sales_invoice.isnull(),
DeliveryNoteItem.against_sales_invoice == "",
]
)
) )
) )
si_amounts = {si_item[0]: si_item[1] for si_item in si_amounts} dn_res = dn_query.run()
outstanding_based_on_dn = flt(dn_res[0][0]) if dn_res and dn_res[0][0] is not None else 0.0
for dn_item in unmarked_delivery_note_items:
dn_amount = flt(dn_item.amount)
si_amount = flt(si_amounts.get(dn_item.name))
if dn_amount > si_amount and dn_item.base_net_total:
outstanding_based_on_dn += (
(dn_amount - 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 return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn