From a638c9c8afeacdc3ea54bca47c52100130aea962 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 24 Mar 2024 17:44:37 +0530 Subject: [PATCH] fix: Batch No is mandatory while making manufacture entry (cherry picked from commit 43fd60a8779a54124ec275c14eb120fa3ac44394) --- .../doctype/work_order/test_work_order.py | 46 +++++++++++++++++++ .../doctype/work_order/work_order.py | 17 +++++++ .../stock/doctype/stock_entry/stock_entry.js | 4 +- .../stock/doctype/stock_entry/stock_entry.py | 1 + 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 309408992d4..d4180a0eb1f 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -1207,6 +1207,51 @@ class TestWorkOrder(FrappeTestCase): except frappe.MandatoryError: self.fail("Batch generation causing failing in Work Order") + @change_settings("Manufacturing Settings", {"make_serial_no_batch_from_work_order": 1}) + def test_auto_serial_no_batch_creation(self): + from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom + + fg_item = frappe.generate_hash(length=20) + child_item = frappe.generate_hash(length=20) + + bom_tree = {fg_item: {child_item: {}}} + + create_nested_bom(bom_tree, prefix="") + + item = frappe.get_doc("Item", fg_item) + item.update( + { + "has_serial_no": 1, + "has_batch_no": 1, + "serial_no_series": f"SN-TEST-{item.name}.#####", + "create_new_batch": 1, + "batch_number_series": f"BATCH-TEST-{item.name}.#####", + } + ) + item.save() + + try: + wo_order = make_wo_order_test_record(item=fg_item, batch_size=5, qty=10, skip_transfer=True) + serial_nos = self.get_serial_nos_for_fg(wo_order.name) + + stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10)) + stock_entry.set_work_order_details() + stock_entry.set_serial_no_batch_for_finished_good() + for row in stock_entry.items: + if row.item_code == fg_item: + self.assertTrue(row.serial_and_batch_bundle) + self.assertEqual( + sorted(get_serial_nos_from_bundle(row.serial_and_batch_bundle)), sorted(serial_nos) + ) + + sn_doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) + for row in sn_doc.entries: + self.assertTrue(row.serial_no) + self.assertTrue(row.batch_no) + + except frappe.MandatoryError: + self.fail("Batch generation causing failing in Work Order") + def get_serial_nos_for_fg(self, work_order): serial_nos = [] for row in frappe.get_all("Serial No", filters={"work_order": work_order}): @@ -2269,6 +2314,7 @@ def make_wo_order_test_record(**args): wo_order.planned_start_date = args.planned_start_date or now() wo_order.transfer_material_against = args.transfer_material_against or "Work Order" wo_order.from_wip_warehouse = args.from_wip_warehouse or 0 + wo_order.batch_size = args.batch_size or 0 if args.source_warehouse: for item in wo_order.get("required_items"): diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 024a631ab11..e4d09bd42b8 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -536,6 +536,12 @@ class WorkOrder(Document): "Item", self.production_item, ["serial_no_series", "item_name", "description"], as_dict=1 ) + batches = [] + if self.has_batch_no: + batches = frappe.get_all( + "Batch", filters={"reference_name": self.name}, order_by="creation", pluck="name" + ) + serial_nos = [] if item_details.serial_no_series: serial_nos = get_available_serial_nos(item_details.serial_no_series, self.qty) @@ -556,10 +562,20 @@ class WorkOrder(Document): "description", "status", "work_order", + "batch_no", ] serial_nos_details = [] + index = 0 for serial_no in serial_nos: + index += 1 + batch_no = None + if batches and self.batch_size: + batch_no = batches[0] + + if index % self.batch_size == 0: + batches.remove(batch_no) + serial_nos_details.append( ( serial_no, @@ -574,6 +590,7 @@ class WorkOrder(Document): item_details.description, "Inactive", self.name, + batch_no, ) ) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index ac2fe5814d3..96a92096437 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -1081,7 +1081,9 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle cint(frappe.user_defaults?.use_serial_batch_fields) === 1 ) { this.frm.doc.items.forEach((item) => { - frappe.model.set_value(item.doctype, item.name, "use_serial_batch_fields", 1); + if (!item.serial_and_batch_bundle) { + frappe.model.set_value(item.doctype, item.name, "use_serial_batch_fields", 1); + } }); } } diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index e4c9ded2cc4..e6214290fda 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -2536,6 +2536,7 @@ class StockEntry(StockController): ) d.serial_and_batch_bundle = id + d.use_serial_batch_fields = 0 def get_available_serial_nos(self) -> List[str]: serial_nos = []