diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.js b/erpnext/manufacturing/doctype/bom_creator/bom_creator.js index 2c4b786ef428b..01dc89b080268 100644 --- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.js +++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.js @@ -13,23 +13,30 @@ frappe.ui.form.on("BOM Creator", { if (!frm.is_new()) { if ((!frappe.bom_configurator || frappe.bom_configurator.bom_configurator !== frm.doc.name)) { - let $parent = $(frm.fields_dict["bom_creator"].wrapper); - $parent.empty(); - - frappe.require('bom_configurator.bundle.js').then(() => { - frappe.bom_configurator = new frappe.ui.BOMConfigurator({ - wrapper: $parent, - page: $parent, - frm: frm, - bom_configurator: frm.doc.name, - }); - }); + frm.trigger("build_tree"); } } else { + let $parent = $(frm.fields_dict["bom_creator"].wrapper); + $parent.empty(); frm.trigger("make_new_entry"); } }, + build_tree(frm) { + let $parent = $(frm.fields_dict["bom_creator"].wrapper); + $parent.empty(); + frm.toggle_enable("item_code", false); + + frappe.require('bom_configurator.bundle.js').then(() => { + frappe.bom_configurator = new frappe.ui.BOMConfigurator({ + wrapper: $parent, + page: $parent, + frm: frm, + bom_configurator: frm.doc.name, + }); + }); + }, + make_new_entry(frm) { let dialog = new frappe.ui.Dialog({ title: __("Multi-level BOM Creator"), @@ -122,7 +129,11 @@ frappe.ui.form.on("BOM Creator", { }, add_custom_buttons(frm) { - // + if (!frm.is_new()) { + frm.add_custom_button(__("Rebuild Tree"), () => { + frm.trigger("build_tree"); + }); + } } }); diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py index 1791f8ba3a638..999d610dfae5f 100644 --- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py +++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py @@ -34,11 +34,19 @@ class BOMCreator(Document): def before_save(self): self.set_status() + self.set_is_expandable() self.set_conversion_factor() self.set_reference_id() - self.set_is_expandable() self.set_rate_for_items() + def validate(self): + self.validate_items() + + def validate_items(self): + for row in self.items: + if row.is_expandable and row.item_code == self.item_code: + frappe.throw(_("Item {0} cannot be added as a sub-assembly of itself").format(row.item_code)) + def set_status(self, save=False): self.status = { 0: "Draft", @@ -135,7 +143,7 @@ def get_raw_material_cost(self, fg_reference_id=None, amount=0): return amount def set_is_expandable(self): - fg_items = [row.fg_item for row in self.items] + fg_items = [row.fg_item for row in self.items if row.fg_item != self.item_code] for row in self.items: row.is_expandable = 0 if row.item_code in fg_items: @@ -231,6 +239,7 @@ def create_bom(self, row, production_item_wise_rm): for item in production_item_wise_rm[(row.item_code, row.name)]["items"]: bom_no = "" + item.do_not_explode = 1 if (item.item_code, item.name) in production_item_wise_rm: bom_no = production_item_wise_rm.get((item.item_code, item.name)).bom_no item.do_not_explode = 0 @@ -343,6 +352,7 @@ def add_sub_assembly(**kwargs): "stock_qty": bom_item.qty, "fg_reference_id": name, "do_not_explode": 1, + "is_expandable": 1, "stock_uom": item_info.stock_uom, }, ) @@ -405,5 +415,10 @@ def delete_node(**kwargs): @frappe.whitelist() -def edit_qty(doctype, docname, qty): +def edit_qty(doctype, docname, qty, parent): frappe.db.set_value(doctype, docname, "qty", qty) + doc = frappe.get_doc("BOM Creator", parent) + doc.set_rate_for_items() + doc.save() + + return doc diff --git a/erpnext/public/js/bom_configurator/bom_configurator.bundle.js b/erpnext/public/js/bom_configurator/bom_configurator.bundle.js index 6b0181a38ff19..b3b2e9f9b86fd 100644 --- a/erpnext/public/js/bom_configurator/bom_configurator.bundle.js +++ b/erpnext/public/js/bom_configurator/bom_configurator.bundle.js @@ -30,14 +30,15 @@ class BOMConfigurator { bind_events() { frappe.views.trees["BOM Configurator"].events = { + frm: this.frm, add_item: this.add_item, add_sub_assembly: this.add_sub_assembly, get_sub_assembly_modal_fields: this.get_sub_assembly_modal_fields, convert_to_sub_assembly: this.convert_to_sub_assembly, delete_node: this.delete_node, edit_qty: this.edit_qty, - frm: this.frm, load_tree: this.load_tree, + set_default_qty: this.set_default_qty, } } @@ -93,9 +94,8 @@ class BOMConfigurator { display: inline-flex; min-width: 128px; border: 1px solid var(--bg-gray); - " - data-bom-qty-docname="${docname}"> -
${qty} ${uom}
+ "> +
${qty} ${uom}
${amount}
@@ -108,7 +108,7 @@ class BOMConfigurator { label:__(frappe.utils.icon('edit', 'sm') + " Qty"), click: function(node) { let view = frappe.views.trees["BOM Configurator"]; - view.events.edit_qty(node); + view.events.edit_qty(node, view); }, btnClass: "hidden-xs" }, @@ -232,6 +232,7 @@ class BOMConfigurator { }); dialog.show(); + view.events.set_default_qty(dialog); dialog.set_primary_action(__("Add"), () => { let bom_item = dialog.get_values(); @@ -285,6 +286,8 @@ class BOMConfigurator { }); dialog.show(); + view.events.set_default_qty(dialog); + dialog.set_primary_action(__("Add"), () => { let bom_item = dialog.get_values(); @@ -307,6 +310,17 @@ class BOMConfigurator { }); } + set_default_qty(dialog) { + dialog.fields_dict.items.grid.fields_map.item_code.onchange = function (event) { + if (event) { + let name = $(event.currentTarget).closest('.grid-row').attr("data-name") + let item_row = dialog.fields_dict.items.grid.grid_rows_by_docname[name].doc; + item_row.qty = 1; + dialog.fields_dict.items.grid.refresh() + } + } + } + delete_node(node, view) { frappe.confirm(__("Are you sure you want to delete this Item?"), () => { frappe.call({ @@ -324,7 +338,7 @@ class BOMConfigurator { }); } - edit_qty(node) { + edit_qty(node, view) { let qty = node.data.qty || this.frm.doc.qty; frappe.prompt([ { label: __("Qty"), fieldname: "qty", default: qty, fieldtype: "Float", reqd: 1 }, @@ -339,11 +353,13 @@ class BOMConfigurator { doctype: doctype, docname: docname, qty: data.qty, + parent: node.data.parent_id, }, callback: (r) => { node.data.qty = data.qty; let uom = node.data.uom || this.frm.doc.uom; $(node.parent.get(0)).find(`[data-bom-qty-docname='${docname}']`).html(data.qty + " " + uom); + view.events.load_tree(r, node); } }); },