Merge pull request #17645 from nabinhait/bom-item-rate-dev
fix: BOM Item rate based on uom conversion factor and exchange rate
This commit is contained in:
commit
45cd60dfb2
@ -205,7 +205,12 @@ var get_bom_material_detail= function(doc, cdt, cdn, scrap_items) {
|
|||||||
'item_code': d.item_code,
|
'item_code': d.item_code,
|
||||||
'bom_no': d.bom_no != null ? d.bom_no: '',
|
'bom_no': d.bom_no != null ? d.bom_no: '',
|
||||||
"scrap_items": scrap_items,
|
"scrap_items": scrap_items,
|
||||||
'qty': d.qty
|
'qty': d.qty,
|
||||||
|
"stock_qty": d.stock_qty,
|
||||||
|
"include_item_in_manufacturing": d.include_item_in_manufacturing,
|
||||||
|
"uom": d.uom,
|
||||||
|
"stock_uom": d.stock_uom,
|
||||||
|
"conversion_factor": d.conversion_factor
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
d = locals[cdt][cdn];
|
d = locals[cdt][cdn];
|
||||||
|
@ -172,13 +172,14 @@ class BOM(WebsiteGenerator):
|
|||||||
#Customer Provided parts will have zero rate
|
#Customer Provided parts will have zero rate
|
||||||
if not frappe.db.get_value('Item', arg["item_code"], 'is_customer_provided_item'):
|
if not frappe.db.get_value('Item', arg["item_code"], 'is_customer_provided_item'):
|
||||||
if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom:
|
if arg.get('bom_no') and 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']) * (arg.get("conversion_factor") or 1)
|
||||||
else:
|
else:
|
||||||
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) * (arg.get("conversion_factor") or 1)
|
||||||
elif self.rm_cost_as_per == 'Last Purchase Rate':
|
elif self.rm_cost_as_per == 'Last Purchase Rate':
|
||||||
rate = arg.get('last_purchase_rate') \
|
rate = (arg.get('last_purchase_rate') \
|
||||||
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")
|
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")) \
|
||||||
|
* (arg.get("conversion_factor") or 1)
|
||||||
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"))
|
||||||
@ -191,7 +192,7 @@ class BOM(WebsiteGenerator):
|
|||||||
"transaction_type": "buying",
|
"transaction_type": "buying",
|
||||||
"company": self.company,
|
"company": self.company,
|
||||||
"currency": self.currency,
|
"currency": self.currency,
|
||||||
"conversion_rate": self.conversion_rate or 1,
|
"conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function
|
||||||
"conversion_factor": arg.get("conversion_factor") or 1,
|
"conversion_factor": arg.get("conversion_factor") or 1,
|
||||||
"plc_conversion_rate": 1,
|
"plc_conversion_rate": 1,
|
||||||
"ignore_party": True
|
"ignore_party": True
|
||||||
@ -203,13 +204,13 @@ class BOM(WebsiteGenerator):
|
|||||||
|
|
||||||
if not rate:
|
if not rate:
|
||||||
if self.rm_cost_as_per == "Price List":
|
if self.rm_cost_as_per == "Price List":
|
||||||
frappe.msgprint(_("Price not found for item {0} and price list {1}")
|
frappe.msgprint(_("Price not found for item {0} in price list {1}")
|
||||||
.format(arg["item_code"], self.buying_price_list), alert=True)
|
.format(arg["item_code"], self.buying_price_list), alert=True)
|
||||||
else:
|
else:
|
||||||
frappe.msgprint(_("{0} not found for item {1}")
|
frappe.msgprint(_("{0} not found for item {1}")
|
||||||
.format(self.rm_cost_as_per, arg["item_code"]), alert=True)
|
.format(self.rm_cost_as_per, arg["item_code"]), alert=True)
|
||||||
|
|
||||||
return flt(rate)
|
return flt(rate) / (self.conversion_rate or 1)
|
||||||
|
|
||||||
def update_cost(self, update_parent=True, from_child_bom=False, save=True):
|
def update_cost(self, update_parent=True, from_child_bom=False, save=True):
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
|
@ -9,6 +9,7 @@ from frappe.utils import cstr
|
|||||||
from frappe.test_runner import make_test_records
|
from frappe.test_runner import make_test_records
|
||||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
|
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
|
||||||
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
|
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
|
||||||
|
from six import string_types
|
||||||
|
|
||||||
test_records = frappe.get_test_records('BOM')
|
test_records = frappe.get_test_records('BOM')
|
||||||
|
|
||||||
@ -63,16 +64,8 @@ class TestBOM(unittest.TestCase):
|
|||||||
and item_code='_Test Item 2' and docstatus=1 and parenttype='BOM'""")
|
and item_code='_Test Item 2' and docstatus=1 and parenttype='BOM'""")
|
||||||
rm_rate = rm_rate[0][0] if rm_rate else 0
|
rm_rate = rm_rate[0][0] if rm_rate else 0
|
||||||
|
|
||||||
# update valuation rate of item '_Test Item 2'
|
# Reset item valuation rate
|
||||||
warehouse_list = frappe.db.sql_list("""select warehouse from `tabBin`
|
reset_item_valuation_rate(item_code='_Test Item 2', qty=200, rate=rm_rate + 10)
|
||||||
where item_code='_Test Item 2' and actual_qty > 0""")
|
|
||||||
|
|
||||||
if not warehouse_list:
|
|
||||||
warehouse_list.append("_Test Warehouse - _TC")
|
|
||||||
|
|
||||||
for warehouse in warehouse_list:
|
|
||||||
create_stock_reconciliation(item_code="_Test Item 2", warehouse=warehouse,
|
|
||||||
qty=200, rate=rm_rate + 10)
|
|
||||||
|
|
||||||
# update cost of all BOMs based on latest valuation rate
|
# update cost of all BOMs based on latest valuation rate
|
||||||
update_cost()
|
update_cost()
|
||||||
@ -96,7 +89,7 @@ class TestBOM(unittest.TestCase):
|
|||||||
self.assertEqual(bom.base_raw_material_cost, 480000)
|
self.assertEqual(bom.base_raw_material_cost, 480000)
|
||||||
self.assertEqual(bom.base_total_cost, 486000)
|
self.assertEqual(bom.base_total_cost, 486000)
|
||||||
|
|
||||||
def test_bom_cost_multi_uom_multi_currency(self):
|
def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self):
|
||||||
frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependant", 1)
|
frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependant", 1)
|
||||||
for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)):
|
for item_code, rate in (("_Test Item", 3600), ("_Test Item Home Desktop Manufactured", 3000)):
|
||||||
frappe.db.sql("delete from `tabItem Price` where price_list='_Test Price List' and item_code=%s",
|
frappe.db.sql("delete from `tabItem Price` where price_list='_Test Price List' and item_code=%s",
|
||||||
@ -131,5 +124,35 @@ class TestBOM(unittest.TestCase):
|
|||||||
self.assertEqual(bom.base_raw_material_cost, 27000)
|
self.assertEqual(bom.base_raw_material_cost, 27000)
|
||||||
self.assertEqual(bom.base_total_cost, 33000)
|
self.assertEqual(bom.base_total_cost, 33000)
|
||||||
|
|
||||||
|
def test_bom_cost_multi_uom_based_on_valuation_rate(self):
|
||||||
|
bom = frappe.copy_doc(test_records[2])
|
||||||
|
bom.set_rate_of_sub_assembly_item_based_on_bom = 0
|
||||||
|
bom.rm_cost_as_per = "Valuation Rate"
|
||||||
|
bom.items[0].uom = "_Test UOM 1"
|
||||||
|
bom.items[0].conversion_factor = 6
|
||||||
|
bom.insert()
|
||||||
|
|
||||||
|
reset_item_valuation_rate(item_code='_Test Item', qty=200, rate=200)
|
||||||
|
|
||||||
|
bom.update_cost()
|
||||||
|
|
||||||
|
self.assertEqual(bom.items[0].rate, 20)
|
||||||
|
|
||||||
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})
|
||||||
|
|
||||||
|
def reset_item_valuation_rate(item_code, warehouse_list=None, qty=None, rate=None):
|
||||||
|
if warehouse_list and isinstance(warehouse_list, string_types):
|
||||||
|
warehouse_list = [warehouse_list]
|
||||||
|
|
||||||
|
if not warehouse_list:
|
||||||
|
warehouse_list = frappe.db.sql_list("""
|
||||||
|
select warehouse from `tabBin`
|
||||||
|
where item_code=%s and actual_qty > 0
|
||||||
|
""", item_code)
|
||||||
|
|
||||||
|
if not warehouse_list:
|
||||||
|
warehouse_list.append("_Test Warehouse - _TC")
|
||||||
|
|
||||||
|
for warehouse in warehouse_list:
|
||||||
|
create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=qty, rate=rate)
|
||||||
|
@ -438,7 +438,7 @@ def get_price_list_rate(args, item_doc, out):
|
|||||||
pl_details = get_price_list_currency_and_exchange_rate(args)
|
pl_details = get_price_list_currency_and_exchange_rate(args)
|
||||||
args.update(pl_details)
|
args.update(pl_details)
|
||||||
validate_price_list(args)
|
validate_price_list(args)
|
||||||
if meta.get_field("currency") and args.price_list:
|
if meta.get_field("currency"):
|
||||||
validate_conversion_rate(args, meta)
|
validate_conversion_rate(args, meta)
|
||||||
|
|
||||||
price_list_rate = get_price_list_rate_for(args, item_doc.name) or 0
|
price_list_rate = get_price_list_rate_for(args, item_doc.name) or 0
|
||||||
@ -615,21 +615,22 @@ def validate_conversion_rate(args, meta):
|
|||||||
get_field_precision(meta.get_field("conversion_rate"),
|
get_field_precision(meta.get_field("conversion_rate"),
|
||||||
frappe._dict({"fields": args})))
|
frappe._dict({"fields": args})))
|
||||||
|
|
||||||
if (not args.plc_conversion_rate
|
if args.price_list:
|
||||||
and args.price_list_currency==frappe.db.get_value("Price List", args.price_list, "currency", cache=True)):
|
if (not args.plc_conversion_rate
|
||||||
args.plc_conversion_rate = 1.0
|
and args.price_list_currency==frappe.db.get_value("Price List", args.price_list, "currency", cache=True)):
|
||||||
|
args.plc_conversion_rate = 1.0
|
||||||
|
|
||||||
# validate price list currency conversion rate
|
# validate price list currency conversion rate
|
||||||
if not args.get("price_list_currency"):
|
if not args.get("price_list_currency"):
|
||||||
throw(_("Price List Currency not selected"))
|
throw(_("Price List Currency not selected"))
|
||||||
else:
|
else:
|
||||||
validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate,
|
validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate,
|
||||||
meta.get_label("plc_conversion_rate"), args.company)
|
meta.get_label("plc_conversion_rate"), args.company)
|
||||||
|
|
||||||
if meta.get_field("plc_conversion_rate"):
|
if meta.get_field("plc_conversion_rate"):
|
||||||
args.plc_conversion_rate = flt(args.plc_conversion_rate,
|
args.plc_conversion_rate = flt(args.plc_conversion_rate,
|
||||||
get_field_precision(meta.get_field("plc_conversion_rate"),
|
get_field_precision(meta.get_field("plc_conversion_rate"),
|
||||||
frappe._dict({"fields": args})))
|
frappe._dict({"fields": args})))
|
||||||
|
|
||||||
def get_party_item_code(args, item_doc, out):
|
def get_party_item_code(args, item_doc, out):
|
||||||
if args.transaction_type=="selling" and args.customer:
|
if args.transaction_type=="selling" and args.customer:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user