BOM rates as per selected currency and UOM

This commit is contained in:
Nabin Hait 2017-08-18 17:52:49 +05:30 committed by Rohit Waghchaure
parent d32f08f6ec
commit 22cff25bf5
9 changed files with 365 additions and 213 deletions

View File

@ -5,7 +5,10 @@ frappe.provide("erpnext.bom");
frappe.ui.form.on("BOM", { frappe.ui.form.on("BOM", {
setup: function(frm) { setup: function(frm) {
frm.add_fetch('buying_price_list', 'currency', 'currency'); frm.add_fetch("item", "description", "description");
frm.add_fetch("item", "image", "image");
frm.add_fetch("item", "item_name", "item_name");
frm.add_fetch("item", "stock_uom", "uom");
frm.set_query("bom_no", "items", function() { frm.set_query("bom_no", "items", function() {
return { return {
@ -23,6 +26,38 @@ frappe.ui.form.on("BOM", {
} }
}; };
}); });
frm.set_query("item", function() {
return {
query: "erpnext.controllers.queries.item_query"
};
});
frm.set_query("project", function() {
return{
filters:[
['Project', 'status', 'not in', 'Completed, Cancelled']
]
};
});
frm.set_query("item_code", "items", function() {
return {
query: "erpnext.controllers.queries.item_query",
filters: [["Item", "name", "!=", cur_frm.doc.item]]
};
});
frm.set_query("bom_no", "items", function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
return {
filters: {
'item': d.item_code,
'is_active': 1,
'docstatus': 1
}
};
});
}, },
onload_post_render: function(frm) { onload_post_render: function(frm) {
@ -69,7 +104,7 @@ frappe.ui.form.on("BOM", {
}); });
erpnext.bom.BomController = erpnext.TransactionController.extend({ erpnext.bom.BomController = erpnext.TransactionController.extend({
conversion_rate: function(doc, cdt, cdn) { conversion_rate: function(doc) {
if(this.frm.doc.currency === this.get_company_currency()) { if(this.frm.doc.currency === this.get_company_currency()) {
this.frm.set_value("conversion_rate", 1.0); this.frm.set_value("conversion_rate", 1.0);
} else { } else {
@ -90,7 +125,7 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({
get_bom_material_detail(doc, cdt, cdn, scrap_items); get_bom_material_detail(doc, cdt, cdn, scrap_items);
}, },
conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) { conversion_factor: function(doc, cdt, cdn) {
if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
var item = frappe.get_doc(cdt, cdn); var item = frappe.get_doc(cdt, cdn);
frappe.model.round_floats_in(item, ["qty", "conversion_factor"]); frappe.model.round_floats_in(item, ["qty", "conversion_factor"]);
@ -99,30 +134,24 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({
this.toggle_conversion_factor(item); this.toggle_conversion_factor(item);
} }
}, },
}) });
$.extend(cur_frm.cscript, new erpnext.bom.BomController({frm: cur_frm})); $.extend(cur_frm.cscript, new erpnext.bom.BomController({frm: cur_frm}));
cur_frm.add_fetch("item", "description", "description"); cur_frm.cscript.hour_rate = function(doc) {
cur_frm.add_fetch("item", "image", "image");
cur_frm.add_fetch("item", "item_name", "item_name");
cur_frm.add_fetch("item", "stock_uom", "uom");
cur_frm.cscript.hour_rate = function(doc, dt, dn) {
erpnext.bom.calculate_op_cost(doc); erpnext.bom.calculate_op_cost(doc);
erpnext.bom.calculate_total(doc); erpnext.bom.calculate_total(doc);
} };
cur_frm.cscript.time_in_mins = cur_frm.cscript.hour_rate; cur_frm.cscript.time_in_mins = cur_frm.cscript.hour_rate;
cur_frm.cscript.bom_no = function(doc, cdt, cdn) { cur_frm.cscript.bom_no = function(doc, cdt, cdn) {
get_bom_material_detail(doc, cdt, cdn, false); get_bom_material_detail(doc, cdt, cdn, false);
} };
cur_frm.cscript.is_default = function(doc) { cur_frm.cscript.is_default = function(doc) {
if (doc.is_default) cur_frm.set_value("is_active", 1); if (doc.is_default) cur_frm.set_value("is_active", 1);
} };
var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) { var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) {
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
@ -141,6 +170,7 @@ var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) {
$.extend(d, r.message); $.extend(d, r.message);
refresh_field("items"); refresh_field("items");
refresh_field("scrap_items"); refresh_field("scrap_items");
doc = locals[doc.doctype][doc.name]; doc = locals[doc.doctype][doc.name];
erpnext.bom.calculate_rm_cost(doc); erpnext.bom.calculate_rm_cost(doc);
erpnext.bom.calculate_scrap_materials_cost(doc); erpnext.bom.calculate_scrap_materials_cost(doc);
@ -149,13 +179,13 @@ var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) {
freeze: true freeze: true
}); });
} }
} };
cur_frm.cscript.qty = function(doc, cdt, cdn) { cur_frm.cscript.qty = function(doc) {
erpnext.bom.calculate_rm_cost(doc); erpnext.bom.calculate_rm_cost(doc);
erpnext.bom.calculate_scrap_materials_cost(doc); erpnext.bom.calculate_scrap_materials_cost(doc);
erpnext.bom.calculate_total(doc); erpnext.bom.calculate_total(doc);
} };
cur_frm.cscript.rate = function(doc, cdt, cdn) { cur_frm.cscript.rate = function(doc, cdt, cdn) {
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
@ -173,14 +203,14 @@ cur_frm.cscript.rate = function(doc, cdt, cdn) {
erpnext.bom.calculate_scrap_materials_cost(doc); erpnext.bom.calculate_scrap_materials_cost(doc);
erpnext.bom.calculate_total(doc); erpnext.bom.calculate_total(doc);
} }
} };
erpnext.bom.update_cost = function(doc) { erpnext.bom.update_cost = function(doc) {
erpnext.bom.calculate_op_cost(doc); erpnext.bom.calculate_op_cost(doc);
erpnext.bom.calculate_rm_cost(doc); erpnext.bom.calculate_rm_cost(doc);
erpnext.bom.calculate_scrap_materials_cost(doc); erpnext.bom.calculate_scrap_materials_cost(doc);
erpnext.bom.calculate_total(doc); erpnext.bom.calculate_total(doc);
} };
erpnext.bom.calculate_op_cost = function(doc) { erpnext.bom.calculate_op_cost = function(doc) {
var op = doc.operations || []; var op = doc.operations || [];
@ -189,7 +219,7 @@ erpnext.bom.calculate_op_cost = function(doc) {
for(var i=0;i<op.length;i++) { for(var i=0;i<op.length;i++) {
var operating_cost = flt(flt(op[i].hour_rate) * flt(op[i].time_in_mins) / 60, 2); var operating_cost = flt(flt(op[i].hour_rate) * flt(op[i].time_in_mins) / 60, 2);
var base_operating_cost = flt(flt(op[i].base_hour_rate) * flt(op[i].time_in_mins) / 60, 2); var base_operating_cost = flt(operating_cost * doc.conversion_rate, 2);
frappe.model.set_value('BOM Operation',op[i].name, "operating_cost", operating_cost); frappe.model.set_value('BOM Operation',op[i].name, "operating_cost", operating_cost);
frappe.model.set_value('BOM Operation',op[i].name, "base_operating_cost", base_operating_cost); frappe.model.set_value('BOM Operation',op[i].name, "base_operating_cost", base_operating_cost);
@ -197,7 +227,7 @@ erpnext.bom.calculate_op_cost = function(doc) {
doc.base_operating_cost += base_operating_cost; doc.base_operating_cost += base_operating_cost;
} }
refresh_field(['operating_cost', 'base_operating_cost']); refresh_field(['operating_cost', 'base_operating_cost']);
} };
// rm : raw material // rm : raw material
erpnext.bom.calculate_rm_cost = function(doc) { erpnext.bom.calculate_rm_cost = function(doc) {
@ -206,19 +236,23 @@ erpnext.bom.calculate_rm_cost = function(doc) {
var base_total_rm_cost = 0; var base_total_rm_cost = 0;
for(var i=0;i<rm.length;i++) { for(var i=0;i<rm.length;i++) {
var amount = flt(rm[i].rate) * flt(rm[i].qty); var amount = flt(rm[i].rate) * flt(rm[i].qty);
var base_amount = flt(rm[i].rate) * flt(doc.conversion_rate) * flt(rm[i].qty); var base_amount = amount * flt(doc.conversion_rate);
frappe.model.set_value('BOM Item', rm[i].name, 'base_rate', flt(rm[i].rate) * flt(doc.conversion_rate))
frappe.model.set_value('BOM Item', rm[i].name, 'amount', amount) frappe.model.set_value('BOM Item', rm[i].name, 'base_rate',
frappe.model.set_value('BOM Item', rm[i].name, 'qty_consumed_per_unit', flt(rm[i].qty)/flt(doc.quantity)) flt(rm[i].rate) * flt(doc.conversion_rate));
frappe.model.set_value('BOM Item', rm[i].name, 'base_amount', base_amount) frappe.model.set_value('BOM Item', rm[i].name, 'amount', amount);
frappe.model.set_value('BOM Item', rm[i].name, 'base_amount', base_amount);
frappe.model.set_value('BOM Item', rm[i].name,
'qty_consumed_per_unit', flt(rm[i].stock_qty)/flt(doc.quantity));
total_rm_cost += amount; total_rm_cost += amount;
base_total_rm_cost += base_amount; base_total_rm_cost += base_amount;
} }
cur_frm.set_value("raw_material_cost", total_rm_cost); cur_frm.set_value("raw_material_cost", total_rm_cost);
cur_frm.set_value("base_raw_material_cost", base_total_rm_cost); cur_frm.set_value("base_raw_material_cost", base_total_rm_cost);
} };
//sm : scrap material // sm : scrap material
erpnext.bom.calculate_scrap_materials_cost = function(doc) { erpnext.bom.calculate_scrap_materials_cost = function(doc) {
var sm = doc.scrap_items || []; var sm = doc.scrap_items || [];
var total_sm_cost = 0; var total_sm_cost = 0;
@ -226,8 +260,9 @@ erpnext.bom.calculate_scrap_materials_cost = function(doc) {
for(var i=0;i<sm.length;i++) { for(var i=0;i<sm.length;i++) {
var base_rate = flt(sm[i].rate) * flt(doc.conversion_rate); var base_rate = flt(sm[i].rate) * flt(doc.conversion_rate);
var amount = flt(sm[i].rate) * flt(sm[i].qty); var amount = flt(sm[i].rate) * flt(sm[i].stock_qty);
var base_amount = flt(sm[i].rate) * flt(sm[i].qty) * flt(doc.conversion_rate); var base_amount = amount * flt(doc.conversion_rate);
frappe.model.set_value('BOM Scrap Item',sm[i].name, 'base_rate', base_rate); frappe.model.set_value('BOM Scrap Item',sm[i].name, 'base_rate', base_rate);
frappe.model.set_value('BOM Scrap Item',sm[i].name, 'amount', amount); frappe.model.set_value('BOM Scrap Item',sm[i].name, 'amount', amount);
frappe.model.set_value('BOM Scrap Item',sm[i].name, 'base_amount', base_amount); frappe.model.set_value('BOM Scrap Item',sm[i].name, 'base_amount', base_amount);
@ -238,52 +273,21 @@ erpnext.bom.calculate_scrap_materials_cost = function(doc) {
cur_frm.set_value("scrap_material_cost", total_sm_cost); cur_frm.set_value("scrap_material_cost", total_sm_cost);
cur_frm.set_value("base_scrap_material_cost", base_total_sm_cost); cur_frm.set_value("base_scrap_material_cost", base_total_sm_cost);
} };
// Calculate Total Cost // Calculate Total Cost
erpnext.bom.calculate_total = function(doc) { erpnext.bom.calculate_total = function(doc) {
var total_cost = flt(doc.operating_cost) + flt(doc.raw_material_cost) - flt(doc.scrap_material_cost); var total_cost = flt(doc.operating_cost) + flt(doc.raw_material_cost) - flt(doc.scrap_material_cost);
var base_total_cost = flt(doc.base_operating_cost) + flt(doc.base_raw_material_cost) - flt(doc.base_scrap_material_cost); var base_total_cost = flt(doc.base_operating_cost) + flt(doc.base_raw_material_cost)
- flt(doc.base_scrap_material_cost);
cur_frm.set_value("total_cost", total_cost); cur_frm.set_value("total_cost", total_cost);
cur_frm.set_value("base_total_cost", base_total_cost); cur_frm.set_value("base_total_cost", base_total_cost);
} };
cur_frm.cscript.validate = function(doc) {
cur_frm.fields_dict['item'].get_query = function(doc) { erpnext.bom.update_cost(doc);
return{ };
query: "erpnext.controllers.queries.item_query"
}
}
cur_frm.fields_dict['project'].get_query = function(doc, dt, dn) {
return{
filters:[
['Project', 'status', 'not in', 'Completed, Cancelled']
]
}
}
cur_frm.fields_dict['items'].grid.get_field('item_code').get_query = function(doc) {
return{
query: "erpnext.controllers.queries.item_query",
filters: [["Item", "name", "!=", cur_frm.doc.item]]
}
}
cur_frm.fields_dict['items'].grid.get_field('bom_no').get_query = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
return{
filters:{
'item': d.item_code,
'is_active': 1,
'docstatus': 1
}
}
}
cur_frm.cscript.validate = function(doc, dt, dn) {
erpnext.bom.update_cost(doc)
}
frappe.ui.form.on("BOM Operation", "operation", function(frm, cdt, cdn) { frappe.ui.form.on("BOM Operation", "operation", function(frm, cdt, cdn) {
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
@ -304,7 +308,7 @@ frappe.ui.form.on("BOM Operation", "operation", function(frm, cdt, cdn) {
frappe.model.set_value(d.doctype, d.name, "workstation", data.message.workstation); frappe.model.set_value(d.doctype, d.name, "workstation", data.message.workstation);
} }
} }
}) });
}); });
frappe.ui.form.on("BOM Operation", "workstation", function(frm, cdt, cdn) { frappe.ui.form.on("BOM Operation", "workstation", function(frm, cdt, cdn) {
@ -317,21 +321,22 @@ frappe.ui.form.on("BOM Operation", "workstation", function(frm, cdt, cdn) {
name: d.workstation name: d.workstation
}, },
callback: function (data) { callback: function (data) {
frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate); frappe.model.set_value(d.doctype, d.name, "base_hour_rate", data.message.hour_rate);
frappe.model.set_value(d.doctype, d.name, "base_hour_rate", flt(data.message.hour_rate) * flt(frm.doc.conversion_rate)); frappe.model.set_value(d.doctype, d.name, "hour_rate",
flt(flt(data.message.hour_rate) / flt(frm.doc.conversion_rate)), 2);
erpnext.bom.calculate_op_cost(frm.doc); erpnext.bom.calculate_op_cost(frm.doc);
erpnext.bom.calculate_total(frm.doc); erpnext.bom.calculate_total(frm.doc);
} }
}) });
}); });
frappe.ui.form.on("BOM Item", "qty", function(frm, cdt, cdn) { frappe.ui.form.on("BOM Item", "qty", function(frm, cdt, cdn) {
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
d.stock_qty = d.qty * d.conversion_factor; d.stock_qty = d.qty * d.conversion_factor;
refresh_field("items"); refresh_field("stock_qty", d.name, d.parentfield);
}); });
frappe.ui.form.on("BOM Operation", "operations_remove", function(frm) { frappe.ui.form.on("BOM Operation", "operations_remove", function(frm) {
erpnext.bom.calculate_op_cost(frm.doc); erpnext.bom.calculate_op_cost(frm.doc);
erpnext.bom.calculate_total(frm.doc); erpnext.bom.calculate_total(frm.doc);
@ -344,7 +349,7 @@ frappe.ui.form.on("BOM Item", "items_remove", function(frm) {
var toggle_operations = function(frm) { var toggle_operations = function(frm) {
frm.toggle_display("operations_section", cint(frm.doc.with_operations) == 1); frm.toggle_display("operations_section", cint(frm.doc.with_operations) == 1);
} };
frappe.ui.form.on("BOM", "with_operations", function(frm) { frappe.ui.form.on("BOM", "with_operations", function(frm) {
if(!cint(frm.doc.with_operations)) { if(!cint(frm.doc.with_operations)) {
@ -355,4 +360,4 @@ frappe.ui.form.on("BOM", "with_operations", function(frm) {
cur_frm.cscript.image = function() { cur_frm.cscript.image = function() {
refresh_field("image_view"); refresh_field("image_view");
} };

View File

@ -322,6 +322,37 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -1051,37 +1082,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -1322,7 +1322,7 @@
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 1,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fieldname": "section_break0", "fieldname": "section_break0",

View File

@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe, erpnext
from frappe.utils import cint, cstr, flt from frappe.utils import cint, cstr, flt
from frappe import _ from frappe import _
from erpnext.setup.utils import get_exchange_rate from erpnext.setup.utils import get_exchange_rate
@ -41,16 +41,12 @@ class BOM(WebsiteGenerator):
self.name = 'BOM-' + self.item + ('-%.3i' % idx) self.name = 'BOM-' + self.item + ('-%.3i' % idx)
def validate(self): def validate(self):
# if not self.route:
self.route = frappe.scrub(self.name).replace('_', '-') self.route = frappe.scrub(self.name).replace('_', '-')
self.clear_operations() self.clear_operations()
self.validate_main_item() self.validate_main_item()
self.validate_currency() self.validate_currency()
self.set_conversion_rate() self.set_conversion_rate()
self.validate_uom_is_interger()
from erpnext.utilities.transaction_base import validate_uom_is_integer
validate_uom_is_integer(self, "stock_uom", "stock_qty", "BOM Item")
self.update_stock_qty() self.update_stock_qty()
self.set_bom_material_details() self.set_bom_material_details()
self.validate_materials() self.validate_materials()
@ -96,14 +92,18 @@ class BOM(WebsiteGenerator):
def set_bom_material_details(self): def set_bom_material_details(self):
for item in self.get("items"): for item in self.get("items"):
ret = self.get_bom_material_detail({"item_code": item.item_code, "item_name": item.item_name, "bom_no": item.bom_no, self.validate_bom_currecny(item)
"stock_qty": item.stock_qty})
ret = self.get_bom_material_detail({
"item_code": item.item_code,
"item_name": item.item_name,
"bom_no": item.bom_no,
"stock_qty": item.stock_qty
})
for r in ret: for r in ret:
if not item.get(r): if not item.get(r):
item.set(r, ret[r]) item.set(r, ret[r])
self.validate_bom_currecny(item)
def get_bom_material_detail(self, args=None): def get_bom_material_detail(self, args=None):
""" Get raw material details like uom, desc and rate""" """ Get raw material details like uom, desc and rate"""
if not args: if not args:
@ -126,17 +126,19 @@ class BOM(WebsiteGenerator):
'image' : item and args['image'] or '', 'image' : item and args['image'] or '',
'stock_uom' : item and args['stock_uom'] or '', 'stock_uom' : item and args['stock_uom'] or '',
'uom' : item and args['stock_uom'] or '', 'uom' : item and args['stock_uom'] or '',
'conversion_factor' : 1, 'conversion_factor': 1,
'bom_no' : args['bom_no'], 'bom_no' : args['bom_no'],
'rate' : rate, 'rate' : rate / self.conversion_rate,
'qty' : args.get("qty") or args.get("stock_qty") or 1,
'stock_qty' : args.get("qty") or args.get("stock_qty") or 1, 'stock_qty' : args.get("qty") or args.get("stock_qty") or 1,
'base_rate' : rate if self.company_currency() == self.currency else rate * self.conversion_rate 'base_rate' : rate
} }
return ret_item return ret_item
def validate_bom_currecny(self, item): def validate_bom_currecny(self, item):
if item.get('bom_no') and frappe.db.get_value('BOM', item.get('bom_no'), 'currency') != self.currency: if item.get('bom_no') and frappe.db.get_value('BOM', item.get('bom_no'), 'currency') != self.currency:
frappe.throw(_("Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2}").format(item.idx, item.bom_no, self.currency)) frappe.throw(_("Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2}")
.format(item.idx, item.bom_no, self.currency))
def get_rm_rate(self, arg): def get_rm_rate(self, arg):
""" Get raw material rate as per selected method, if bom exists takes bom cost """ """ Get raw material rate as per selected method, if bom exists takes bom cost """
@ -148,17 +150,21 @@ class BOM(WebsiteGenerator):
if self.rm_cost_as_per == 'Valuation Rate': if self.rm_cost_as_per == 'Valuation Rate':
rate = self.get_valuation_rate(arg) rate = self.get_valuation_rate(arg)
elif self.rm_cost_as_per == 'Last Purchase Rate': elif self.rm_cost_as_per == 'Last Purchase Rate':
rate = arg['last_purchase_rate'] rate = arg['last_purchase_rate'] \
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")
elif self.rm_cost_as_per == "Price List": elif self.rm_cost_as_per == "Price List":
if not self.buying_price_list: if not self.buying_price_list:
frappe.throw(_("Please select Price List")) frappe.throw(_("Please select Price List"))
rate = frappe.db.get_value("Item Price", {"price_list": self.buying_price_list, rate = frappe.db.get_value("Item Price",
"item_code": arg["item_code"]}, "price_list_rate") or 0 {"price_list": self.buying_price_list, "item_code": arg["item_code"]}, "price_list_rate")
price_list_currency = frappe.db.get_value("Price List", self.buying_price_list, "currency")
if price_list_currency != self.company_currency():
rate = flt(rate * self.conversion_rate)
if arg['bom_no'] and (not rate or self.set_rate_of_sub_assembly_item_based_on_bom): if arg['bom_no'] and (not rate or self.set_rate_of_sub_assembly_item_based_on_bom):
rate = self.get_bom_unitcost(arg['bom_no']) rate = self.get_bom_unitcost(arg['bom_no'])
return rate return flt(rate)
def update_cost(self, update_parent=True, from_child_bom=False): def update_cost(self, update_parent=True, from_child_bom=False):
if self.docstatus == 2: if self.docstatus == 2:
@ -167,10 +173,9 @@ class BOM(WebsiteGenerator):
existing_bom_cost = self.total_cost existing_bom_cost = self.total_cost
for d in self.get("items"): for d in self.get("items"):
rate = self.get_bom_material_detail({'item_code': d.item_code, 'bom_no': d.bom_no, rate = self.get_rm_rate({"item_code": d.item_code, "bom_no": d.bom_no})
'stock_qty': d.stock_qty})["rate"]
if rate: if rate:
d.rate = rate d.rate = rate * flt(d.conversion_factor) / flt(self.conversion_rate)
if self.docstatus == 1: if self.docstatus == 1:
self.flags.ignore_validate_update_after_submit = True self.flags.ignore_validate_update_after_submit = True
@ -190,7 +195,7 @@ class BOM(WebsiteGenerator):
frappe.msgprint(_("Cost Updated")) frappe.msgprint(_("Cost Updated"))
def get_bom_unitcost(self, bom_no): def get_bom_unitcost(self, bom_no):
bom = frappe.db.sql("""select name, total_cost/quantity as unit_cost from `tabBOM` bom = frappe.db.sql("""select name, base_total_cost/quantity as unit_cost from `tabBOM`
where is_active = 1 and name = %s""", bom_no, as_dict=1) where is_active = 1 and name = %s""", bom_no, as_dict=1)
return bom and bom[0]['unit_cost'] or 0 return bom and bom[0]['unit_cost'] or 0
@ -253,14 +258,14 @@ class BOM(WebsiteGenerator):
frappe.throw(_("Quantity should be greater than 0")) frappe.throw(_("Quantity should be greater than 0"))
def validate_currency(self): def validate_currency(self):
if self.rm_cost_as_per == 'Price List' and \ if self.rm_cost_as_per == 'Price List':
frappe.db.get_value('Price List', self.buying_price_list, 'currency') != self.currency: price_list_currency = frappe.db.get_value('Price List', self.buying_price_list, 'currency')
frappe.throw(_("Currency of the price list {0} is not similar with the selected currency {1}").format(self.buying_price_list, self.currency)) if price_list_currency not in (self.currency, self.company_currency()):
frappe.throw(_("Currency of the price list {0} must be {1} or {2}")
.format(self.buying_price_list, self.currency, self.company_currency()))
def update_stock_qty(self): def update_stock_qty(self):
for m in self.get('items'): for m in self.get('items'):
if not m.conversion_factor: if not m.conversion_factor:
m.conversion_factor = flt(get_conversion_factor(m.item_code, m.uom)['conversion_factor']) m.conversion_factor = flt(get_conversion_factor(m.item_code, m.uom)['conversion_factor'])
if m.uom and m.qty: if m.uom and m.qty:
@ -269,8 +274,15 @@ class BOM(WebsiteGenerator):
m.uom = m.stock_uom m.uom = m.stock_uom
m.qty = m.stock_qty m.qty = m.stock_qty
def validate_uom_is_interger(self):
from erpnext.utilities.transaction_base import validate_uom_is_integer
validate_uom_is_integer(self, "uom", "qty", "BOM Item")
validate_uom_is_integer(self, "stock_uom", "stock_qty", "BOM Item")
def set_conversion_rate(self): def set_conversion_rate(self):
if self.currency == self.company_currency():
self.conversion_rate = 1
elif self.conversion_rate == 1 or flt(self.conversion_rate) <= 0:
self.conversion_rate = get_exchange_rate(self.currency, self.company_currency()) self.conversion_rate = get_exchange_rate(self.currency, self.company_currency())
def validate_materials(self): def validate_materials(self):
@ -289,7 +301,7 @@ class BOM(WebsiteGenerator):
for m in self.get('items'): for m in self.get('items'):
if m.bom_no: if m.bom_no:
validate_bom_no(m.item_code, m.bom_no) validate_bom_no(m.item_code, m.bom_no)
if flt(m.stock_qty) <= 0: if flt(m.qty) <= 0:
frappe.throw(_("Quantity required for Item {0} in row {1}").format(m.item_code, m.idx)) frappe.throw(_("Quantity required for Item {0} in row {1}").format(m.item_code, m.idx))
check_list.append(m) check_list.append(m)
@ -361,12 +373,13 @@ class BOM(WebsiteGenerator):
for d in self.get('operations'): for d in self.get('operations'):
if d.workstation: if d.workstation:
if not d.hour_rate: if not d.hour_rate:
d.hour_rate = flt(frappe.db.get_value("Workstation", d.workstation, "hour_rate")) hour_rate = flt(frappe.db.get_value("Workstation", d.workstation, "hour_rate"))
d.hour_rate = hour_rate / flt(self.conversion_rate)
if d.hour_rate and d.time_in_mins: if d.hour_rate and d.time_in_mins:
d.operating_cost = flt(d.hour_rate) * flt(d.time_in_mins) / 60.0
d.base_hour_rate = flt(d.hour_rate) * flt(self.conversion_rate) d.base_hour_rate = flt(d.hour_rate) * flt(self.conversion_rate)
d.base_operating_cost = flt(d.base_hour_rate) * flt(d.time_in_mins) / 60.0 d.operating_cost = flt(d.hour_rate) * flt(d.time_in_mins) / 60.0
d.base_operating_cost = flt(d.operating_cost) * flt(self.conversion_rate)
self.operating_cost += flt(d.operating_cost) self.operating_cost += flt(d.operating_cost)
self.base_operating_cost += flt(d.base_operating_cost) self.base_operating_cost += flt(d.base_operating_cost)
@ -378,9 +391,11 @@ class BOM(WebsiteGenerator):
for d in self.get('items'): for d in self.get('items'):
d.base_rate = flt(d.rate) * flt(self.conversion_rate) d.base_rate = flt(d.rate) * flt(self.conversion_rate)
d.amount = flt(d.rate, self.precision("rate", d)) * flt(d.stock_qty, self.precision("stock_qty", d)) d.amount = flt(d.rate, d.precision("rate")) * flt(d.qty, d.precision("qty"))
d.base_amount = d.amount * flt(self.conversion_rate) d.base_amount = d.amount * flt(self.conversion_rate)
d.qty_consumed_per_unit = flt(d.stock_qty, self.precision("stock_qty", d)) / flt(self.quantity, self.precision("quantity")) d.qty_consumed_per_unit = flt(d.stock_qty, d.precision("stock_qty")) \
/ flt(self.quantity, self.precision("quantity"))
total_rm_cost += d.amount total_rm_cost += d.amount
base_total_rm_cost += d.base_amount base_total_rm_cost += d.base_amount
@ -394,7 +409,7 @@ class BOM(WebsiteGenerator):
for d in self.get('scrap_items'): for d in self.get('scrap_items'):
d.base_rate = d.rate * self.conversion_rate d.base_rate = d.rate * self.conversion_rate
d.amount = flt(d.rate, self.precision("rate", d)) * flt(d.stock_qty, self.precision("stock_qty", d)) d.amount = flt(d.rate, d.precision("rate")) * flt(d.stock_qty, d.precision("stock_qty"))
d.base_amount = d.amount * self.conversion_rate d.base_amount = d.amount * self.conversion_rate
total_sm_cost += d.amount total_sm_cost += d.amount
base_total_sm_cost += d.base_amount base_total_sm_cost += d.base_amount
@ -426,7 +441,7 @@ class BOM(WebsiteGenerator):
})) }))
def company_currency(self): def company_currency(self):
return frappe.db.get_value('Company', self.company, 'default_currency') return erpnext.get_company_currency(self.company)
def add_to_cur_exploded_items(self, args): def add_to_cur_exploded_items(self, args):
if self.cur_exploded_items.get(args.item_code): if self.cur_exploded_items.get(args.item_code):
@ -459,6 +474,7 @@ class BOM(WebsiteGenerator):
"Add items to Flat BOM table" "Add items to Flat BOM table"
frappe.db.sql("""delete from `tabBOM Explosion Item` where parent=%s""", self.name) frappe.db.sql("""delete from `tabBOM Explosion Item` where parent=%s""", self.name)
self.set('exploded_items', []) self.set('exploded_items', [])
for d in sorted(self.cur_exploded_items, key=itemgetter(0)): for d in sorted(self.cur_exploded_items, key=itemgetter(0)):
ch = self.append('exploded_items', {}) ch = self.append('exploded_items', {})
for i in self.cur_exploded_items[d].keys(): for i in self.cur_exploded_items[d].keys():

View File

@ -14,14 +14,16 @@ test_records = frappe.get_test_records('BOM')
class TestBOM(unittest.TestCase): class TestBOM(unittest.TestCase):
def test_get_items(self): def test_get_items(self):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
items_dict = get_bom_items_as_dict(bom=get_default_bom(), company="_Test Company", qty=1, fetch_exploded=0) items_dict = get_bom_items_as_dict(bom=get_default_bom(),
company="_Test Company", qty=1, fetch_exploded=0)
self.assertTrue(test_records[2]["items"][0]["item_code"] in items_dict) self.assertTrue(test_records[2]["items"][0]["item_code"] in items_dict)
self.assertTrue(test_records[2]["items"][1]["item_code"] in items_dict) self.assertTrue(test_records[2]["items"][1]["item_code"] in items_dict)
self.assertEquals(len(items_dict.values()), 2) self.assertEquals(len(items_dict.values()), 2)
def test_get_items_exploded(self): def test_get_items_exploded(self):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
items_dict = get_bom_items_as_dict(bom=get_default_bom(), company="_Test Company", qty=1, fetch_exploded=1) items_dict = get_bom_items_as_dict(bom=get_default_bom(),
company="_Test Company", qty=1, fetch_exploded=1)
self.assertTrue(test_records[2]["items"][0]["item_code"] in items_dict) self.assertTrue(test_records[2]["items"][0]["item_code"] in items_dict)
self.assertFalse(test_records[2]["items"][1]["item_code"] in items_dict) self.assertFalse(test_records[2]["items"][1]["item_code"] in items_dict)
self.assertTrue(test_records[0]["items"][0]["item_code"] in items_dict) self.assertTrue(test_records[0]["items"][0]["item_code"] in items_dict)
@ -75,5 +77,50 @@ class TestBOM(unittest.TestCase):
where item_code='_Test Item 2' and docstatus=1""", as_dict=1): where item_code='_Test Item 2' and docstatus=1""", as_dict=1):
self.assertEqual(d.rate, rm_rate + 10) self.assertEqual(d.rate, rm_rate + 10)
def test_bom_cost(self):
bom = frappe.copy_doc(test_records[2])
bom.insert()
# test amounts in selected currency
self.assertEqual(bom.operating_cost, 100)
self.assertEqual(bom.raw_material_cost, 8000)
self.assertEqual(bom.total_cost, 8100)
# test amounts in selected currency
self.assertEqual(bom.base_operating_cost, 6000)
self.assertEqual(bom.base_raw_material_cost, 480000)
self.assertEqual(bom.base_total_cost, 486000)
def test_bom_cost_multi_uom_multi_currency(self):
for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)):
item_price = frappe.new_doc("Item Price")
item_price.price_list = "_Test Price List"
item_price.item_code = item_code
item_price.price_list_rate = rate
item_price.insert()
bom = frappe.copy_doc(test_records[2])
bom.rm_cost_as_per = "Price List"
bom.buying_price_list = "_Test Price List"
bom.items[0].uom = "_Test UOM 1"
bom.items[0].conversion_factor = 5
bom.insert()
bom.update_cost()
# test amounts in selected currency
self.assertEqual(bom.items[0].rate, 300)
self.assertEqual(bom.items[1].rate, 50)
self.assertEqual(bom.operating_cost, 100)
self.assertEqual(bom.raw_material_cost, 450)
self.assertEqual(bom.total_cost, 550)
# test amounts in selected currency
self.assertEqual(bom.items[0].base_rate, 18000)
self.assertEqual(bom.items[1].base_rate, 3000)
self.assertEqual(bom.base_operating_cost, 6000)
self.assertEqual(bom.base_raw_material_cost, 27000)
self.assertEqual(bom.base_total_cost, 33000)
def get_default_bom(item_code="_Test FG Item 2"): def get_default_bom(item_code="_Test FG Item 2"):
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1}) return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})

