412 lines
11 KiB
JavaScript
412 lines
11 KiB
JavaScript
class BOMConfigurator {
|
|
constructor({ wrapper, page, frm, bom_configurator }) {
|
|
this.$wrapper = $(wrapper);
|
|
this.page = page;
|
|
this.bom_configurator = bom_configurator;
|
|
this.frm = frm;
|
|
|
|
this.make();
|
|
this.prepare_layout();
|
|
this.bind_events();
|
|
}
|
|
|
|
add_boms() {
|
|
this.frm.call({
|
|
method: "add_boms",
|
|
freeze: true,
|
|
doc: this.frm.doc,
|
|
});
|
|
}
|
|
|
|
make() {
|
|
let options = {
|
|
...this.tree_options(),
|
|
...this.tree_methods(),
|
|
};
|
|
|
|
frappe.views.trees["BOM Configurator"] = new frappe.views.TreeView(options);
|
|
this.tree_view = frappe.views.trees["BOM Configurator"];
|
|
}
|
|
|
|
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,
|
|
load_tree: this.load_tree,
|
|
set_default_qty: this.set_default_qty,
|
|
}
|
|
}
|
|
|
|
tree_options() {
|
|
return {
|
|
parent: this.$wrapper.get(0),
|
|
body: this.$wrapper.get(0),
|
|
doctype: 'BOM Configurator',
|
|
page: this.page,
|
|
expandable: true,
|
|
title: __("Configure Product Assembly"),
|
|
breadcrumb: "Manufacturing",
|
|
get_tree_nodes: "erpnext.manufacturing.doctype.bom_creator.bom_creator.get_children",
|
|
root_label: this.frm.doc.item_code,
|
|
disable_add_node: true,
|
|
get_tree_root: false,
|
|
show_expand_all: false,
|
|
extend_toolbar: false,
|
|
do_not_make_page: true,
|
|
do_not_setup_menu: true,
|
|
}
|
|
}
|
|
|
|
tree_methods() {
|
|
let frm_obj = this;
|
|
let view = frappe.views.trees["BOM Configurator"];
|
|
|
|
return {
|
|
onload: function(me) {
|
|
me.args["parent_id"] = frm_obj.frm.doc.name;
|
|
me.args["parent"] = frm_obj.frm.doc.item_code;
|
|
me.parent = frm_obj.$wrapper.get(0);
|
|
me.body = frm_obj.$wrapper.get(0);
|
|
me.make_tree();
|
|
},
|
|
onrender(node) {
|
|
const qty = node.data.qty || frm_obj.frm.doc.qty;
|
|
const uom = node.data.uom || frm_obj.frm.doc.uom;
|
|
const docname = node.data.name || frm_obj.frm.doc.name;
|
|
let amount = node.data.amount;
|
|
if (node.data.value === frm_obj.frm.doc.item_code) {
|
|
amount = frm_obj.frm.doc.raw_material_cost;
|
|
}
|
|
|
|
amount = frappe.format(amount, { fieldtype: "Currency", currency: frm_obj.frm.doc.currency });
|
|
|
|
$(`
|
|
<div class="pill small pull-right bom-qty-pill"
|
|
style="background-color: var(--bg-white);
|
|
color: var(--text-on-gray);
|
|
font-weight:450;
|
|
margin-right: 40px;
|
|
display: inline-flex;
|
|
min-width: 128px;
|
|
border: 1px solid var(--bg-gray);
|
|
">
|
|
<div style="padding-right:5px" data-bom-qty-docname="${docname}">${qty} ${uom}</div>
|
|
<div class="fg-item-amt" style="padding-left:12px; border-left:1px solid var(--bg-gray)">
|
|
${amount}
|
|
</div>
|
|
</div>
|
|
|
|
`).insertBefore(node.$ul);
|
|
},
|
|
toolbar: this.frm?.doc.docstatus === 0 ? [
|
|
{
|
|
label:__(frappe.utils.icon('edit', 'sm') + " Qty"),
|
|
click: function(node) {
|
|
let view = frappe.views.trees["BOM Configurator"];
|
|
view.events.edit_qty(node, view);
|
|
},
|
|
btnClass: "hidden-xs"
|
|
},
|
|
{
|
|
label:__(frappe.utils.icon('add', 'sm') + " Raw Material"),
|
|
click: function(node) {
|
|
let view = frappe.views.trees["BOM Configurator"];
|
|
view.events.add_item(node, view);
|
|
},
|
|
condition: function(node) {
|
|
return node.expandable;
|
|
},
|
|
btnClass: "hidden-xs"
|
|
},
|
|
{
|
|
label:__(frappe.utils.icon('add', 'sm') + " Sub Assembly"),
|
|
click: function(node) {
|
|
let view = frappe.views.trees["BOM Configurator"];
|
|
view.events.add_sub_assembly(node, view);
|
|
},
|
|
condition: function(node) {
|
|
return node.expandable;
|
|
},
|
|
btnClass: "hidden-xs"
|
|
},
|
|
{
|
|
label:__("Expand All"),
|
|
click: function(node) {
|
|
let view = frappe.views.trees["BOM Configurator"];
|
|
|
|
if (!node.expanded) {
|
|
view.tree.load_children(node, true);
|
|
$(node.parent[0]).find(".tree-children").show();
|
|
node.$toolbar.find(".expand-all-btn").html("Collapse All");
|
|
} else {
|
|
node.$tree_link.trigger("click");
|
|
node.$toolbar.find(".expand-all-btn").html("Expand All");
|
|
}
|
|
},
|
|
condition: function(node) {
|
|
return node.expandable && node.is_root;
|
|
},
|
|
btnClass: "hidden-xs expand-all-btn"
|
|
},
|
|
{
|
|
label:__(frappe.utils.icon('move', 'sm') + " Sub Assembly"),
|
|
click: function(node) {
|
|
let view = frappe.views.trees["BOM Configurator"];
|
|
view.events.convert_to_sub_assembly(node, view);
|
|
},
|
|
condition: function(node) {
|
|
return !node.expandable;
|
|
},
|
|
btnClass: "hidden-xs"
|
|
},
|
|
{
|
|
label:__(frappe.utils.icon('delete', 'sm') + __(" Item")),
|
|
click: function(node) {
|
|
let view = frappe.views.trees["BOM Configurator"];
|
|
view.events.delete_node(node, view);
|
|
},
|
|
condition: function(node) {
|
|
return !node.is_root;
|
|
},
|
|
btnClass: "hidden-xs"
|
|
},
|
|
] : [{
|
|
label:__("Expand All"),
|
|
click: function(node) {
|
|
let view = frappe.views.trees["BOM Configurator"];
|
|
|
|
if (!node.expanded) {
|
|
view.tree.load_children(node, true);
|
|
$(node.parent[0]).find(".tree-children").show();
|
|
node.$toolbar.find(".expand-all-btn").html("Collapse All");
|
|
} else {
|
|
node.$tree_link.trigger("click");
|
|
node.$toolbar.find(".expand-all-btn").html("Expand All");
|
|
}
|
|
},
|
|
condition: function(node) {
|
|
return node.expandable && node.is_root;
|
|
},
|
|
btnClass: "hidden-xs expand-all-btn"
|
|
}],
|
|
}
|
|
}
|
|
|
|
add_item(node, view) {
|
|
frappe.prompt([
|
|
{ label: __("Item"), fieldname: "item_code", fieldtype: "Link", options: "Item", reqd: 1 },
|
|
{ label: __("Qty"), fieldname: "qty", default: 1.0, fieldtype: "Float", reqd: 1 },
|
|
],
|
|
(data) => {
|
|
if (!node.data.parent_id) {
|
|
node.data.parent_id = this.frm.doc.name;
|
|
}
|
|
|
|
frappe.call({
|
|
method: "erpnext.manufacturing.doctype.bom_creator.bom_creator.add_item",
|
|
args: {
|
|
parent: node.data.parent_id,
|
|
fg_item: node.data.value,
|
|
item_code: data.item_code,
|
|
fg_reference_id: node.data.name || this.frm.doc.name,
|
|
qty: data.qty,
|
|
},
|
|
callback: (r) => {
|
|
view.events.load_tree(r, node);
|
|
}
|
|
});
|
|
},
|
|
__("Add Item"),
|
|
__("Add"));
|
|
}
|
|
|
|
add_sub_assembly(node, view) {
|
|
let dialog = new frappe.ui.Dialog({
|
|
fields: view.events.get_sub_assembly_modal_fields(),
|
|
title: __("Add Sub Assembly"),
|
|
});
|
|
|
|
dialog.show();
|
|
view.events.set_default_qty(dialog);
|
|
|
|
dialog.set_primary_action(__("Add"), () => {
|
|
let bom_item = dialog.get_values();
|
|
|
|
if (!node.data?.parent_id) {
|
|
node.data.parent_id = this.frm.doc.name;
|
|
}
|
|
|
|
frappe.call({
|
|
method: "erpnext.manufacturing.doctype.bom_creator.bom_creator.add_sub_assembly",
|
|
args: {
|
|
parent: node.data.parent_id,
|
|
fg_item: node.data.value,
|
|
fg_reference_id: node.data.name || this.frm.doc.name,
|
|
bom_item: bom_item,
|
|
},
|
|
callback: (r) => {
|
|
view.events.load_tree(r, node);
|
|
}
|
|
});
|
|
|
|
dialog.hide();
|
|
});
|
|
|
|
}
|
|
|
|
get_sub_assembly_modal_fields(read_only=false) {
|
|
return [
|
|
{ label: __("Sub Assembly Item"), fieldname: "item_code", fieldtype: "Link", options: "Item", reqd: 1, read_only: read_only },
|
|
{ fieldtype: "Column Break" },
|
|
{ label: __("Qty"), fieldname: "qty", default: 1.0, fieldtype: "Float", reqd: 1, read_only: read_only },
|
|
{ fieldtype: "Section Break" },
|
|
{ label: __("Raw Materials"), fieldname: "items", fieldtype: "Table", reqd: 1,
|
|
fields: [
|
|
{ label: __("Item"), fieldname: "item_code", fieldtype: "Link", options: "Item", reqd: 1, in_list_view: 1 },
|
|
{ label: __("Qty"), fieldname: "qty", default: 1.0, fieldtype: "Float", reqd: 1, in_list_view: 1 },
|
|
]
|
|
},
|
|
]
|
|
}
|
|
|
|
convert_to_sub_assembly(node, view) {
|
|
let dialog = new frappe.ui.Dialog({
|
|
fields: view.events.get_sub_assembly_modal_fields(true),
|
|
title: __("Add Sub Assembly"),
|
|
});
|
|
|
|
dialog.set_values({
|
|
item_code: node.data.value,
|
|
qty: node.data.qty,
|
|
});
|
|
|
|
dialog.show();
|
|
view.events.set_default_qty(dialog);
|
|
|
|
dialog.set_primary_action(__("Add"), () => {
|
|
let bom_item = dialog.get_values();
|
|
|
|
frappe.call({
|
|
method: "erpnext.manufacturing.doctype.bom_creator.bom_creator.add_sub_assembly",
|
|
args: {
|
|
parent: node.data.parent_id,
|
|
fg_item: node.data.value,
|
|
bom_item: bom_item,
|
|
fg_reference_id: node.data.name || this.frm.doc.name,
|
|
convert_to_sub_assembly: true,
|
|
},
|
|
callback: (r) => {
|
|
node.expandable = true;
|
|
view.events.load_tree(r, node);
|
|
}
|
|
});
|
|
|
|
dialog.hide();
|
|
});
|
|
}
|
|
|
|
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({
|
|
method: "erpnext.manufacturing.doctype.bom_creator.bom_creator.delete_node",
|
|
args: {
|
|
parent: node.data.parent_id,
|
|
fg_item: node.data.value,
|
|
doctype: node.data.doctype,
|
|
docname: node.data.name,
|
|
},
|
|
callback: (r) => {
|
|
view.events.load_tree(r, node.parent_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 },
|
|
],
|
|
(data) => {
|
|
let doctype = node.data.doctype || this.frm.doc.doctype;
|
|
let docname = node.data.name || this.frm.doc.name;
|
|
|
|
frappe.call({
|
|
method: "erpnext.manufacturing.doctype.bom_creator.bom_creator.edit_qty",
|
|
args: {
|
|
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);
|
|
}
|
|
});
|
|
},
|
|
__("Edit Qty"),
|
|
__("Update"));
|
|
}
|
|
|
|
prepare_layout() {
|
|
let main_div = $(this.page)[0];
|
|
|
|
main_div.style.marginBottom = "15px";
|
|
$(main_div).find(".tree-children")[0].style.minHeight = "370px";
|
|
$(main_div).find(".tree-children")[0].style.maxHeight = "370px";
|
|
$(main_div).find(".tree-children")[0].style.overflowY = "auto";
|
|
}
|
|
|
|
load_tree(response, node) {
|
|
let item_row = "";
|
|
let parent_dom = ""
|
|
let total_amount = response.message.raw_material_cost;
|
|
|
|
frappe.views.trees["BOM Configurator"].tree.load_children(node);
|
|
|
|
while (node) {
|
|
item_row = response.message.items.filter(item => item.name === node.data.name);
|
|
|
|
if (item_row?.length) {
|
|
node.data.amount = item_row[0].amount;
|
|
total_amount = node.data.amount
|
|
} else {
|
|
total_amount = response.message.raw_material_cost;
|
|
}
|
|
|
|
parent_dom = $(node.parent.get(0));
|
|
total_amount = frappe.format(
|
|
total_amount, {
|
|
fieldtype: "Currency",
|
|
currency: this.frm.doc.currency
|
|
}
|
|
);
|
|
|
|
$($(parent_dom).find(".fg-item-amt")[0]).html(total_amount);
|
|
node = node.parent_node;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
frappe.ui.BOMConfigurator = BOMConfigurator; |