Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: batch no not copied while making Material Consumption entry (backport #40290) #40300

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 79 additions & 20 deletions erpnext/manufacturing/doctype/work_order/test_work_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -1382,8 +1382,9 @@ def test_backflushed_serial_no_raw_materials_based_on_transferred(self):

# 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(
Expand All @@ -1395,19 +1396,22 @@ def test_backflushed_serial_no_raw_materials_based_on_transferred(self):

# 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(
Expand Down Expand Up @@ -1541,19 +1545,9 @@ def test_non_consumed_material_return_against_work_order(self):
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()
Expand Down Expand Up @@ -1896,6 +1890,71 @@ def test_capcity_planning_for_workstation(self):
"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)
Expand Down
34 changes: 28 additions & 6 deletions erpnext/stock/doctype/stock_entry/stock_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -2163,23 +2163,42 @@ def update_item_in_stock_entry_detail(self, row, item, qty) -> None:
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:
Expand Down Expand Up @@ -2330,6 +2349,9 @@ def add_to_stock_entry_detail(self, item_dict, bom_no=None):
"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))
Expand Down Expand Up @@ -2978,15 +3000,15 @@ def get_available_materials(work_order) -> dict:
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

if row.serial_no:
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:
Expand All @@ -2996,15 +3018,15 @@ def get_available_materials(work_order) -> dict:
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

if row.serial_no:
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)

Expand Down
Loading