View File

@ -6,8 +6,9 @@
"doctype": "BOM Item", "doctype": "BOM Item",
"item_code": "_Test Serialized Item With Series", "item_code": "_Test Serialized Item With Series",
"parentfield": "items", "parentfield": "items",
"stock_qty": 1.0, "qty": 1.0,
"rate": 5000.0, "rate": 5000.0,
"uom": "_Test UOM",
"stock_uom": "_Test UOM", "stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC" "source_warehouse": "_Test Warehouse - _TC"
}, },
@ -16,8 +17,9 @@
"doctype": "BOM Item", "doctype": "BOM Item",
"item_code": "_Test Item 2", "item_code": "_Test Item 2",
"parentfield": "items", "parentfield": "items",
"stock_qty": 2.0, "qty": 2.0,
"rate": 1000.0, "rate": 1000.0,
"uom": "_Test UOM",
"stock_uom": "_Test UOM", "stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC" "source_warehouse": "_Test Warehouse - _TC"
} }
@ -34,9 +36,9 @@
"scrap_items":[ "scrap_items":[
{ {
"amount": 2000.0, "amount": 2000.0,
"doctype": "BOM Item", "doctype": "BOM Scrap Item",
"item_code": "_Test Item Home Desktop 100", "item_code": "_Test Item Home Desktop 100",
"parentfield": "items", "parentfield": "scrap_items",
"stock_qty": 1.0, "stock_qty": 1.0,
"rate": 2000.0, "rate": 2000.0,
"stock_uom": "_Test UOM" "stock_uom": "_Test UOM"
@ -48,8 +50,9 @@
"doctype": "BOM Item", "doctype": "BOM Item",
"item_code": "_Test Item", "item_code": "_Test Item",
"parentfield": "items", "parentfield": "items",
"stock_qty": 1.0, "qty": 1.0,
"rate": 5000.0, "rate": 5000.0,
"uom": "_Test UOM",
"stock_uom": "_Test UOM", "stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC" "source_warehouse": "_Test Warehouse - _TC"
}, },
@ -58,8 +61,9 @@
"doctype": "BOM Item", "doctype": "BOM Item",
"item_code": "_Test Item Home Desktop 100", "item_code": "_Test Item Home Desktop 100",
"parentfield": "items", "parentfield": "items",
"stock_qty": 2.0, "qty": 2.0,
"rate": 1000.0, "rate": 1000.0,
"uom": "_Test UOM",
"stock_uom": "_Test UOM", "stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC" "source_warehouse": "_Test Warehouse - _TC"
} }
@ -78,6 +82,7 @@
"operation": "_Test Operation 1", "operation": "_Test Operation 1",
"description": "_Test", "description": "_Test",
"workstation": "_Test Workstation 1", "workstation": "_Test Workstation 1",
"hour_rate": 100,
"time_in_mins": 60, "time_in_mins": 60,
"operating_cost": 100 "operating_cost": 100
} }
@ -88,19 +93,21 @@
"doctype": "BOM Item", "doctype": "BOM Item",
"item_code": "_Test Item", "item_code": "_Test Item",
"parentfield": "items", "parentfield": "items",
"stock_qty": 1.0, "qty": 1.0,
"rate": 5000.0, "rate": 5000.0,
"uom": "_Test UOM",
"stock_uom": "_Test UOM", "stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC" "source_warehouse": "_Test Warehouse - _TC"
}, },
{ {
"amount": 2000.0, "amount": 3000.0,
"bom_no": "BOM-_Test Item Home Desktop Manufactured-001", "bom_no": "BOM-_Test Item Home Desktop Manufactured-001",
"doctype": "BOM Item", "doctype": "BOM Item",
"item_code": "_Test Item Home Desktop Manufactured", "item_code": "_Test Item Home Desktop Manufactured",
"parentfield": "items", "parentfield": "items",
"stock_qty": 3.0, "qty": 3.0,
"rate": 1000.0, "rate": 1000.0,
"uom": "_Test UOM",
"stock_uom": "_Test UOM", "stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC" "source_warehouse": "_Test Warehouse - _TC"
} }
@ -110,6 +117,8 @@
"is_active": 1, "is_active": 1,
"is_default": 1, "is_default": 1,
"currency": "USD", "currency": "USD",
"conversion_rate": 60,
"company": "_Test Company",
"item": "_Test FG Item 2", "item": "_Test FG Item 2",
"quantity": 1.0, "quantity": 1.0,
"with_operations": 1 "with_operations": 1
@ -130,8 +139,9 @@
"doctype": "BOM Item", "doctype": "BOM Item",
"item_code": "_Test Item", "item_code": "_Test Item",
"parentfield": "items", "parentfield": "items",
"stock_qty": 2.0, "qty": 2.0,
"rate": 3000.0, "rate": 3000.0,
"uom": "_Test UOM",
"stock_uom": "_Test UOM", "stock_uom": "_Test UOM",
"source_warehouse": "_Test Warehouse - _TC" "source_warehouse": "_Test Warehouse - _TC"
} }

