fix: bom incorrect price list rate for raw material if price list currency is different from company currency (#21585)

* fix: bom incorrect price list rate for raw material if price list currency is different from company currency

* fixed test cases

* fixed base_rate calculation and added plc_conversion_rate trigger
This commit is contained in:
rohitwaghchaure 2020-05-18 14:23:30 +05:30 committed by GitHub
parent 1e7bc3a97e
commit be750096ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 105 additions and 20 deletions

View File

@ -212,6 +212,12 @@ frappe.ui.form.on("BOM", {
}); });
}, },
rm_cost_as_per: function(frm) {
if (in_list(["Valuation Rate", "Last Purchase Rate"], frm.doc.rm_cost_as_per)) {
frm.set_value("plc_conversion_rate", 1.0);
}
},
routing: function(frm) { routing: function(frm) {
if (frm.doc.routing) { if (frm.doc.routing) {
frappe.call({ frappe.call({
@ -252,6 +258,17 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({
get_bom_material_detail(doc, cdt, cdn, scrap_items); get_bom_material_detail(doc, cdt, cdn, scrap_items);
}, },
buying_price_list: function(doc) {
this.apply_price_list();
},
plc_conversion_rate: function(doc) {
if (!this.in_apply_price_list) {
this.apply_price_list();
}
},
conversion_factor: function(doc, cdt, cdn) { 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);

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"allow_import": 1, "allow_import": 1,
"creation": "2013-01-22 15:11:38", "creation": "2013-01-22 15:11:38",
"doctype": "DocType", "doctype": "DocType",
@ -6,23 +7,25 @@
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"item", "item",
"quantity", "company",
"set_rate_of_sub_assembly_item_based_on_bom", "item_name",
"uom",
"cb0", "cb0",
"is_active", "is_active",
"is_default", "is_default",
"allow_alternative_item", "allow_alternative_item",
"image", "set_rate_of_sub_assembly_item_based_on_bom",
"item_name",
"uom",
"currency_detail",
"company",
"project", "project",
"quantity",
"image",
"currency_detail",
"currency",
"conversion_rate", "conversion_rate",
"column_break_12", "column_break_12",
"currency",
"rm_cost_as_per", "rm_cost_as_per",
"buying_price_list", "buying_price_list",
"price_list_currency",
"plc_conversion_rate",
"section_break_21", "section_break_21",
"with_operations", "with_operations",
"column_break_23", "column_break_23",
@ -176,7 +179,8 @@
}, },
{ {
"fieldname": "currency_detail", "fieldname": "currency_detail",
"fieldtype": "Section Break" "fieldtype": "Section Break",
"label": "Currency and Price List"
}, },
{ {
"fieldname": "company", "fieldname": "company",
@ -324,7 +328,7 @@
}, },
{ {
"fieldname": "base_scrap_material_cost", "fieldname": "base_scrap_material_cost",
"fieldtype": "Data", "fieldtype": "Currency",
"label": "Scrap Material Cost(Company Currency)", "label": "Scrap Material Cost(Company Currency)",
"no_copy": 1, "no_copy": 1,
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
@ -477,13 +481,31 @@
{ {
"fieldname": "column_break_52", "fieldname": "column_break_52",
"fieldtype": "Column Break" "fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
"depends_on": "eval:doc.rm_cost_as_per=='Price List'",
"fieldname": "plc_conversion_rate",
"fieldtype": "Float",
"label": "Price List Exchange Rate"
},
{
"allow_on_submit": 1,
"depends_on": "eval:doc.rm_cost_as_per=='Price List'",
"fieldname": "price_list_currency",
"fieldtype": "Link",
"label": "Price List Currency",
"options": "Currency",
"print_hide": 1,
"read_only": 1
} }
], ],
"icon": "fa fa-sitemap", "icon": "fa fa-sitemap",
"idx": 1, "idx": 1,
"image_field": "image", "image_field": "image",
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-11-22 14:35:12.142150", "links": [],
"modified": "2020-05-05 14:29:32.634952",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "BOM", "name": "BOM",

View File

@ -70,11 +70,13 @@ class BOM(WebsiteGenerator):
self.validate_main_item() self.validate_main_item()
self.validate_currency() self.validate_currency()
self.set_conversion_rate() self.set_conversion_rate()
self.set_plc_conversion_rate()
self.validate_uom_is_interger() self.validate_uom_is_interger()
self.set_bom_material_details() self.set_bom_material_details()
self.validate_materials() self.validate_materials()
self.validate_operations() self.validate_operations()
self.calculate_cost() self.calculate_cost()
self.update_cost(update_parent=False, from_child_bom=True, save=False)
def get_context(self, context): def get_context(self, context):
context.parents = [{'name': 'boms', 'title': _('All BOMs') }] context.parents = [{'name': 'boms', 'title': _('All BOMs') }]
@ -165,7 +167,7 @@ class BOM(WebsiteGenerator):
'rate' : rate, 'rate' : rate,
'qty' : args.get("qty") or args.get("stock_qty") or 1, '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, 'base_rate' : flt(rate) * (flt(self.conversion_rate) or 1),
'include_item_in_manufacturing': cint(args['transfer_for_manufacture']) or 0 'include_item_in_manufacturing': cint(args['transfer_for_manufacture']) or 0
} }
@ -226,7 +228,7 @@ class BOM(WebsiteGenerator):
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) / (self.conversion_rate or 1) return flt(rate) * flt(self.plc_conversion_rate or 1) / (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:
@ -243,9 +245,14 @@ class BOM(WebsiteGenerator):
"stock_uom": d.stock_uom, "stock_uom": d.stock_uom,
"conversion_factor": d.conversion_factor "conversion_factor": d.conversion_factor
}) })
if rate: if rate:
d.rate = rate d.rate = rate
d.amount = flt(d.rate) * flt(d.qty) d.amount = flt(d.rate) * flt(d.qty)
d.base_rate = flt(d.rate) * flt(self.conversion_rate)
d.base_amount = flt(d.amount) * flt(self.conversion_rate)
if save:
d.db_update() d.db_update()
if self.docstatus == 1: if self.docstatus == 1:
@ -372,6 +379,13 @@ class BOM(WebsiteGenerator):
elif self.conversion_rate == 1 or flt(self.conversion_rate) <= 0: elif self.conversion_rate == 1 or flt(self.conversion_rate) <= 0:
self.conversion_rate = get_exchange_rate(self.currency, self.company_currency(), args="for_buying") self.conversion_rate = get_exchange_rate(self.currency, self.company_currency(), args="for_buying")
def set_plc_conversion_rate(self):
if self.rm_cost_as_per in ["Valuation Rate", "Last Purchase Rate"]:
self.plc_conversion_rate = 1
elif not self.plc_conversion_rate and self.price_list_currency:
self.plc_conversion_rate = get_exchange_rate(self.price_list_currency,
self.company_currency(), args="for_buying")
def validate_materials(self): def validate_materials(self):
""" Validate raw material entries """ """ Validate raw material entries """

