diff --git a/erpnext/__init__.py b/erpnext/__init__.py index faee116955b..c0a4cea763c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from frappe.utils.user import is_website_user -__version__ = "15.45.0" +__version__ = "15.45.1" def get_default_company(user=None): diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 92e4715590b..9d862e0bbdf 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -182,25 +182,30 @@ frappe.ui.form.on("BOM", { }, make_work_order(frm) { - frm.events.setup_variant_prompt(frm, "Work Order", (frm, item, data, variant_items) => { - frappe.call({ - method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", - args: { - bom_no: frm.doc.name, - item: item, - qty: data.qty || 0.0, - project: frm.doc.project, - variant_items: variant_items, - }, - freeze: true, - callback(r) { - if (r.message) { - let doc = frappe.model.sync(r.message)[0]; - frappe.set_route("Form", doc.doctype, doc.name); - } - }, - }); - }); + frm.events.setup_variant_prompt( + frm, + "Work Order", + (frm, item, data, variant_items, use_multi_level_bom) => { + frappe.call({ + method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", + args: { + bom_no: frm.doc.name, + item: item, + qty: data.qty || 0.0, + project: frm.doc.project, + variant_items: variant_items, + use_multi_level_bom: use_multi_level_bom, + }, + freeze: true, + callback(r) { + if (r.message) { + let doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + }, + }); + } + ); }, make_variant_bom(frm) { @@ -248,6 +253,13 @@ frappe.ui.form.on("BOM", { }; }, }); + + fields.push({ + fieldtype: "Check", + label: __("Use Multi-Level BOM"), + fieldname: "use_multi_level_bom", + default: 1, + }); } fields.push({ @@ -292,6 +304,7 @@ frappe.ui.form.on("BOM", { fieldname: "items", fieldtype: "Table", label: __("Raw Materials"), + depends_on: "eval:!doc.use_multi_level_bom", fields: [ { fieldname: "item_code", @@ -354,14 +367,15 @@ frappe.ui.form.on("BOM", { (data) => { let item = data.item || frm.doc.item; let variant_items = data.items || []; + let use_multi_level_bom = data.use_multi_level_bom || 0; variant_items.forEach((d) => { - if (!d.variant_item_code) { + if (!d.variant_item_code && !use_multi_level_bom) { frappe.throw(__("Select variant item code for the template item {0}", [d.item_code])); } }); - callback(frm, item, data, variant_items); + callback(frm, item, data, variant_items, use_multi_level_bom); }, __(title), __("Create") @@ -371,7 +385,7 @@ frappe.ui.form.on("BOM", { dialog.fields_dict.items.df.data.push({ item_code: d.item_code, variant_item_code: "", - qty: d.qty, + qty: (d.qty / frm.doc.quantity) * (dialog.fields_dict.qty.value || 1), source_warehouse: d.source_warehouse, operation: d.operation, }); diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 480a98f8916..395e9e83005 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -176,7 +176,7 @@ class BOM(WebsiteGenerator): search_key = f"{self.doctype}-{self.item}%" existing_boms = frappe.get_all( - "BOM", filters={"name": ("like", search_key), "amended_from": ["is", "not set"]}, pluck="name" + "BOM", filters={"name": search_key, "amended_from": ["is", "not set"]}, pluck="name" ) index = self.get_index_for_bom(existing_boms) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 1d0df26800d..e5219d7cb3e 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1299,7 +1299,7 @@ def get_item_details(item, project=None, skip_bom_info=False, throw=True): @frappe.whitelist() -def make_work_order(bom_no, item, qty=0, project=None, variant_items=None): +def make_work_order(bom_no, item, qty=0, project=None, variant_items=None, use_multi_level_bom=None): if not frappe.has_permission("Work Order", "write"): frappe.throw(_("Not permitted"), frappe.PermissionError) @@ -1309,12 +1309,13 @@ def make_work_order(bom_no, item, qty=0, project=None, variant_items=None): wo_doc.production_item = item wo_doc.update(item_details) wo_doc.bom_no = bom_no + wo_doc.use_multi_level_bom = cint(use_multi_level_bom) if flt(qty) > 0: wo_doc.qty = flt(qty) wo_doc.get_items_and_operations_from_bom() - if variant_items: + if variant_items and not wo_doc.use_multi_level_bom: add_variant_item(variant_items, wo_doc, bom_no, "required_items") return wo_doc @@ -1358,7 +1359,18 @@ def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate")) args["uom"] = item_data.stock_uom - wo_doc.append(table_name, args) + + existing_row = get_template_rm_item(wo_doc, item.get("item_code")) + if existing_row: + existing_row.update(args) + else: + wo_doc.append(table_name, args) + + +def get_template_rm_item(wo_doc, item_code): + for row in wo_doc.required_items: + if row.item_code == item_code: + return row @frappe.whitelist()