From f2a53247c5ccfcd26470c05f97cc740211a266f9 Mon Sep 17 00:00:00 2001 From: R-Jayaraman Date: Fri, 31 Jul 2026 12:01:39 +0530 Subject: [PATCH] test(sales): add coverage for zero-qty return rejection Greptile flagged that the sales-side zero-qty-return fix had no dedicated test proving the behavior - the existing suite happened to pass, but nothing specifically asserted that an all-zero return is rejected while a normal negative-qty return still succeeds. Adds two tests covering the doctypes that rely entirely on this check (no other guard covers them for a non-stock-effect return): - Delivery Note return with qty 0 -> rejected - Sales Invoice return with qty 0 (no update_stock) -> rejected POS Invoice is not covered separately here since it always runs with update_stock=1, which is already guarded by the pre-existing validate_zero_qty_for_return_invoices_with_stock check regardless of this fix. (cherry picked from commit 732c884633acc8ed5862cded0f18f725457a6439) --- .../tests/test_sales_and_purchase_return.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/erpnext/controllers/tests/test_sales_and_purchase_return.py b/erpnext/controllers/tests/test_sales_and_purchase_return.py index 5a8a07b7502..0448faf3817 100644 --- a/erpnext/controllers/tests/test_sales_and_purchase_return.py +++ b/erpnext/controllers/tests/test_sales_and_purchase_return.py @@ -56,3 +56,35 @@ class TestSalesAndPurchaseReturn(ERPNextTestSuite): return_pi.items[0].item_code = "" self.assertRaises(frappe.ValidationError, return_pi.save) + + def test_delivery_note_zero_qty_return_is_rejected(self): + # A return with every item at qty 0 moves no stock and no value, so it must be + # rejected the same way a return with no items at all would be. + from erpnext.stock.doctype.delivery_note.mapper import make_sales_return + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry + + se = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=20, basic_rate=100) + self.addCleanup(self._cancel_and_delete, "Stock Entry", se.name) + + dn = create_delivery_note(qty=5) + self.addCleanup(self._cancel_and_delete, "Delivery Note", dn.name) + + return_dn = make_sales_return(dn.name) + return_dn.items[0].qty = 0 + + self.assertRaises(frappe.ValidationError, return_dn.insert) + + def test_sales_invoice_zero_qty_return_is_rejected(self): + # Same rule for a standalone (non stock-affecting) Sales Invoice return: qty 0 on + # every row must be rejected, not silently accepted as a no-op credit note. + from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice + from erpnext.controllers.sales_and_purchase_return import make_return_doc + + si = create_sales_invoice(qty=10) + self.addCleanup(self._cancel_and_delete, "Sales Invoice", si.name) + + return_si = make_return_doc(si.doctype, si.name) + return_si.items[0].qty = 0 + + self.assertRaises(frappe.ValidationError, return_si.save)