perf(selling): batch billed quantity lookup

This commit is contained in:
Mihir Kandoi
2026-08-11 12:27:22 +05:30
parent 114ba42850
commit dd23cf40e1
2 changed files with 49 additions and 9 deletions

View File

@@ -441,25 +441,35 @@ def make_sales_invoice(
# 0 qty is accepted, as the qty is uncertain for some items
has_unit_price_items = frappe.db.get_value("Sales Order", source_name, "has_unit_price_items")
billed_qty_by_item = None
pending_qty_by_item = {}
def is_unit_price_row(source):
return has_unit_price_items and source.qty == 0
def get_billed_qty(so_item_name):
table = frappe.qb.DocType("Sales Invoice Item")
query = (
frappe.qb.from_(table)
.select(Sum(table.qty).as_("qty"))
.where((table.docstatus == 1) & (table.so_detail == so_item_name))
)
return flt(query.run(pluck="qty")[0])
def get_billed_qty_by_item():
nonlocal billed_qty_by_item
if billed_qty_by_item is None:
invoice_item = frappe.qb.DocType("Sales Invoice Item")
sales_order_item = frappe.qb.DocType("Sales Order Item")
rows = (
frappe.qb.from_(invoice_item)
.inner_join(sales_order_item)
.on(invoice_item.so_detail == sales_order_item.name)
.select(invoice_item.so_detail, Sum(invoice_item.qty).as_("qty"))
.where((invoice_item.docstatus == 1) & (sales_order_item.parent == source_name))
.groupby(invoice_item.so_detail)
).run(as_dict=True)
billed_qty_by_item = {row.so_detail: flt(row.qty) for row in rows}
return billed_qty_by_item
def get_pending_qty(source):
if source.name not in pending_qty_by_item:
billable_qty = get_qty_net_of_returns(source)
if source.qty and source.billed_amt:
billable_qty -= get_billed_qty(source.name)
billable_qty -= get_billed_qty_by_item().get(source.name, 0)
pending_qty_by_item[source.name] = max(flt(billable_qty), 0)

View File

@@ -347,6 +347,36 @@ class TestSalesOrder(ERPNextTestSuite):
so.load_from_db()
self.assertEqual(so.get("items")[0].billed_amt, 500)
def test_make_sales_invoice_after_partial_billing_multiple_items(self):
so = make_sales_order(
item_list=[
{
"item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC",
"qty": 10,
"rate": 100,
},
{
"item_code": "_Test FG Item",
"warehouse": "_Test Warehouse - _TC",
"qty": 10,
"rate": 100,
},
]
)
si = make_sales_invoice(so.name)
si.get("items")[0].qty = 4
si.get("items")[1].qty = 6
si.insert()
si.submit()
pending_invoice = make_sales_invoice(so.name)
self.assertEqual(
{item.so_detail: item.qty for item in pending_invoice.get("items")},
{so.get("items")[0].name: 6, so.get("items")[1].name: 4},
)
def test_so_billed_amount_against_return_entry(self):
from erpnext.accounts.doctype.sales_invoice.mapper import make_sales_return