From 4544a6c935818cb1f6f25ad52bef0654ebfb1736 Mon Sep 17 00:00:00 2001 From: Afsal Syed Date: Mon, 13 Jul 2026 19:01:00 +0530 Subject: [PATCH 1/2] fix(stock): fix sqlparse token limit in get_bundle_wise_serial_nos --- .../serial_and_batch_bundle.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 57b8c4cee93..fe671b32801 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -2615,22 +2615,24 @@ def get_serial_nos_based_on_posting_date(kwargs, ignore_serial_nos): def get_bundle_wise_serial_nos(data, kwargs): bundle_wise_serial_nos = defaultdict(list) - bundles = [d.serial_and_batch_bundle for d in data if d.serial_and_batch_bundle] + bundles = list({d.serial_and_batch_bundle for d in data if d.serial_and_batch_bundle}) if not bundles: return bundle_wise_serial_nos - filters = {"parent": ("in", bundles), "docstatus": 1, "serial_no": ("is", "set")} - - if kwargs.get("check_serial_nos") and kwargs.get("serial_nos"): - filters["serial_no"] = ("in", kwargs.get("serial_nos")) - - bundle_data = frappe.get_all( - "Serial and Batch Entry", - fields=["serial_no", "parent"], - filters=filters, + sabe = frappe.qb.DocType("Serial and Batch Entry") + query = ( + frappe.qb.from_(sabe) + .select(sabe.serial_no, sabe.parent) + .where(sabe.parent.isin(bundles)) + .where(sabe.docstatus == 1) + .where(sabe.serial_no.isnotnull()) + .where(sabe.serial_no != "") ) - for d in bundle_data: + if kwargs.get("check_serial_nos") and kwargs.get("serial_nos"): + query = query.where(sabe.serial_no.isin(kwargs.get("serial_nos"))) + + for d in query.run(as_dict=True): if d.parent: bundle_wise_serial_nos[d.parent].append(d.serial_no) From e748bf512b3d3f430d19c9c9ed3c4fef93df6f76 Mon Sep 17 00:00:00 2001 From: Afsal Syed Date: Mon, 13 Jul 2026 19:01:50 +0530 Subject: [PATCH 2/2] test(stock): add unit test for get_bundle_wise_serial_nos query --- .../test_serial_and_batch_bundle.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py index a491c805aa3..9110394444d 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py @@ -1603,3 +1603,37 @@ class TestSerialandBatchBundleLogic(ERPNextTestSuite): serialized.append("entries", {"qty": 5}) serialized.calculate_total_qty(save=False) self.assertEqual(serialized.total_qty, 1) + + def test_get_bundle_wise_serial_nos(self): + from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import ( + get_bundle_wise_serial_nos, + ) + + item_code = make_item(properties={"has_serial_no": 1, "serial_no_series": "TEST-BWSN-.#####"}).name + + bundles = [] + for _ in range(2): + se = make_stock_entry( + item_code=item_code, + target="_Test Warehouse - _TC", + qty=3, + rate=100, + ) + bundles.append(se.items[0].serial_and_batch_bundle) + + data = [frappe._dict(serial_and_batch_bundle=bundle) for bundle in bundles] + + self.assertEqual(get_bundle_wise_serial_nos([], {}), {}) + + bundle_wise_serial_nos = get_bundle_wise_serial_nos(data, {}) + for bundle in bundles: + self.assertEqual(sorted(bundle_wise_serial_nos[bundle]), get_serial_nos_from_bundle(bundle)) + + # check_serial_nos must restrict the result to the requested serial nos + serial_no = get_serial_nos_from_bundle(bundles[0])[0] + bundle_wise_serial_nos = get_bundle_wise_serial_nos( + data, {"check_serial_nos": True, "serial_nos": [serial_no]} + ) + + self.assertNotIn(bundles[1], bundle_wise_serial_nos) + self.assertEqual(bundle_wise_serial_nos[bundles[0]], [serial_no])