fix: disallow BOM finished good item in secondary items table (#55710)

The FG item produced by a BOM should not also appear as a secondary
item (Co-Product/By-Product/Scrap/Additional Finished Good). When an
Additional Finished Good shared the main FG's item code, the resulting
Stock Entry ended up with two rows of the same item carrying different
valuation rates. Validate against it instead, exempting legacy rows so
migrated BOMs can still be re-saved.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Mihir Kandoi
2026-06-08 13:17:32 +05:30
committed by GitHub
parent ae6aef91bd
commit 00880eb657
2 changed files with 31 additions and 0 deletions

View File

@@ -366,6 +366,13 @@ class BOM(WebsiteGenerator):
def validate_secondary_items(self):
for item in self.secondary_items:
if not item.is_legacy and item.item_code == self.item:
frappe.throw(
_(
"Row #{0}: Finished Good Item {1} cannot be added in the Secondary Items table."
).format(item.idx, get_link_to_form("Item", item.item_code))
)
if not item.qty:
frappe.throw(
_("Row #{0}: Quantity should be greater than 0 for {1} Item {2}").format(

View File

@@ -444,6 +444,30 @@ class TestBOM(ERPNextTestSuite):
# Items with whole UOMs can't be PL Items
self.assertRaises(frappe.ValidationError, bom_doc.submit)
@timeout
def test_fg_item_not_allowed_in_secondary_items(self):
fg_item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100}).name
rm_item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100}).name
bom_doc = frappe.new_doc("BOM")
bom_doc.item = fg_item
bom_doc.quantity = 1
bom_doc.company = "_Test Company"
bom_doc.currency = "INR"
bom_doc.append("items", {"item_code": rm_item, "qty": 1, "rate": 100.0})
bom_doc.append(
"secondary_items",
{
"item_code": fg_item,
"secondary_item_type": "Additional Finished Good",
"qty": 1,
"cost_allocation_per": 10,
},
)
# FG item of the BOM cannot also be a secondary item
self.assertRaises(frappe.ValidationError, bom_doc.save)
@timeout
def test_bom_item_query(self):
query = partial(