From dc5faa8b7107e6edec59188f50c854b7013d151e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 21 Dec 2025 21:37:13 +0530 Subject: [PATCH 1/3] fix: same serial number was picked in multiple sales invoices (cherry picked from commit 61c31f0cd053c993101d3287e91993ed93faa6cd) # Conflicts: # erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py --- .../serial_and_batch_bundle.py | 75 +++++++++++++++++-- 1 file changed, 68 insertions(+), 7 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 e20363cec3a..9cf921b8646 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 @@ -1989,8 +1989,35 @@ def get_available_serial_nos(kwargs): if kwargs.warehouse: filters["warehouse"] = kwargs.warehouse +<<<<<<< HEAD # Since SLEs are not present against Reserved Stock [POS invoices, SRE], need to ignore reserved serial nos. ignore_serial_nos = get_reserved_serial_nos(kwargs) +======= + reserved_entries = get_reserved_serial_nos_for_sre(kwargs) + + ignore_serial_nos = [] + if reserved_entries: + if kwargs.get("sabb_voucher_type") == "Delivery Note" and kwargs.get("against_sales_order"): + reserved_voucher_details = [kwargs.get("against_sales_order")] + else: + reserved_voucher_details = get_reserved_voucher_details(kwargs) + + # Check if serial nos are reserved for the current voucher then fetch only those serial nos + if reserved_serial_nos := get_reserved_serial_nos_for_voucher( + kwargs, reserved_entries, reserved_voucher_details + ): + filters["name"] = ("in", reserved_serial_nos) + return get_serial_nos_based_on_filters(filters, fields, order_by, kwargs) + + # Check if serial nos are reserved for other vouchers then ignore those serial nos + elif ignore_reserved_serial_nos := get_other_doc_reserved_serials( + kwargs, reserved_entries, reserved_voucher_details + ): + ignore_serial_nos.extend(ignore_reserved_serial_nos) + + if reserved_for_pos := get_reserved_serial_nos_for_pos(kwargs): + ignore_serial_nos.extend(reserved_for_pos) +>>>>>>> 61c31f0cd0 (fix: same serial number was picked in multiple sales invoices) # To ignore serial nos in the same record for the draft state if kwargs.get("ignore_serial_nos"): @@ -2018,13 +2045,47 @@ def get_available_serial_nos(kwargs): filters["batch_no"] = ("in", batches) - return frappe.get_all( - "Serial No", - fields=fields, - filters=filters, - limit=cint(kwargs.qty) or 10000000, - order_by=order_by, - ) + return get_serial_nos_based_on_filters(filters, fields, order_by, kwargs) + + +def get_serial_nos_based_on_filters(filters, fields, order_by, kwargs): + doctype = frappe.qb.DocType("Serial No") + + order_by_column = getattr(doctype, order_by) + query = frappe.qb.from_(doctype).orderby(order_by_column).limit(cint(kwargs.qty) or 10000000).for_update() + + for key, value in filters.items(): + column = getattr(doctype, key) + + if isinstance(value, tuple): + operator = value[0] + + if operator == "between": + query = query.where(column.between(value[1], value[2])) + + elif operator == "in": + query = query.where(column.isin(value[1])) + + elif operator == "not in": + query = query.where(column.notin(value[1])) + + elif operator == "is": + if value[1] == "set": + query = query.where(column.isnotnull()) + elif value[1] == "not set": + query = query.where(column.isnull()) + else: + query = query.where(column == value) + + for field in fields: + if " as " in field.lower(): + # Split field and alias + field_name, alias = field.split(" as ", 1) + query = query.select(getattr(doctype, field_name).as_(alias)) + else: + query = query.select(getattr(doctype, field)) + + return query.run(as_dict=True) def get_non_expired_batches(batches): From c77c4266525c714dff47a26650c535f50ec1bef0 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 22 Dec 2025 11:37:19 +0530 Subject: [PATCH 2/3] chore: fix conflicts Removed logic for handling reserved serial numbers in sales invoices. --- .../serial_and_batch_bundle.py | 27 ------------------- 1 file changed, 27 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 9cf921b8646..bb68468619b 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 @@ -1989,35 +1989,8 @@ def get_available_serial_nos(kwargs): if kwargs.warehouse: filters["warehouse"] = kwargs.warehouse -<<<<<<< HEAD # Since SLEs are not present against Reserved Stock [POS invoices, SRE], need to ignore reserved serial nos. ignore_serial_nos = get_reserved_serial_nos(kwargs) -======= - reserved_entries = get_reserved_serial_nos_for_sre(kwargs) - - ignore_serial_nos = [] - if reserved_entries: - if kwargs.get("sabb_voucher_type") == "Delivery Note" and kwargs.get("against_sales_order"): - reserved_voucher_details = [kwargs.get("against_sales_order")] - else: - reserved_voucher_details = get_reserved_voucher_details(kwargs) - - # Check if serial nos are reserved for the current voucher then fetch only those serial nos - if reserved_serial_nos := get_reserved_serial_nos_for_voucher( - kwargs, reserved_entries, reserved_voucher_details - ): - filters["name"] = ("in", reserved_serial_nos) - return get_serial_nos_based_on_filters(filters, fields, order_by, kwargs) - - # Check if serial nos are reserved for other vouchers then ignore those serial nos - elif ignore_reserved_serial_nos := get_other_doc_reserved_serials( - kwargs, reserved_entries, reserved_voucher_details - ): - ignore_serial_nos.extend(ignore_reserved_serial_nos) - - if reserved_for_pos := get_reserved_serial_nos_for_pos(kwargs): - ignore_serial_nos.extend(reserved_for_pos) ->>>>>>> 61c31f0cd0 (fix: same serial number was picked in multiple sales invoices) # To ignore serial nos in the same record for the draft state if kwargs.get("ignore_serial_nos"): From 73643de61256651e12d545dfef3587ee978f57b2 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 22 Dec 2025 12:02:38 +0530 Subject: [PATCH 3/3] fix: added limit --- erpnext/stock/get_item_details.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 8e71367c663..b0ade4324fa 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -204,6 +204,7 @@ def update_stock(ctx, out, doc=None): "item_code": ctx.item_code, "warehouse": ctx.warehouse, "based_on": frappe.db.get_single_value("Stock Settings", "pick_serial_and_batch_based_on"), + "qty": out.stock_qty, } )