diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index bbaa043208..e767ae7df6 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -29,7 +29,7 @@ class PurchaseOrder(BuyingController): 'target_field': 'ordered_qty', 'target_parent_dt': 'Material Request', 'target_parent_field': 'per_ordered', - 'target_ref_field': 'qty', + 'target_ref_field': 'stock_qty', 'source_field': 'stock_qty', 'percent_join_field': 'material_request', 'overflow_type': 'order' diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 25a164071c..cc42ae9fc9 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -466,3 +466,4 @@ erpnext.patches.v9_2.delete_healthcare_domain_default_items erpnext.patches.v9_1.create_issue_opportunity_type erpnext.patches.v9_2.rename_translated_domains_in_en erpnext.patches.v9_0.set_shipping_type_for_existing_shipping_rules +erpnext.patches.v9_0.update_multi_uom_fields_in_material_request \ No newline at end of file diff --git a/erpnext/patches/v9_0/update_multi_uom_fields_in_material_request.py b/erpnext/patches/v9_0/update_multi_uom_fields_in_material_request.py new file mode 100644 index 0000000000..45610ed5a7 --- /dev/null +++ b/erpnext/patches/v9_0/update_multi_uom_fields_in_material_request.py @@ -0,0 +1,12 @@ +# 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.reload_doctype('Material Request') + frappe.reload_doctype('Material Request Item') + + frappe.db.sql(""" update `tabMaterial Request Item` + set stock_uom = uom, stock_qty = qty, conversion_factor = 1.0""") \ No newline at end of file diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 621e9a749e..f93d76ec78 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -650,7 +650,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ 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); - if(!dont_fetch_price_list_rate) this.apply_price_list(item, true); + if (!dont_fetch_price_list_rate && + frappe.meta.has_field(doc.doctype, "price_list_currency")) { + this.apply_price_list(item, true); + } } }, diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 59075c588b..da310aa3d7 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -158,7 +158,7 @@ class MaterialRequest(BuyingController): and material_request_item = %s and docstatus = 1""", (self.name, d.name))[0][0]) - if d.ordered_qty and d.ordered_qty > d.qty: + if d.ordered_qty and d.ordered_qty > d.stock_qty: frappe.throw(_("The total Issue / Transfer quantity {0} in Material Request {1} \ cannot be greater than requested quantity {2} for Item {3}").format(d.ordered_qty, d.parent, d.qty, d.item_code)) @@ -170,11 +170,12 @@ class MaterialRequest(BuyingController): frappe.db.set_value(d.doctype, d.name, "ordered_qty", d.ordered_qty) + target_ref_field = 'qty' if self.material_request_type == "Manufacture" else 'stock_qty' self._update_percent_field({ "target_dt": "Material Request Item", "target_parent_dt": self.doctype, "target_parent_field": "per_ordered", - "target_ref_field": "qty", + "target_ref_field": target_ref_field, "target_field": "ordered_qty", "name": self.name, }, update_modified) @@ -216,9 +217,9 @@ def set_missing_values(source, target_doc): target_doc.run_method("calculate_taxes_and_totals") def update_item(obj, target, source_parent): - target.conversion_factor = 1 - target.qty = flt(obj.qty) - flt(obj.ordered_qty) - target.stock_qty = target.qty + target.conversion_factor = obj.conversion_factor + target.qty = flt(flt(obj.stock_qty) - flt(obj.ordered_qty))/ target.conversion_factor + target.stock_qty = (target.qty * target.conversion_factor) @frappe.whitelist() def make_purchase_order(source_name, target_doc=None): @@ -242,7 +243,7 @@ def make_purchase_order(source_name, target_doc=None): ["uom", "uom"] ], "postprocess": update_item, - "condition": lambda doc: doc.ordered_qty < doc.qty + "condition": lambda doc: doc.ordered_qty < doc.stock_qty } }, target_doc, postprocess) @@ -353,11 +354,11 @@ def make_supplier_quotation(source_name, target_doc=None): @frappe.whitelist() def make_stock_entry(source_name, target_doc=None): def update_item(obj, target, source_parent): - qty = flt(obj.qty) - flt(obj.ordered_qty) \ - if flt(obj.qty) > flt(obj.ordered_qty) else 0 + qty = flt(flt(obj.stock_qty) - flt(obj.ordered_qty))/ target.conversion_factor \ + if flt(obj.stock_qty) > flt(obj.ordered_qty) else 0 target.qty = qty - target.transfer_qty = qty - target.conversion_factor = 1 + target.transfer_qty = qty * obj.conversion_factor + target.conversion_factor = obj.conversion_factor if source_parent.material_request_type == "Material Transfer": target.t_warehouse = obj.warehouse @@ -384,7 +385,7 @@ def make_stock_entry(source_name, target_doc=None): "uom": "stock_uom", }, "postprocess": update_item, - "condition": lambda doc: doc.ordered_qty < doc.qty + "condition": lambda doc: doc.ordered_qty < doc.stock_qty } }, target_doc, set_missing_values) @@ -405,7 +406,7 @@ def raise_production_orders(material_request): prod_order.fg_warehouse = d.warehouse prod_order.wip_warehouse = default_wip_warehouse prod_order.description = d.description - prod_order.stock_uom = d.uom + prod_order.stock_uom = d.stock_uom prod_order.expected_delivery_date = d.schedule_date prod_order.sales_order = d.sales_order prod_order.bom_no = get_item_details(d.item_code).bom_no diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index a24957e2b0..83971d3779 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe, unittest, erpnext -from frappe.utils import flt +from frappe.utils import flt, today from erpnext.stock.doctype.material_request.material_request import raise_production_orders class TestMaterialRequest(unittest.TestCase): @@ -558,5 +558,48 @@ class TestMaterialRequest(unittest.TestCase): item_code= %s and warehouse= %s """, (mr.items[0].item_code, mr.items[0].warehouse))[0][0] self.assertEquals(requested_qty, new_requested_qty) + def test_multi_uom_for_purchase(self): + from erpnext.stock.doctype.material_request.material_request import make_purchase_order + + mr = frappe.copy_doc(test_records[0]) + mr.material_request_type = 'Purchase' + item = mr.items[0] + mr.schedule_date = today() + + if not frappe.db.get_value('UOM Conversion Detail', + {'parent': item.item_code, 'uom': 'Kg'}): + item_doc = frappe.get_doc('Item', item.item_code) + item_doc.append('uoms', { + 'uom': 'Kg', + 'conversion_factor': 5 + }) + item_doc.save(ignore_permissions=True) + + item.uom = 'Kg' + for item in mr.items: + item.schedule_date = mr.schedule_date + + mr.insert() + self.assertRaises(frappe.ValidationError, make_purchase_order, + mr.name) + + mr = frappe.get_doc("Material Request", mr.name) + mr.submit() + item = mr.items[0] + + self.assertEquals(item.uom, "Kg") + self.assertEquals(item.conversion_factor, 5.0) + self.assertEquals(item.stock_qty, flt(item.qty * 5)) + + po = make_purchase_order(mr.name) + self.assertEquals(po.doctype, "Purchase Order") + self.assertEquals(len(po.get("items")), len(mr.get("items"))) + + po.supplier = '_Test Supplier' + po.insert() + po.submit() + mr = frappe.get_doc("Material Request", mr.name) + self.assertEquals(mr.per_ordered, 100) + test_dependencies = ["Currency Exchange", "BOM"] test_records = frappe.get_test_records('Material Request') diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index 5bf3f27a1b..3434b4d48f 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "autoname": "hash", @@ -13,6 +14,7 @@ "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 1, "collapsible": 0, @@ -46,6 +48,7 @@ "width": "100px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -73,6 +76,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -105,6 +109,7 @@ "width": "100px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 1, @@ -134,6 +139,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -166,6 +172,7 @@ "width": "250px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -194,6 +201,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -223,6 +231,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -251,6 +260,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -283,11 +293,12 @@ "width": "80px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "uom", + "fieldname": "stock_uom", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -299,23 +310,21 @@ "label": "Stock UOM", "length": 0, "no_copy": 0, - "oldfieldname": "uom", - "oldfieldtype": "Link", "options": "UOM", "permlevel": 0, - "print_hide": 0, + "precision": "", + "print_hide": 1, "print_hide_if_no_value": 0, - "print_width": "70px", "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 0, "set_only_once": 0, - "unique": 0, - "width": "70px" + "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -349,6 +358,7 @@ "width": "100px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -376,6 +386,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 1, "collapsible": 0, @@ -409,6 +420,101 @@ "width": "100px" }, { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "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": "UOM", + "length": 0, + "no_copy": 0, + "oldfieldname": "uom", + "oldfieldtype": "Link", + "options": "UOM", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": "70px", + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "70px" + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "conversion_factor", + "fieldtype": "Float", + "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": "UOM Conversion Factor", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "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_qty", + "fieldtype": "Float", + "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 Qty", + "length": 0, + "no_copy": 1, + "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, @@ -437,6 +543,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -469,6 +576,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -502,6 +610,7 @@ "width": "100px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -532,6 +641,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -561,6 +671,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -591,6 +702,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -618,6 +730,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -650,6 +763,7 @@ "width": "70px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, @@ -682,6 +796,7 @@ "width": "70px" }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -711,6 +826,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -741,6 +857,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, @@ -771,17 +888,17 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 1, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-02-20 13:31:50.143583", + "modified": "2017-10-25 17:18:59.974778", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 539e8a5667..102d168840 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -164,11 +164,17 @@ def get_basic_details(args, item): warehouse = user_default_warehouse or item.default_warehouse or args.warehouse + material_request_type = '' + if args.get('doctype') == "Material Request": + material_request_type = frappe.db.get_value('Material Request', + args.get('name'), 'material_request_type') + #Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master if not args.uom: if args.get('doctype') in ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']: args.uom = item.sales_uom if item.sales_uom else item.stock_uom - elif args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']: + elif (args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']) or \ + (args.get('doctype') == 'Material Request' and material_request_type == 'Purchase'): args.uom = item.purchase_uom if item.purchase_uom else item.stock_uom else: args.uom = item.stock_uom diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py index 46b8840538..d4bc08b045 100644 --- a/erpnext/stock/reorder_item.py +++ b/erpnext/stock/reorder_item.py @@ -128,16 +128,26 @@ def create_material_request(material_requests): for d in items: d = frappe._dict(d) item = frappe.get_doc("Item", d.item_code) + uom = item.stock_uom + conversion_factor = 1.0 + + if request_type == 'Purchase': + uom = item.purchase_uom or item.stock_uom + if uom != item.stock_uom: + conversion_factor = frappe.db.get_value("UOM Conversion Detail", + {'parent': item.name, 'uom': uom}, 'conversion_factor') or 1.0 + mr.append("items", { "doctype": "Material Request Item", "item_code": d.item_code, "schedule_date": add_days(nowdate(),cint(item.lead_time_days)), - "uom": item.stock_uom, + "qty": d.reorder_qty / conversion_factor, + "uom": uom, + "stock_uom": item.stock_uom, "warehouse": d.warehouse, "item_name": item.item_name, "description": item.description, "item_group": item.item_group, - "qty": d.reorder_qty, "brand": item.brand, })