From c613a7f7f0444c03680ad8f27466a90a30d8b087 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Mon, 7 Sep 2026 19:27:00 +0530 Subject: [PATCH] fix(selling): subtract billed qty for zero-amount Sales Order rows (#58816) --- erpnext/selling/doctype/sales_order/mapper.py | 4 +--- .../selling/doctype/sales_order/test_sales_order.py | 13 +++++++++++++ .../test_subcontracting_inward_order.py | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/mapper.py b/erpnext/selling/doctype/sales_order/mapper.py index 86d5fcdda61..c84d873f993 100644 --- a/erpnext/selling/doctype/sales_order/mapper.py +++ b/erpnext/selling/doctype/sales_order/mapper.py @@ -493,9 +493,7 @@ def make_sales_invoice( 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_by_item().get(source.name, 0) - + billable_qty -= get_billed_qty_by_item().get(source.name, 0) billable_qty -= mapped_qty_by_item.get(source.name, 0) pending_qty_by_item[source.name] = max(flt(billable_qty, source.precision("qty")), 0) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index c65b8774322..9d9160cb8b4 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -427,6 +427,19 @@ class TestSalesOrder(ERPNextTestSuite): self.assertEqual(len(si.get("items")), 1) self.assertEqual(si.get("items")[0].qty, 150) + def test_make_sales_invoice_skips_fully_invoiced_free_item(self): + free_item = make_item("_Test Free Item", {"is_stock_item": 1}).name + so = make_sales_order(qty=10, rate=100, do_not_submit=True) + so.append("items", {"item_code": free_item, "qty": 5, "rate": 0, "warehouse": so.items[0].warehouse}) + so.submit() + + si = make_sales_invoice(so.name) + self.assertEqual([row.qty for row in si.items], [10, 5]) + si.insert() + si.submit() + + self.assertEqual(len(make_sales_invoice(so.name).items), 0) + def test_make_sales_invoice_after_return_and_redelivery(self): from erpnext.stock.doctype.delivery_note.mapper import make_sales_return diff --git a/erpnext/subcontracting/doctype/subcontracting_inward_order/test_subcontracting_inward_order.py b/erpnext/subcontracting/doctype/subcontracting_inward_order/test_subcontracting_inward_order.py index b7c03df67ff..2713f3a262c 100644 --- a/erpnext/subcontracting/doctype/subcontracting_inward_order/test_subcontracting_inward_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_inward_order/test_subcontracting_inward_order.py @@ -455,7 +455,7 @@ class IntegrationTestSubcontractingInwardOrder(ERPNextTestSuite): scio.reload() si = make_sales_invoice(so.name) - self.assertEqual(len(si.items), 1) + self.assertEqual(len(si.items), 0) def test_extra_items_reservation_transfer(self): so, scio = create_so_scio()