mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-29 22:58:25 +00:00
feat: phantom bom (#50351)
* feat: add phantom bom settings in bom doctype * feat: new explosion logic for production plan, subcontracting and work order/manufacturing * feat: modify explosion logic in reports and bom creator * fix: failing test * feat: add convert to phantom item support in bom creator * test: added test cases * fix: always fetch rm rate if phantom bom * refactor: PP phantom explosion logic * fix: report test cases * feat: add phantom item in description of item if phantom item in bom tree * fix: hide create button if bom is phantom * fix: bugs found by coderabbit
This commit is contained in:
@@ -550,6 +550,8 @@ class SubcontractingController(StockController):
|
||||
frappe.delete_doc("Serial and Batch Bundle", item.serial_and_batch_bundle, force=True)
|
||||
|
||||
def __get_materials_from_bom(self, item_code, bom_no, exploded_item=0):
|
||||
data = []
|
||||
|
||||
doctype = "BOM Item" if not exploded_item else "BOM Explosion Item"
|
||||
fields = [f"`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit"]
|
||||
|
||||
@@ -558,7 +560,7 @@ class SubcontractingController(StockController):
|
||||
"name": "bom_detail_no",
|
||||
"source_warehouse": "reserve_warehouse",
|
||||
}
|
||||
for field in [
|
||||
fields_list = [
|
||||
"item_code",
|
||||
"name",
|
||||
"rate",
|
||||
@@ -567,7 +569,12 @@ class SubcontractingController(StockController):
|
||||
"description",
|
||||
"item_name",
|
||||
"stock_uom",
|
||||
]:
|
||||
]
|
||||
|
||||
if doctype == "BOM Item":
|
||||
fields_list.extend(["is_phantom_item", "bom_no"])
|
||||
|
||||
for field in fields_list:
|
||||
fields.append(f"`tab{doctype}`.`{field}` As {alias_dict.get(field, field)}")
|
||||
|
||||
filters = [
|
||||
@@ -577,7 +584,19 @@ class SubcontractingController(StockController):
|
||||
[doctype, "sourced_by_supplier", "=", 0],
|
||||
]
|
||||
|
||||
return frappe.get_all("BOM", fields=fields, filters=filters, order_by=f"`tab{doctype}`.`idx`") or []
|
||||
data = frappe.get_all("BOM", fields=fields, filters=filters, order_by=f"`tab{doctype}`.`idx`") or []
|
||||
to_remove = []
|
||||
for item in data:
|
||||
if item.is_phantom_item:
|
||||
data += self.__get_materials_from_bom(
|
||||
item.rm_item_code, item.bom_no, exploded_item=exploded_item
|
||||
)
|
||||
to_remove.append(item)
|
||||
|
||||
for item in to_remove:
|
||||
data.remove(item)
|
||||
|
||||
return data
|
||||
|
||||
def __update_reserve_warehouse(self, row, item):
|
||||
if (
|
||||
|
||||
@@ -1141,6 +1141,28 @@ class TestSubcontractingController(IntegrationTestCase):
|
||||
itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6],
|
||||
)
|
||||
|
||||
def test_phantom_bom_explosion(self):
|
||||
from erpnext.manufacturing.doctype.bom.test_bom import create_tree_for_phantom_bom_tests
|
||||
|
||||
expected = create_tree_for_phantom_bom_tests()
|
||||
service_items = [
|
||||
{
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"item_code": "Subcontracted Service Item 11",
|
||||
"qty": 5,
|
||||
"rate": 100,
|
||||
"fg_item": "Top Level Parent",
|
||||
"fg_item_qty": 5,
|
||||
},
|
||||
]
|
||||
sco = get_subcontracting_order(service_items=service_items, do_not_submit=True)
|
||||
sco.items[0].include_exploded_items = 0
|
||||
sco.save()
|
||||
sco.submit()
|
||||
sco.reload()
|
||||
|
||||
self.assertEqual([item.rm_item_code for item in sco.supplied_items], expected)
|
||||
|
||||
|
||||
def add_second_row_in_scr(scr):
|
||||
item_dict = {}
|
||||
@@ -1313,6 +1335,7 @@ def make_subcontracted_items():
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "SBAT.####",
|
||||
},
|
||||
"Top Level Parent": {},
|
||||
}
|
||||
|
||||
for item, properties in sub_contracted_items.items():
|
||||
@@ -1364,6 +1387,7 @@ def make_service_items():
|
||||
"Subcontracted Service Item 8": {},
|
||||
"Subcontracted Service Item 9": {},
|
||||
"Subcontracted Service Item 10": {},
|
||||
"Subcontracted Service Item 11": {},
|
||||
}
|
||||
|
||||
for item, properties in service_items.items():
|
||||
@@ -1389,6 +1413,7 @@ def make_bom_for_subcontracted_items():
|
||||
"Subcontracted Item SA7": ["Subcontracted SRM Item 1"],
|
||||
"Subcontracted Item SA8": ["Subcontracted SRM Item 8"],
|
||||
"Subcontracted Item SA10": ["Subcontracted SRM Item 10"],
|
||||
"Subcontracted Service Item 11": ["Top Level Parent"],
|
||||
}
|
||||
|
||||
for item_code, raw_materials in boms.items():
|
||||
|
||||
Reference in New Issue
Block a user