Skip to content

Commit

Permalink
feat: Multi-level BOM Creator (#36494)
Browse files Browse the repository at this point in the history
* feat: Multi-level BOM Creator

* fix: renamed BOM Configurator to BOM Creator

* fix: added Cost in the tree

* fix: finished good cost

* fix: valuation rate in tree ui

* chore: conflicts and removed unnecessary files

* test: test cases for BOM Creator

* fix: added shortcut for the BOM Creator

* fix: added validation for Final Product
  • Loading branch information
rohitwaghchaure authored Aug 24, 2023
1 parent ab6e600 commit 3c15fea
Show file tree
Hide file tree
Showing 14 changed files with 1,963 additions and 17 deletions.
34 changes: 32 additions & 2 deletions erpnext/manufacturing/doctype/bom/bom.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@
"show_items",
"show_operations",
"web_long_description",
"reference_section",
"bom_creator",
"bom_creator_item",
"column_break_oxbz",
"amended_from",
"connections_tab"
],
Expand Down Expand Up @@ -233,7 +237,7 @@
"fieldname": "rm_cost_as_per",
"fieldtype": "Select",
"label": "Rate Of Materials Based On",
"options": "Valuation Rate\nLast Purchase Rate\nPrice List"
"options": "Valuation Rate\nLast Purchase Rate\nPrice List\nManual"
},
{
"allow_on_submit": 1,
Expand Down Expand Up @@ -599,14 +603,40 @@
"fieldname": "operating_cost_per_bom_quantity",
"fieldtype": "Currency",
"label": "Operating Cost Per BOM Quantity"
},
{
"fieldname": "reference_section",
"fieldtype": "Section Break",
"label": "Reference"
},
{
"fieldname": "bom_creator",
"fieldtype": "Link",
"label": "BOM Creator",
"no_copy": 1,
"options": "BOM Creator",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "bom_creator_item",
"fieldtype": "Data",
"label": "BOM Creator Item",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "column_break_oxbz",
"fieldtype": "Column Break"
}
],
"icon": "fa fa-sitemap",
"idx": 1,
"image_field": "image",
"is_submittable": 1,
"links": [],
"modified": "2023-04-06 12:47:58.514795",
"modified": "2023-08-07 11:38:08.152294",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM",
Expand Down
50 changes: 37 additions & 13 deletions erpnext/manufacturing/doctype/bom/bom.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ def on_update(self):

def on_submit(self):
self.manage_default_bom()
self.update_bom_creator_status()

def on_cancel(self):
self.db_set("is_active", 0)
Expand All @@ -214,6 +215,23 @@ def on_cancel(self):
# check if used in any other bom
self.validate_bom_links()
self.manage_default_bom()
self.update_bom_creator_status()

def update_bom_creator_status(self):
if not self.bom_creator:
return

if self.bom_creator_item:
frappe.db.set_value(
"BOM Creator Item",
self.bom_creator_item,
"bom_created",
1 if self.docstatus == 1 else 0,
update_modified=False,
)

doc = frappe.get_doc("BOM Creator", self.bom_creator)
doc.set_status(save=True)

def on_update_after_submit(self):
self.validate_bom_links()
Expand Down Expand Up @@ -662,18 +680,19 @@ def calculate_rm_cost(self, save=False):

for d in self.get("items"):
old_rate = d.rate
d.rate = self.get_rm_rate(
{
"company": self.company,
"item_code": d.item_code,
"bom_no": d.bom_no,
"qty": d.qty,
"uom": d.uom,
"stock_uom": d.stock_uom,
"conversion_factor": d.conversion_factor,
"sourced_by_supplier": d.sourced_by_supplier,
}
)
if self.rm_cost_as_per != "Manual":
d.rate = self.get_rm_rate(
{
"company": self.company,
"item_code": d.item_code,
"bom_no": d.bom_no,
"qty": d.qty,
"uom": d.uom,
"stock_uom": d.stock_uom,
"conversion_factor": d.conversion_factor,
"sourced_by_supplier": d.sourced_by_supplier,
}
)

d.base_rate = flt(d.rate) * flt(self.conversion_rate)
d.amount = flt(d.rate, d.precision("rate")) * flt(d.qty, d.precision("qty"))
Expand Down Expand Up @@ -964,7 +983,12 @@ def get_valuation_rate(data):
.as_("valuation_rate")
)
.where((bin_table.item_code == item_code) & (wh_table.company == company))
).run(as_dict=True)[0]
)

if data.get("set_rate_based_on_warehouse") and data.get("warehouse"):
item_valuation = item_valuation.where(bin_table.warehouse == data.get("warehouse"))

