[enhance] Added multi UOM feature in Material Request (#11352)
* [enhance] Added multi UOM in Material Request * test cases * patch to update multi uom fields in material request item
This commit is contained in:
parent
f71f3af938
commit
e8ccc0e942
@ -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'
|
||||
|
@ -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
|
@ -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""")
|
@ -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);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
})
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user