diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index ee07892fe43..220059796ac 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1056,7 +1056,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-09-14 14:36:12.418690", + "modified": "2021-01-22 20:27:11.418690", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index be5763b1d9c..75c28328a00 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -89,7 +89,7 @@ class TestPurchaseOrder(unittest.TestCase): frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0) - def test_update_child_qty_rate(self): + def test_update_child(self): mr = make_material_request(qty=10) po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" @@ -119,7 +119,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3) - def test_add_new_item_in_update_child_qty_rate(self): + def test_update_child_adding_new_item(self): po = create_purchase_order(do_not_save=1) po.items[0].qty = 4 po.save() @@ -145,7 +145,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(po.status, 'To Receive and Bill') - def test_remove_item_in_update_child_qty_rate(self): + def test_update_child_removing_item(self): po = create_purchase_order(do_not_save=1) po.items[0].qty = 4 po.save() @@ -185,7 +185,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEquals(len(po.get('items')), 1) self.assertEqual(po.status, 'To Receive and Bill') - def test_update_child_qty_rate_perm(self): + def test_update_child_perm(self): po = create_purchase_order(item_code= "_Test Item", qty=4) user = 'test@example.com' diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3c628345a59..69ed1ee8439 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1196,8 +1196,9 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.item_name = item.item_name child_item.description = item.description child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date - child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 - child_item.uom = item.stock_uom + child_item.uom = trans_item.get("uom") or item.stock_uom + conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) + child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor set_child_tax_template_and_map(item, child_item, p_doc) add_taxes_from_tax_template(child_item, p_doc) child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) @@ -1218,8 +1219,9 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.item_name = item.item_name child_item.description = item.description child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date - child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 - child_item.uom = item.stock_uom + child_item.uom = trans_item.get("uom") or item.stock_uom + conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) + child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation set_child_tax_template_and_map(item, child_item, p_doc) @@ -1316,6 +1318,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate")) prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty")) prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor")) + prev_uom, new_uom = child_item.get("uom"), d.get("uom") if parent_doctype == 'Sales Order': prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date") @@ -1324,9 +1327,10 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil rate_unchanged = prev_rate == new_rate qty_unchanged = prev_qty == new_qty + uom_unchanged = prev_uom == new_uom conversion_factor_unchanged = prev_con_fac == new_con_fac date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc - if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged: + if rate_unchanged and qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged: continue validate_quantity(child_item, d) @@ -1347,6 +1351,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil child_item.conversion_factor = 1 else: child_item.conversion_factor = flt(d.get('conversion_factor'), conv_fac_precision) + + if d.get("uom"): + child_item.uom = d.get("uom") + conversion_factor = flt(get_conversion_factor(child_item.item_code, child_item.uom).get("conversion_factor")) + child_item.conversion_factor = flt(d.get('conversion_factor')) or conversion_factor if d.get("delivery_date") and parent_doctype == 'Sales Order': child_item.delivery_date = d.get('delivery_date') @@ -1413,6 +1422,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.update_receiving_percentage() if parent.is_subcontracted == "Yes": parent.update_reserved_qty_for_subcontract() + parent.create_raw_materials_supplied("supplied_items") + parent.save() else: parent.update_reserved_qty() parent.update_project() diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 6e2409cf875..4547fc97a31 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -469,6 +469,33 @@ erpnext.utils.update_child_items = function(opts) { read_only: 0, disabled: 0, label: __('Item Code') + }, { + fieldtype:'Link', + fieldname:'uom', + options: 'UOM', + read_only: 0, + label: __('UOM'), + reqd: 1, + onchange: function () { + frappe.call({ + method: "erpnext.stock.get_item_details.get_conversion_factor", + args: { item_code: this.doc.item_code, uom: this.value }, + callback: r => { + if(!r.exc) { + if (this.doc.conversion_factor == r.message.conversion_factor) return; + + const docname = this.doc.docname; + dialog.fields_dict.trans_items.df.data.some(doc => { + if (doc.docname == docname) { + doc.conversion_factor = r.message.conversion_factor; + dialog.fields_dict.trans_items.grid.refresh(); + return true; + } + }); + } + } + }); + }, }, { fieldtype:'Float', fieldname:"qty", @@ -552,6 +579,7 @@ erpnext.utils.update_child_items = function(opts) { "conversion_factor": d.conversion_factor, "qty": d.qty, "rate": d.rate, + "uom": d.uom }); this.data = dialog.fields_dict.trans_items.df.data; dialog.fields_dict.trans_items.grid.refresh(); diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 37c29befcd7..7aefbd0a127 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -319,7 +319,7 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2 + 20) - def test_add_new_item_in_update_child_qty_rate(self): + def test_update_child_adding_new_item(self): so = make_sales_order(item_code= "_Test Item", qty=4) create_dn_against_so(so.name, 4) make_sales_invoice(so.name) @@ -339,7 +339,7 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(so.get("items")[-1].amount, 1400) self.assertEqual(so.status, 'To Deliver and Bill') - def test_remove_item_in_update_child_qty_rate(self): + def test_update_child_removing_item(self): so = make_sales_order(**{ "item_list": [{ "item_code": '_Test Item', @@ -382,7 +382,7 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(so.status, 'To Deliver and Bill') - def test_update_child_qty_rate(self): + def test_update_child(self): so = make_sales_order(item_code= "_Test Item", qty=4) create_dn_against_so(so.name, 4) make_sales_invoice(so.name) @@ -419,7 +419,7 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(so.items[0].rate, 200.34669) make_property_setter("Sales Order Item", "rate", "precision", precision, "Currency") - def test_update_child_qty_rate_perm(self): + def test_update_child_perm(self): so = make_sales_order(item_code= "_Test Item", qty=4) user = 'test@example.com' @@ -472,7 +472,7 @@ class TestSalesOrder(unittest.TestCase): workflow.is_active = 0 workflow.save() - def test_update_child_qty_rate_product_bundle(self): + def test_update_child_product_bundle(self): # test Update Items with product bundle if not frappe.db.exists("Item", "_Product Bundle Item"): bundle_item = make_item("_Product Bundle Item", {"is_stock_item": 0}) @@ -492,6 +492,20 @@ class TestSalesOrder(unittest.TestCase): so.reload() self.assertEqual(so.packed_items[0].qty, 4) + # test uom and conversion factor change + update_uom_conv_factor = json.dumps([{ + 'item_code': so.get("items")[0].item_code, + 'rate': so.get("items")[0].rate, + 'qty': so.get("items")[0].qty, + 'uom': "_Test UOM 1", + 'conversion_factor': 2, + 'docname': so.get("items")[0].name + }]) + update_child_qty_rate('Sales Order', update_uom_conv_factor, so.name) + + so.reload() + self.assertEqual(so.packed_items[0].qty, 8) + def test_update_child_with_tax_template(self): """ Test Action: Create a SO with one item having its tax account head already in the SO.