diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index db804245e31..bca73736922 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -839,7 +839,7 @@ class SellingController(StockController): sre_doc = frappe.get_doc("Stock Reservation Entry", sre) qty_can_be_deliver = 0 - if sre_doc.reservation_based_on == "Serial and Batch" and item.serial_and_batch_bundle: + if sre_doc.reservation_based_on == "Serial and Batch": sbb = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle) if sre_doc.has_serial_no: delivered_serial_nos = [d.serial_no for d in sbb.entries] diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index f0e9d33f700..71901d82919 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -1100,7 +1100,13 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None): dn_item.qty = flt(sre.reserved_qty) / flt(dn_item.get("conversion_factor", 1)) dn_item.warehouse = sre.warehouse - if sre.reservation_based_on == "Serial and Batch" and (sre.has_serial_no or sre.has_batch_no): + use_serial_batch_fields = frappe.get_single_value("Stock Settings", "use_serial_batch_fields") + + if ( + not use_serial_batch_fields + and sre.reservation_based_on == "Serial and Batch" + and (sre.has_serial_no or sre.has_batch_no) + ): dn_item.serial_and_batch_bundle = get_ssb_bundle_for_voucher(sre) target_doc.append("items", dn_item) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index fceca989fbb..cc1ab8e04cc 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -444,8 +444,6 @@ class DeliveryNote(SellingController): self.update_prevdoc_status() self.update_billing_status() - self.update_stock_reservation_entries() - if not self.is_return: self.check_credit_limit() elif self.issue_credit_note: @@ -458,6 +456,8 @@ class DeliveryNote(SellingController): self.make_bundle_for_sales_purchase_return(table_name) self.make_bundle_using_old_serial_batch_fields(table_name) + self.update_stock_reservation_entries() + # Updating stock ledger should always be called after updating prevdoc status, # because updating reserved qty in bin depends upon updated delivered qty in SO self.update_stock_ledger() 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 7c6a95ffc83..de93e1eac70 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 @@ -1802,7 +1802,10 @@ def get_available_serial_nos(kwargs): filters["warehouse"] = kwargs.warehouse # 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) + ignore_serial_nos, consider_serial_nos = get_reserved_serial_nos(kwargs) + + if consider_serial_nos: + filters["name"] = ("in", consider_serial_nos) # To ignore serial nos in the same record for the draft state if kwargs.get("ignore_serial_nos"): @@ -1911,19 +1914,23 @@ def get_reserved_serial_nos(kwargs) -> list: """Returns a list of `Serial No` reserved in POS Invoice and Stock Reservation Entry.""" ignore_serial_nos = [] + consider_serial_nos = [] # Extend the list by serial nos reserved in POS Invoice ignore_serial_nos.extend(get_reserved_serial_nos_for_pos(kwargs)) reserved_entries = get_reserved_serial_nos_for_sre(kwargs) if not reserved_entries: - return ignore_serial_nos + return ignore_serial_nos, consider_serial_nos reserved_voucher_details = get_reserved_voucher_details(kwargs) + if not reserved_voucher_details: + return ignore_serial_nos, consider_serial_nos serial_nos = [] for entry in reserved_entries: if entry.voucher_no in reserved_voucher_details: + consider_serial_nos.append(entry.serial_no) continue if kwargs.get("serial_nos") and entry.serial_no in kwargs.get("serial_nos"): @@ -1939,7 +1946,7 @@ def get_reserved_serial_nos(kwargs) -> list: # Extend the list by serial nos reserved via SRE ignore_serial_nos.extend(serial_nos) - return ignore_serial_nos + return ignore_serial_nos, consider_serial_nos def get_reserved_voucher_details(kwargs): @@ -1958,15 +1965,15 @@ def get_reserved_voucher_details(kwargs): "Delivery Note": { "name": kwargs.get("sabb_voucher_detail_no"), "parent": kwargs.get("sabb_voucher_no"), - "docstatus": 1, + "docstatus": ("<", 2), }, "Stock Entry": { "name": kwargs.get("sabb_voucher_no"), - "docstatus": 1, + "docstatus": ("<", 2), }, "Work Order": { "name": kwargs.get("sabb_voucher_no"), - "docstatus": 1, + "docstatus": ("<", 2), }, }.get(kwargs.get("sabb_voucher_type")) diff --git a/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py index 7c17800555a..573da6a53c6 100644 --- a/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py +++ b/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py @@ -5,7 +5,7 @@ from random import randint import frappe from frappe.tests import IntegrationTestCase -from frappe.utils import today +from frappe.utils import cint, today from erpnext.selling.doctype.sales_order.sales_order import create_pick_list, make_delivery_note from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order @@ -250,6 +250,8 @@ class TestStockReservationEntry(IntegrationTestCase): }, ) def test_stock_reservation_against_sales_order(self) -> None: + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + items_details = create_items() se = create_material_receipt(items_details, self.warehouse, qty=10) @@ -326,16 +328,29 @@ class TestStockReservationEntry(IntegrationTestCase): so = make_sales_order( item_list=item_list, warehouse=self.warehouse, + do_not_submit=True, ) + + for row in so.items: + row.qty = 80 + + so.save() + so.submit() so.create_stock_reservation_entries() # Test - 7: Partial Delivery against Sales Order. dn1 = make_delivery_note(so.name) + item_wise_serial_nos = {} + for item in dn1.items: - item.qty = randint(1, 10) + item.qty = 10 dn1.save() + for row in dn1.items: + if row.serial_no: + item_wise_serial_nos.setdefault(row.item_code, []).extend(get_serial_nos(row.serial_no)) + dn1.submit() for item in so.items: @@ -350,9 +365,17 @@ class TestStockReservationEntry(IntegrationTestCase): dn2 = make_delivery_note(so.name) for item in dn2.items: - item.qty += randint(1, 10) + item.qty = 80 dn2.save() + for row in dn2.items: + if row.item_code in item_wise_serial_nos: + consumed_serial_no = ", ".join(item_wise_serial_nos[row.item_code]) + picked_serial_no = get_serial_nos(row.serial_no) + + serial_nos = list(set(picked_serial_no) - set(consumed_serial_no)) + row.serial_no = "\n".join(serial_nos) + dn2.submit() for item in so.items: diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 88faa841586..62022cab7a0 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -202,6 +202,9 @@ def update_stock(ctx, out, doc=None): "item_code": ctx.item_code, "warehouse": ctx.warehouse, "based_on": frappe.get_single_value("Stock Settings", "pick_serial_and_batch_based_on"), + "sabb_voucher_no": doc.get("name"), + "sabb_voucher_detail_no": ctx.child_docname, + "sabb_voucher_type": ctx.doctype, } )