mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-10 18:35:07 +00:00
fix: add v15 compatibility for scrap item
This commit is contained in:
@@ -2519,13 +2519,11 @@ class TestWorkOrder(FrappeTestCase):
|
||||
scrap_item = make_item("Test Scrap for Multi Batch Disassembly", {"is_stock_item": 1}).name
|
||||
fg_item = make_item("Test FG for Multi Batch Disassembly", {"is_stock_item": 1}).name
|
||||
bom = make_bom(
|
||||
item=fg_item,
|
||||
quantity=1,
|
||||
raw_materials=[raw_item1, raw_item2],
|
||||
rm_qty=2,
|
||||
scrap_items=[scrap_item],
|
||||
scrap_qty=10,
|
||||
item=fg_item, quantity=1, raw_materials=[raw_item1, raw_item2], rm_qty=2, do_not_submit=True
|
||||
)
|
||||
# add scrap item
|
||||
bom.append("scrap_items", {"item_code": scrap_item, "stock_qty": 10})
|
||||
bom.submit()
|
||||
|
||||
# Create WO
|
||||
wo = make_wo_order_test_record(production_item=fg_item, qty=10, bom_no=bom.name, status="Not Started")
|
||||
@@ -2611,16 +2609,15 @@ class TestWorkOrder(FrappeTestCase):
|
||||
fg_item_row = next((i for i in stock_entry.items if i.item_code == fg_item), None)
|
||||
self.assertEqual(fg_item_row.qty, disassemble_qty)
|
||||
|
||||
# Secondary/Scrap item: should be taken from scrap warehouse in disassembly
|
||||
# Scrap item: should be taken from scrap warehouse in disassembly
|
||||
scrap_row = next((i for i in stock_entry.items if i.item_code == scrap_item), None)
|
||||
self.assertIsNotNone(scrap_row)
|
||||
self.assertEqual(scrap_row.type, "Scrap")
|
||||
self.assertEqual(scrap_row.is_scrap_item, 1)
|
||||
self.assertTrue(scrap_row.s_warehouse)
|
||||
self.assertFalse(scrap_row.t_warehouse)
|
||||
self.assertEqual(scrap_row.s_warehouse, wo.scrap_warehouse)
|
||||
# BOM has scrap_qty=10/FG but also process_loss_per=10%, so actual scrap per FG = 9
|
||||
# Total produced = 9*3 + 9*7 = 90, disassemble 4/10 → 36
|
||||
self.assertEqual(scrap_row.qty, 36)
|
||||
# BOM has scrap_qty=10/FG, total produced = 10*10 = 100, disassemble 4/10 → 40
|
||||
self.assertEqual(scrap_row.qty, 40)
|
||||
|
||||
# RM quantities
|
||||
for bom_item in bom.items:
|
||||
@@ -2665,11 +2662,11 @@ class TestWorkOrder(FrappeTestCase):
|
||||
|
||||
bom_scrap_row = next((i for i in bom_se.items if i.item_code == scrap_item), None)
|
||||
self.assertIsNotNone(bom_scrap_row, "Scrap item must appear in BOM-path disassembly")
|
||||
# Without fix 3: qty = 10 * 2 = 20; with fix 3 (process_loss_per=10%): qty = 9 * 2 = 18
|
||||
# v15: BOM scrap_qty=10/FG, no process_loss_per field → qty = 10 * 2 = 20
|
||||
self.assertEqual(
|
||||
bom_scrap_row.qty,
|
||||
18,
|
||||
f"BOM-path disassembly must apply process_loss_per; expected 18, got {bom_scrap_row.qty}",
|
||||
20,
|
||||
f"BOM-path disassembly scrap qty mismatch; expected 20, got {bom_scrap_row.qty}",
|
||||
)
|
||||
|
||||
def test_disassembly_with_additional_rm_not_in_bom(self):
|
||||
|
||||
@@ -784,7 +784,7 @@ class StockEntry(StockController):
|
||||
|
||||
if self.purpose == "Disassemble":
|
||||
if has_bom:
|
||||
if d.is_finished_item or d.type or d.is_legacy_scrap_item:
|
||||
if d.is_finished_item or d.is_scrap_item:
|
||||
d.t_warehouse = None
|
||||
if not d.s_warehouse:
|
||||
frappe.throw(_("Source warehouse is mandatory for row {0}").format(d.idx))
|
||||
@@ -2136,9 +2136,7 @@ class StockEntry(StockController):
|
||||
"s_warehouse": s_warehouse,
|
||||
"t_warehouse": t_warehouse,
|
||||
"is_finished_item": source_row.is_finished_item,
|
||||
"type": source_row.type,
|
||||
"is_legacy_scrap_item": source_row.is_legacy_scrap_item,
|
||||
"bom_secondary_item": source_row.bom_secondary_item,
|
||||
"is_scrap_item": source_row.is_scrap_item,
|
||||
"bom_no": source_row.bom_no,
|
||||
# batch and serial bundles built on submit
|
||||
"use_serial_batch_fields": 1 if (source_row.batch_no or source_row.serial_no) else 0,
|
||||
@@ -2168,9 +2166,9 @@ class StockEntry(StockController):
|
||||
|
||||
self.add_to_stock_entry_detail(item_dict)
|
||||
|
||||
# Secondary/Scrap items (reverse of what set_secondary_items does for Manufacture)
|
||||
secondary_items = self.get_secondary_items(self.fg_completed_qty)
|
||||
if secondary_items:
|
||||
# Scrap items (reverse: take scrap FROM scrap warehouse instead of producing TO it)
|
||||
scrap_items = self.get_bom_scrap_material(self.fg_completed_qty)
|
||||
if scrap_items:
|
||||
scrap_warehouse = self.from_warehouse
|
||||
if self.work_order:
|
||||
wo_values = frappe.db.get_value(
|
||||
@@ -2178,18 +2176,12 @@ class StockEntry(StockController):
|
||||
)
|
||||
scrap_warehouse = wo_values.scrap_warehouse or scrap_warehouse or wo_values.fg_warehouse
|
||||
|
||||
for item in secondary_items.values():
|
||||
for item in scrap_items.values():
|
||||
item["from_warehouse"] = scrap_warehouse
|
||||
item["to_warehouse"] = ""
|
||||
item["is_finished_item"] = 0
|
||||
|
||||
if item.get("process_loss_per"):
|
||||
item["qty"] -= flt(
|
||||
item["qty"] * (item["process_loss_per"] / 100),
|
||||
self.precision("fg_completed_qty"),
|
||||
)
|
||||
|
||||
self.add_to_stock_entry_detail(secondary_items, bom_no=self.bom_no)
|
||||
self.add_to_stock_entry_detail(scrap_items, bom_no=self.bom_no)
|
||||
|
||||
# Finished goods
|
||||
self.load_items_from_bom()
|
||||
@@ -2208,9 +2200,7 @@ class StockEntry(StockController):
|
||||
SED.basic_rate,
|
||||
SED.conversion_factor,
|
||||
SED.is_finished_item,
|
||||
SED.type,
|
||||
SED.is_legacy_scrap_item,
|
||||
SED.bom_secondary_item,
|
||||
SED.is_scrap_item,
|
||||
SED.batch_no,
|
||||
SED.serial_no,
|
||||
SED.use_serial_batch_fields,
|
||||
|
||||
Reference in New Issue
Block a user