Skip to content

Commit 0902198

Browse files
committed
fix: BOM Item rate based on uom conversion factor and exchange rate
1 parent 9a76815 commit 0902198

4 files changed

Lines changed: 62 additions & 32 deletions

File tree

erpnext/manufacturing/doctype/bom/bom.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,12 @@ var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) {
205205
'item_code': d.item_code,
206206
'bom_no': d.bom_no != null ? d.bom_no: '',
207207
"scrap_items": scrap_items,
208-
'qty': d.qty
208+
'qty': d.qty,
209+
"stock_qty": d.stock_qty,
210+
"include_item_in_manufacturing": d.include_item_in_manufacturing,
211+
"uom": d.uom,
212+
"stock_uom": d.stock_uom,
213+
"conversion_factor": d.conversion_factor
209214
},
210215
callback: function(r) {
211216
d = locals[cdt][cdn];

erpnext/manufacturing/doctype/bom/bom.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,14 @@ def get_rm_rate(self, arg):
170170
rate = self.get_valuation_rate(arg)
171171
elif arg:
172172
if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom:
173-
rate = self.get_bom_unitcost(arg['bom_no'])
173+
rate = self.get_bom_unitcost(arg['bom_no']) * (arg.get("conversion_factor") or 1)
174174
else:
175175
if self.rm_cost_as_per == 'Valuation Rate':
176-
rate = self.get_valuation_rate(arg)
176+
rate = self.get_valuation_rate(arg) * (arg.get("conversion_factor") or 1)
177177
elif self.rm_cost_as_per == 'Last Purchase Rate':
178-
rate = arg.get('last_purchase_rate') \
179-
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")
178+
rate = (arg.get('last_purchase_rate') \
179+
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")) \
180+
* (arg.get("conversion_factor") or 1)
180181
elif self.rm_cost_as_per == "Price List":
181182
if not self.buying_price_list:
182183
frappe.throw(_("Please select Price List"))
@@ -189,7 +190,7 @@ def get_rm_rate(self, arg):
189190
"transaction_type": "buying",
190191
"company": self.company,
191192
"currency": self.currency,
192-
"conversion_rate": self.conversion_rate or 1,
193+
"conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function
193194
"conversion_factor": arg.get("conversion_factor") or 1,
194195
"plc_conversion_rate": 1,
195196
"ignore_party": True
@@ -207,7 +208,7 @@ def get_rm_rate(self, arg):
207208
frappe.msgprint(_("{0} not found for item {1}")
208209
.format(self.rm_cost_as_per, arg["item_code"]), alert=True)
209210

210-
return flt(rate)
211+
return flt(rate) / (self.conversion_rate or 1)
211212

212213
def update_cost(self, update_parent=True, from_child_bom=False, save=True):
213214
if self.docstatus == 2:

erpnext/manufacturing/doctype/bom/test_bom.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from frappe.test_runner import make_test_records
1010
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
1111
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
12+
from six import string_types
1213

1314
test_records = frappe.get_test_records('BOM')
1415

@@ -63,16 +64,8 @@ def test_update_bom_cost_in_all_boms(self):
6364
and item_code='_Test Item 2' and docstatus=1 and parenttype='BOM'""")
6465
rm_rate = rm_rate[0][0] if rm_rate else 0
6566

66-
# update valuation rate of item '_Test Item 2'
67-
warehouse_list = frappe.db.sql_list("""select warehouse from `tabBin`
68-
where item_code='_Test Item 2' and actual_qty > 0""")
69-
70-
if not warehouse_list:
71-
warehouse_list.append("_Test Warehouse - _TC")
72-
73-
for warehouse in warehouse_list:
74-
create_stock_reconciliation(item_code="_Test Item 2", warehouse=warehouse,
75-
qty=200, rate=rm_rate + 10)
67+
# Reset item valuation rate
68+
reset_item_valuation_rate(item_code='_Test Item 2', qty=200, rate=rm_rate + 10)
7669

7770
# update cost of all BOMs based on latest valuation rate
7871
update_cost()
@@ -96,7 +89,7 @@ def test_bom_cost(self):
9689
self.assertEqual(bom.base_raw_material_cost, 480000)
9790
self.assertEqual(bom.base_total_cost, 486000)
9891

99-
def test_bom_cost_multi_uom_multi_currency(self):
92+
def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self):
10093
frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependant", 1)
10194
for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)):
10295
frappe.db.sql("delete from `tabItem Price` where price_list='_Test Price List' and item_code=%s",
@@ -131,5 +124,35 @@ def test_bom_cost_multi_uom_multi_currency(self):
131124
self.assertEqual(bom.base_raw_material_cost, 27000)
132125
self.assertEqual(bom.base_total_cost, 33000)
133126

127+
def test_bom_cost_multi_uom_based_on_valuation_rate(self):
128+
bom = frappe.copy_doc(test_records[2])
129+
bom.set_rate_of_sub_assembly_item_based_on_bom = 0
130+
bom.rm_cost_as_per = "Valuation Rate"
131+
bom.items[0].uom = "_Test UOM 1"
132+
bom.items[0].conversion_factor = 6
133+
bom.insert()
134+
135+
reset_item_valuation_rate(item_code='_Test Item', qty=200, rate=200)
136+
137+
bom.update_cost()
138+
139+
self.assertEqual(bom.items[0].rate, 20)
140+
134141
def get_default_bom(item_code="_Test FG Item 2"):
135142
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
143+
144+
def reset_item_valuation_rate(item_code, warehouse_list=None, qty=None, rate=None):
145+
if warehouse_list and isinstance(warehouse_list, string_types):
146+
warehouse_list = [warehouse_list]
147+
148+
if not warehouse_list:
149+
warehouse_list = frappe.db.sql_list("""
150+
select warehouse from `tabBin`
151+
where item_code=%s and actual_qty > 0
152+
""", item_code)
153+
154+
if not warehouse_list:
155+
warehouse_list.append("_Test Warehouse - _TC")
156+
157+
for warehouse in warehouse_list:
158+
create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=qty, rate=rate)

erpnext/stock/get_item_details.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ def get_price_list_rate(args, item_doc, out):
390390
pl_details = get_price_list_currency_and_exchange_rate(args)
391391
args.update(pl_details)
392392
validate_price_list(args)
393-
if meta.get_field("currency") and args.price_list:
393+
if meta.get_field("currency"):
394394
validate_conversion_rate(args, meta)
395395

396396
price_list_rate = get_price_list_rate_for(args, item_doc.name) or 0
@@ -567,21 +567,22 @@ def validate_conversion_rate(args, meta):
567567
get_field_precision(meta.get_field("conversion_rate"),
568568
frappe._dict({"fields": args})))
569569

570-
if (not args.plc_conversion_rate
571-
and args.price_list_currency==frappe.db.get_value("Price List", args.price_list, "currency", cache=True)):
572-
args.plc_conversion_rate = 1.0
570+
if args.price_list:
571+
if (not args.plc_conversion_rate
572+
and args.price_list_currency==frappe.db.get_value("Price List", args.price_list, "currency", cache=True)):
573+
args.plc_conversion_rate = 1.0
573574

574-
# validate price list currency conversion rate
575-
if not args.get("price_list_currency"):
576-
throw(_("Price List Currency not selected"))
577-
else:
578-
validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate,
579-
meta.get_label("plc_conversion_rate"), args.company)
575+
# validate price list currency conversion rate
576+
if not args.get("price_list_currency"):
577+
throw(_("Price List Currency not selected"))
578+
else:
579+
validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate,
580+
meta.get_label("plc_conversion_rate"), args.company)
580581

581-
if meta.get_field("plc_conversion_rate"):
582-
args.plc_conversion_rate = flt(args.plc_conversion_rate,
583-
get_field_precision(meta.get_field("plc_conversion_rate"),
584-
frappe._dict({"fields": args})))
582+
if meta.get_field("plc_conversion_rate"):
583+
args.plc_conversion_rate = flt(args.plc_conversion_rate,
584+
get_field_precision(meta.get_field("plc_conversion_rate"),
585+
frappe._dict({"fields": args})))
585586

586587
def get_party_item_code(args, item_doc, out):
587588
if args.transaction_type=="selling" and args.customer:

0 commit comments

Comments
 (0)