View File

@ -171,7 +171,7 @@
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 1,
"columns": 0, "columns": 0,
"fieldname": "section_break_5", "fieldname": "section_break_5",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@ -182,6 +182,7 @@
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Description",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
@ -373,7 +374,7 @@
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
@ -404,7 +405,7 @@
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
@ -464,7 +465,39 @@
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "stock_uom",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Stock UOM",
"length": 0,
"no_copy": 0,
"oldfieldname": "stock_uom",
"oldfieldtype": "Data",
"options": "UOM",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
@ -505,8 +538,8 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fieldname": "stock_uom", "fieldname": "rate_amount_section",
"fieldtype": "Link", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
@ -514,16 +547,45 @@
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Stock UOM", "label": "Rate & Amount",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"oldfieldname": "stock_uom", "permlevel": 0,
"oldfieldtype": "Data", "precision": "",
"options": "UOM", "print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "rate",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Rate",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
@ -537,8 +599,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"description": "See \"Rate Of Materials Based On\" in Costing Section", "fieldname": "base_rate",
"fieldname": "rate",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
@ -547,17 +608,47 @@
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Rate", "label": "Basic Rate (Company Currency)",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"options": "currency", "options": "Company:company:default_currency",
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_21",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
@ -596,37 +687,6 @@
"unique": 0, "unique": 0,
"width": "150px" "width": "150px"
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "base_rate",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Basic Rate (Company Currency)",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -700,7 +760,7 @@
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 1, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Scrap %", "label": "Scrap %",
"length": 0, "length": 0,
@ -760,7 +820,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-07-04 17:42:37.218408", "modified": "2017-08-18 16:22:46.078661",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "BOM Item", "name": "BOM Item",

View File

@ -437,3 +437,4 @@ erpnext.patches.v8_7.fix_purchase_receipt_status
erpnext.patches.v8_6.rename_bom_update_tool erpnext.patches.v8_6.rename_bom_update_tool
erpnext.patches.v8_9.add_setup_progress_actions erpnext.patches.v8_9.add_setup_progress_actions
erpnext.patches.v8_9.rename_company_sales_target_field erpnext.patches.v8_9.rename_company_sales_target_field
erpnext.patches.v8_8.set_bom_rate_as_per_uom

View File

View File

@ -0,0 +1,13 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.db.sql("""
update `tabBOM Item`
set rate = rate * conversion_factor
where uom != stock_uom and docstatus < 2
and conversion_factor not in (0, 1)
""")