fix: batch no not copied while making Material Consumption entry (#40290)
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index c72232a..e1c1069 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -1383,8 +1383,9 @@
# Inward raw materials in Stores warehouse
ste_doc.submit()
+ ste_doc.reload()
- serial_nos_list = sorted(get_serial_nos(ste_doc.items[0].serial_no))
+ serial_nos_list = sorted(get_serial_nos_from_bundle(ste_doc.items[0].serial_and_batch_bundle))
wo_doc = make_wo_order_test_record(production_item=fg_item, qty=4)
transferred_ste_doc = frappe.get_doc(
@@ -1396,19 +1397,22 @@
# First Manufacture stock entry
manufacture_ste_doc1 = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 1))
+ manufacture_ste_doc1.submit()
+ manufacture_ste_doc1.reload()
# Serial nos should be same as transferred Serial nos
- self.assertEqual(get_serial_nos(manufacture_ste_doc1.items[0].serial_no), serial_nos_list[0:1])
+ self.assertEqual(
+ sorted(get_serial_nos_from_bundle(manufacture_ste_doc1.items[0].serial_and_batch_bundle)),
+ serial_nos_list[0:1],
+ )
self.assertEqual(manufacture_ste_doc1.items[0].qty, 1)
- manufacture_ste_doc1.submit()
-
# Second Manufacture stock entry
- manufacture_ste_doc2 = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 2))
+ manufacture_ste_doc2 = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 3))
# Serial nos should be same as transferred Serial nos
- self.assertEqual(get_serial_nos(manufacture_ste_doc2.items[0].serial_no), serial_nos_list[1:3])
- self.assertEqual(manufacture_ste_doc2.items[0].qty, 2)
+ self.assertEqual(get_serial_nos(manufacture_ste_doc2.items[0].serial_no), serial_nos_list[1:4])
+ self.assertEqual(manufacture_ste_doc2.items[0].qty, 3)
def test_backflushed_serial_no_batch_raw_materials_based_on_transferred(self):
frappe.db.set_single_value(
@@ -1542,19 +1546,9 @@
row.qty -= 2
row.transfer_qty -= 2
- if not row.serial_and_batch_bundle:
- continue
-
- bundle_id = row.serial_and_batch_bundle
- bundle_doc = frappe.get_doc("Serial and Batch Bundle", bundle_id)
- if bundle_doc.has_serial_no:
- bundle_doc.set("entries", bundle_doc.entries[0:5])
- else:
- for bundle_row in bundle_doc.entries:
- bundle_row.qty += 2
-
- bundle_doc.save()
- bundle_doc.load_from_db()
+ if row.serial_no:
+ serial_nos = get_serial_nos(row.serial_no)
+ row.serial_no = "\n".join(serial_nos[:5])
ste_doc.save()
ste_doc.submit()
@@ -1897,6 +1891,71 @@
"Manufacturing Settings", {"disable_capacity_planning": 1, "mins_between_operations": 0}
)
+ def test_partial_material_consumption_with_batch(self):
+ from erpnext.stock.doctype.stock_entry.test_stock_entry import (
+ make_stock_entry as make_stock_entry_test_record,
+ )
+
+ frappe.db.set_single_value("Manufacturing Settings", "material_consumption", 1)
+ frappe.db.set_single_value(
+ "Manufacturing Settings",
+ "backflush_raw_materials_based_on",
+ "Material Transferred for Manufacture",
+ )
+
+ fg_item = make_item(
+ "Test FG Item For Partial Material Consumption",
+ {"is_stock_item": 1},
+ ).name
+
+ rm_item = make_item(
+ "Test RM Item For Partial Material Consumption",
+ {
+ "is_stock_item": 1,
+ "has_batch_no": 1,
+ "create_new_batch": 1,
+ "batch_number_series": "TST-BATCH-PMCC-.####",
+ },
+ ).name
+
+ make_bom(
+ item=fg_item,
+ source_warehouse="Stores - _TC",
+ raw_materials=[rm_item],
+ )
+
+ make_stock_entry_test_record(
+ purpose="Material Receipt",
+ item_code=rm_item,
+ target="Stores - _TC",
+ qty=10,
+ basic_rate=100,
+ )
+
+ wo_order = make_wo_order_test_record(item=fg_item, qty=10)
+
+ stock_entry = frappe.get_doc(
+ make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 10)
+ )
+ stock_entry.submit()
+ stock_entry.reload()
+
+ batch_no = get_batch_from_bundle(stock_entry.items[0].serial_and_batch_bundle)
+
+ stock_entry = frappe.get_doc(
+ make_stock_entry(wo_order.name, "Material Consumption for Manufacture", 10)
+ )
+
+ self.assertEqual(stock_entry.items[0].batch_no, batch_no)
+ self.assertEqual(stock_entry.items[0].use_serial_batch_fields, 1)
+
+ frappe.db.set_single_value("Manufacturing Settings", "material_consumption", 0)
+ frappe.db.set_single_value(
+ "Manufacturing Settings",
+ "backflush_raw_materials_based_on",
+ "BOM",
+ )
+
def make_operation(**kwargs):
kwargs = frappe._dict(kwargs)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 399e698..d8d9d68 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -2168,23 +2168,42 @@
if not qty:
return
+ use_serial_batch_fields = frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields")
+
ste_item_details = {
"from_warehouse": item.warehouse,
"to_warehouse": "",
"qty": qty,
"item_name": item.item_name,
- "serial_and_batch_bundle": create_serial_and_batch_bundle(self, row, item, "Outward"),
+ "serial_and_batch_bundle": create_serial_and_batch_bundle(self, row, item, "Outward")
+ if not use_serial_batch_fields
+ else "",
"description": item.description,
"stock_uom": item.stock_uom,
"expense_account": item.expense_account,
"cost_center": item.buying_cost_center,
"original_item": item.original_item,
+ "serial_no": "\n".join(row.serial_nos)
+ if row.serial_nos and not row.batches_to_be_consume
+ else "",
+ "use_serial_batch_fields": use_serial_batch_fields,
}
if self.is_return:
ste_item_details["to_warehouse"] = item.s_warehouse
- self.add_to_stock_entry_detail({item.item_code: ste_item_details})
+ if use_serial_batch_fields and not row.serial_no and row.batches_to_be_consume:
+ for batch_no, batch_qty in row.batches_to_be_consume.items():
+ ste_item_details.update(
+ {
+ "batch_no": batch_no,
+ "qty": batch_qty,
+ }
+ )
+
+ self.add_to_stock_entry_detail({item.item_code: ste_item_details})
+ else:
+ self.add_to_stock_entry_detail({item.item_code: ste_item_details})
@staticmethod
def get_serial_nos_based_on_transferred_batch(batch_no, serial_nos) -> list:
@@ -2335,6 +2354,9 @@
"item_name",
"serial_and_batch_bundle",
"allow_zero_valuation_rate",
+ "use_serial_batch_fields",
+ "batch_no",
+ "serial_no",
]:
if item_row.get(field):
se_child.set(field, item_row.get(field))
@@ -2983,7 +3005,7 @@
if row.batch_no:
item_data.batch_details[row.batch_no] += row.qty
- if row.batch_nos:
+ elif row.batch_nos:
for batch_no, qty in row.batch_nos.items():
item_data.batch_details[batch_no] += qty
@@ -2991,7 +3013,7 @@
item_data.serial_nos.extend(get_serial_nos(row.serial_no))
item_data.serial_nos.sort()
- if row.serial_nos:
+ elif row.serial_nos:
item_data.serial_nos.extend(get_serial_nos(row.serial_nos))
item_data.serial_nos.sort()
else:
@@ -3001,7 +3023,7 @@
if row.batch_no:
item_data.batch_details[row.batch_no] -= row.qty
- if row.batch_nos:
+ elif row.batch_nos:
for batch_no, qty in row.batch_nos.items():
item_data.batch_details[batch_no] += qty
@@ -3009,7 +3031,7 @@
for serial_no in get_serial_nos(row.serial_no):
item_data.serial_nos.remove(serial_no)
- if row.serial_nos:
+ elif row.serial_nos:
for serial_no in get_serial_nos(row.serial_nos):
item_data.serial_nos.remove(serial_no)