item_valuation = item_valuation.run(as_dict=True)[0]

valuation_rate = item_valuation.get("valuation_rate")

Expand Down
Empty file.
201 changes: 201 additions & 0 deletions erpnext/manufacturing/doctype/bom_creator/bom_creator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.provide("erpnext.bom");

frappe.ui.form.on("BOM Creator", {
setup(frm) {
frm.trigger("set_queries");
},

setup_bom_creator(frm) {
frm.dashboard.clear_comment();

if (!frm.is_new()) {
if ((!frappe.bom_configurator
|| frappe.bom_configurator.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"),
fields: [
{
label: __("Name"),
fieldtype: "Data",
fieldname: "name",
reqd: 1
},
{ fieldtype: "Column Break" },
{
label: __("Company"),
fieldtype: "Link",
fieldname: "company",
options: "Company",
reqd: 1,
default: frappe.defaults.get_user_default("Company"),
},
{ fieldtype: "Section Break" },
{
label: __("Item Code (Final Product)"),
fieldtype: "Link",
fieldname: "item_code",
options: "Item",
reqd: 1
},
{ fieldtype: "Column Break" },
{
label: __("Quantity"),
fieldtype: "Float",
fieldname: "qty",
reqd: 1,
default: 1.0
},
{ fieldtype: "Section Break" },
{
label: __("Currency"),
fieldtype: "Link",
fieldname: "currency",
options: "Currency",
reqd: 1,
default: frappe.defaults.get_global_default("currency")
},
{ fieldtype: "Column Break" },
{
label: __("Conversion Rate"),
fieldtype: "Float",
fieldname: "conversion_rate",
reqd: 1,
default: 1.0
},
],
primary_action_label: __("Create"),
primary_action: (values) => {
values.doctype = frm.doc.doctype;
frappe.db
.insert(values)
.then((doc) => {
frappe.set_route("Form", doc.doctype, doc.name);
});
}
})

dialog.show();
},

set_queries(frm) {
frm.set_query("bom_no", "items", function(doc, cdt, cdn) {
let item = frappe.get_doc(cdt, cdn);
return {
filters: {
item: item.item_code,
}
}
});
},

refresh(frm) {
frm.trigger("setup_bom_creator");
frm.trigger("set_root_item");
frm.trigger("add_custom_buttons");
},

set_root_item(frm) {
if (frm.is_new() && frm.doc.items?.length) {
frappe.model.set_value(frm.doc.items[0].doctype,
frm.doc.items[0].name, "is_root", 1);
}
},

add_custom_buttons(frm) {
if (!frm.is_new()) {
frm.add_custom_button(__("Rebuild Tree"), () => {
frm.trigger("build_tree");
});
}
}
});

frappe.ui.form.on("BOM Creator Item", {
item_code(frm, cdt, cdn) {
let item = frappe.get_doc(cdt, cdn);
if (item.item_code && item.is_root) {
frappe.model.set_value(cdt, cdn, "fg_item", item.item_code);
}
},

do_not_explode(frm, cdt, cdn) {
let item = frappe.get_doc(cdt, cdn);
if (!item.do_not_explode) {
frm.call({
method: "get_default_bom",
doc: frm.doc,
args: {
item_code: item.item_code
},
callback(r) {
if (r.message) {
frappe.model.set_value(cdt, cdn, "bom_no", r.message);
}
}
})
} else {
frappe.model.set_value(cdt, cdn, "bom_no", "");
}
}
});


erpnext.bom.BomConfigurator = class BomConfigurator extends erpnext.TransactionController {
conversion_rate(doc) {
if(this.frm.doc.currency === this.get_company_currency()) {
this.frm.set_value("conversion_rate", 1.0);
} else {
erpnext.bom.update_cost(doc);
}
}

buying_price_list(doc) {
this.apply_price_list();
}

plc_conversion_rate(doc) {
if (!this.in_apply_price_list) {
this.apply_price_list(null, true);
}
}

conversion_factor(doc, cdt, cdn) {
if (frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
var item = frappe.get_doc(cdt, cdn);
frappe.model.round_floats_in(item, ["qty", "conversion_factor"]);
item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item));
refresh_field("stock_qty", item.name, item.parentfield);
this.toggle_conversion_factor(item);
this.frm.events.update_cost(this.frm);
}
}
};

extend_cscript(cur_frm.cscript, new erpnext.bom.BomConfigurator({frm: cur_frm}));
Loading

0 comments on commit 3c15fea

Please sign in to comment.