View File

@ -81,13 +81,13 @@ class TestBOM(unittest.TestCase):
# test amounts in selected currency # test amounts in selected currency
self.assertEqual(bom.operating_cost, 100) self.assertEqual(bom.operating_cost, 100)
self.assertEqual(bom.raw_material_cost, 8000) self.assertEqual(bom.raw_material_cost, 351.68)
self.assertEqual(bom.total_cost, 8100) self.assertEqual(bom.total_cost, 451.68)
# test amounts in selected currency # test amounts in selected currency
self.assertEqual(bom.base_operating_cost, 6000) self.assertEqual(bom.base_operating_cost, 6000)
self.assertEqual(bom.base_raw_material_cost, 480000) self.assertEqual(bom.base_raw_material_cost, 21100.80)
self.assertEqual(bom.base_total_cost, 486000) self.assertEqual(bom.base_total_cost, 27100.80)
def test_bom_cost_multi_uom_multi_currency_based_on_price_list(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_dependent", 1) frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1)

View File

@ -685,3 +685,4 @@ execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True)
erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price
erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation
erpnext.patches.v12_0.set_serial_no_status erpnext.patches.v12_0.set_serial_no_status
erpnext.patches.v12_0.update_price_list_currency_in_bom

View File

@ -0,0 +1,31 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import getdate, flt
from erpnext.setup.utils import get_exchange_rate
def execute():
frappe.reload_doc("manufacturing", "doctype", "bom")
frappe.reload_doc("manufacturing", "doctype", "bom_item")
frappe.db.sql(""" UPDATE `tabBOM`, `tabPrice List`
SET
`tabBOM`.price_list_currency = `tabPrice List`.currency,
`tabBOM`.plc_conversion_rate = 1.0
WHERE
`tabBOM`.buying_price_list = `tabPrice List`.name AND `tabBOM`.docstatus < 2
AND `tabBOM`.rm_cost_as_per = 'Price List'
""")
for d in frappe.db.sql("""
SELECT
bom.creation, bom.name, bom.price_list_currency as currency,
company.default_currency as company_currency
FROM
`tabBOM` as bom, `tabCompany` as company
WHERE
bom.company = company.name AND bom.rm_cost_as_per = 'Price List' AND
bom.price_list_currency != company.default_currency AND bom.docstatus < 2""", as_dict=1):
plc_conversion_rate = get_exchange_rate(d.currency,
d.company_currency, getdate(d.creation), "for_buying")
frappe.db.set_value("BOM", d.name, "plc_conversion_rate", plc_conversion_rate)