From 047014f2b569a8fbcf9ede9bd6c93d21830d1392 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 26 Jun 2026 10:19:38 +0530 Subject: [PATCH 1/3] test: add coverage for Incorrect Serial and Batch Bundle report Co-Authored-By: Claude Opus 4.8 (1M context) --- .../test_incorrect_serial_and_batch_bundle.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 erpnext/stock/report/incorrect_serial_and_batch_bundle/test_incorrect_serial_and_batch_bundle.py diff --git a/erpnext/stock/report/incorrect_serial_and_batch_bundle/test_incorrect_serial_and_batch_bundle.py b/erpnext/stock/report/incorrect_serial_and_batch_bundle/test_incorrect_serial_and_batch_bundle.py new file mode 100644 index 00000000000..e0bca85a1b4 --- /dev/null +++ b/erpnext/stock/report/incorrect_serial_and_batch_bundle/test_incorrect_serial_and_batch_bundle.py @@ -0,0 +1,64 @@ +# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import frappe + +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry +from erpnext.stock.report.incorrect_serial_and_batch_bundle.incorrect_serial_and_batch_bundle import ( + execute, +) +from erpnext.tests.utils import ERPNextTestSuite + + +class TestIncorrectSerialAndBatchBundle(ERPNextTestSuite): + def run_report(self, **extra): + filters = frappe._dict({"company": "_Test Company"}) + filters.update(extra) + return execute(filters)[1] + + def test_healthy_bundles_not_flagged(self): + batch_item = make_item( + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "ISBB-.#####", + } + ).name + serial_item = make_item( + properties={ + "is_stock_item": 1, + "has_serial_no": 1, + "serial_no_series": "ISBS-.#####", + } + ).name + + make_stock_entry( + item_code=batch_item, + qty=10, + rate=100, + to_warehouse="_Test Warehouse - _TC", + posting_date="2026-06-01", + ) + make_stock_entry( + item_code=serial_item, + qty=3, + rate=100, + to_warehouse="_Test Warehouse - _TC", + posting_date="2026-06-01", + ) + + data = self.run_report() + + bundles = frappe.get_all( + "Serial and Batch Bundle", + filters={"item_code": ["in", [batch_item, serial_item]]}, + pluck="name", + ) + + flagged_names = {row.get("name") for row in data} + self.assertFalse( + flagged_names.intersection(bundles), + msg="Healthy serial/batch bundles should not be flagged as incorrect.", + ) From 851dfb16be9bb5e63250f7dbd0fc33f34d800530 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 26 Jun 2026 13:26:48 +0530 Subject: [PATCH 2/3] test: reuse BootStrapTestData master data to reduce runtime Co-Authored-By: Claude Opus 4.8 (1M context) --- .../test_incorrect_serial_and_batch_bundle.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/report/incorrect_serial_and_batch_bundle/test_incorrect_serial_and_batch_bundle.py b/erpnext/stock/report/incorrect_serial_and_batch_bundle/test_incorrect_serial_and_batch_bundle.py index e0bca85a1b4..806a8d24ad1 100644 --- a/erpnext/stock/report/incorrect_serial_and_batch_bundle/test_incorrect_serial_and_batch_bundle.py +++ b/erpnext/stock/report/incorrect_serial_and_batch_bundle/test_incorrect_serial_and_batch_bundle.py @@ -26,26 +26,20 @@ class TestIncorrectSerialAndBatchBundle(ERPNextTestSuite): "batch_number_series": "ISBB-.#####", } ).name - serial_item = make_item( - properties={ - "is_stock_item": 1, - "has_serial_no": 1, - "serial_no_series": "ISBS-.#####", - } - ).name + serial_item = "_Test Serialized Item With Series" make_stock_entry( item_code=batch_item, qty=10, rate=100, - to_warehouse="_Test Warehouse - _TC", + to_warehouse="Stores - _TC", posting_date="2026-06-01", ) make_stock_entry( item_code=serial_item, qty=3, rate=100, - to_warehouse="_Test Warehouse - _TC", + to_warehouse="Stores - _TC", posting_date="2026-06-01", ) From beb2974317f5355783c2eab4f1076994371a7df0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 1 Jul 2026 19:25:54 +0530 Subject: [PATCH 3/3] test: flag an unlinked (orphan) serial and batch bundle --- .../test_incorrect_serial_and_batch_bundle.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/erpnext/stock/report/incorrect_serial_and_batch_bundle/test_incorrect_serial_and_batch_bundle.py b/erpnext/stock/report/incorrect_serial_and_batch_bundle/test_incorrect_serial_and_batch_bundle.py index 806a8d24ad1..86f32077bea 100644 --- a/erpnext/stock/report/incorrect_serial_and_batch_bundle/test_incorrect_serial_and_batch_bundle.py +++ b/erpnext/stock/report/incorrect_serial_and_batch_bundle/test_incorrect_serial_and_batch_bundle.py @@ -56,3 +56,28 @@ class TestIncorrectSerialAndBatchBundle(ERPNextTestSuite): flagged_names.intersection(bundles), msg="Healthy serial/batch bundles should not be flagged as incorrect.", ) + + def test_unlinked_bundle_is_flagged(self): + # an actual incorrect state: a submitted Serial and Batch Bundle left without any linking + # Stock Ledger Entry (e.g. the SLE was purged but the bundle survived) + batch_item = make_item( + properties={ + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "ISBB-ORPHAN-.#####", + } + ).name + + entry = make_stock_entry( + item_code=batch_item, qty=5, rate=100, to_warehouse="Stores - _TC", posting_date="2026-06-01" + ) + bundle = frappe.db.get_value("Serial and Batch Bundle", {"voucher_no": entry.name}, "name") + self.assertTrue(bundle) + + # orphan the bundle: drop the Stock Ledger Entry that referenced it + frappe.db.delete("Stock Ledger Entry", {"serial_and_batch_bundle": bundle}) + + flagged = {row.get("name"): row for row in self.run_report()} + self.assertIn(bundle, flagged) + self.assertEqual(flagged[bundle]["is_cancelled"], 0)