mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-17 08:28:44 +00:00
fix: derive operation FG items before material expansion, keep the final one the BOM's item
The finished_good derivation ran in validate_semi_finished_goods, after set_materials_based_on_operation_bom had already expanded operation BOM materials. A single-pass insert-and-submit (API or import) with bom_no set but finished_good empty skipped the expansion, persisting a submitted BOM without the referenced components. The derivation also let a final operation inherit another item from its bom_no, so downstream job cards would produce the wrong item. Move the derivation into set_operation_finished_goods, called before the expansion, prefer the BOM's own item for the final operation, and reject a final operation whose FG item is not the BOM's item.
This commit is contained in:
@@ -314,6 +314,7 @@ class BOM(WebsiteGenerator):
|
||||
self.clear_inspection()
|
||||
self.validate_main_item()
|
||||
self.validate_currency()
|
||||
self.set_operation_finished_goods()
|
||||
self.set_materials_based_on_operation_bom()
|
||||
self.set_conversion_rate()
|
||||
self.set_plc_conversion_rate()
|
||||
@@ -340,18 +341,25 @@ class BOM(WebsiteGenerator):
|
||||
self.set_fg_cost_allocation()
|
||||
self.validate_total_cost_allocation()
|
||||
|
||||
def set_operation_finished_goods(self):
|
||||
"""Fill each operation's FG item where it is unambiguous: the final operation produces
|
||||
this BOM's item, an operation with a BOM produces that BOM's item. Runs before
|
||||
set_materials_based_on_operation_bom so derived rows get their materials expanded."""
|
||||
if not self.track_semi_finished_goods:
|
||||
return
|
||||
|
||||
for row in self.operations:
|
||||
if row.is_final_finished_good and not row.finished_good:
|
||||
row.finished_good = self.item
|
||||
elif row.bom_no and not row.finished_good:
|
||||
row.finished_good = frappe.get_cached_value("BOM", row.bom_no, "item")
|
||||
|
||||
def validate_semi_finished_goods(self):
|
||||
if not self.track_semi_finished_goods or not self.operations:
|
||||
return
|
||||
|
||||
fg_items = []
|
||||
for row in self.operations:
|
||||
if row.bom_no and not row.finished_good:
|
||||
row.finished_good = frappe.get_cached_value("BOM", row.bom_no, "item")
|
||||
|
||||
if row.is_final_finished_good and not row.finished_good:
|
||||
row.finished_good = self.item
|
||||
|
||||
if not row.finished_good:
|
||||
frappe.throw(
|
||||
_(
|
||||
@@ -362,6 +370,13 @@ class BOM(WebsiteGenerator):
|
||||
if not row.is_final_finished_good:
|
||||
continue
|
||||
|
||||
if row.finished_good != self.item:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{0}: The operation {1} has 'Is Final Finished Good' checked, so its FG / Semi FG Item must be {2}."
|
||||
).format(row.idx, bold(row.operation), bold(self.item)),
|
||||
)
|
||||
|
||||
fg_items.append(row.finished_good)
|
||||
|
||||
if not fg_items:
|
||||
|
||||
Reference in New Issue
Block a user