diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d104bc003c..c26451a30c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -352,3 +352,4 @@ erpnext.patches.v13_0.shopping_cart_to_ecommerce erpnext.patches.v13_0.update_disbursement_account erpnext.patches.v13_0.update_reserved_qty_closed_wo erpnext.patches.v14_0.delete_amazon_mws_doctype +erpnext.patches.v14_0.set_work_order_qty_in_so_from_mr diff --git a/erpnext/patches/v14_0/set_work_order_qty_in_so_from_mr.py b/erpnext/patches/v14_0/set_work_order_qty_in_so_from_mr.py new file mode 100644 index 0000000000..f097ab9297 --- /dev/null +++ b/erpnext/patches/v14_0/set_work_order_qty_in_so_from_mr.py @@ -0,0 +1,36 @@ +import frappe + + +def execute(): + """ + 1. Get submitted Work Orders with MR, MR Item and SO set + 2. Get SO Item detail from MR Item detail in WO, and set in WO + 3. Update work_order_qty in SO + """ + work_order = frappe.qb.DocType("Work Order") + query = ( + frappe.qb.from_(work_order) + .select( + work_order.name, work_order.produced_qty, + work_order.material_request, + work_order.material_request_item, + work_order.sales_order + ).where( + (work_order.material_request.isnotnull()) + & (work_order.material_request_item.isnotnull()) + & (work_order.sales_order.isnotnull()) + & (work_order.docstatus == 1) + & (work_order.produced_qty > 0) + ) + ) + results = query.run(as_dict=True) + + for row in results: + so_item = frappe.get_value( + "Material Request Item", row.material_request_item, "sales_order_item" + ) + frappe.db.set_value("Work Order", row.name, "sales_order_item", so_item) + + if so_item: + wo = frappe.get_doc("Work Order", row.name) + wo.update_work_order_qty_in_so() diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index acf048e116..73c5bd299a 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -6,7 +6,7 @@ import json import frappe import frappe.permissions from frappe.core.doctype.user_permission.test_user_permission import create_user -from frappe.utils import add_days, flt, getdate, nowdate +from frappe.utils import add_days, flt, getdate, nowdate, today from erpnext.controllers.accounts_controller import update_child_qty_rate from erpnext.maintenance.doctype.maintenance_schedule.test_maintenance_schedule import ( @@ -1399,6 +1399,48 @@ class TestSalesOrder(ERPNextTestCase): so.load_from_db() self.assertEqual(so.billing_status, 'Fully Billed') + def test_so_back_updated_from_wo_via_mr(self): + "SO -> MR (Manufacture) -> WO. Test if WO Qty is updated in SO." + from erpnext.manufacturing.doctype.work_order.work_order import ( + make_stock_entry as make_se_from_wo, + ) + from erpnext.stock.doctype.material_request.material_request import raise_work_orders + + so = make_sales_order(item_list=[{"item_code": "_Test FG Item","qty": 2, "rate":100}]) + + mr = make_material_request(so.name) + mr.material_request_type = "Manufacture" + mr.schedule_date = today() + mr.submit() + + # WO from MR + wo_name = raise_work_orders(mr.name)[0] + wo = frappe.get_doc("Work Order", wo_name) + wo.wip_warehouse = "Work In Progress - _TC" + wo.skip_transfer = True + + self.assertEqual(wo.sales_order, so.name) + self.assertEqual(wo.sales_order_item, so.items[0].name) + + wo.submit() + make_stock_entry(item_code="_Test Item", # Stock RM + target="Work In Progress - _TC", + qty=4, basic_rate=100 + ) + make_stock_entry(item_code="_Test Item Home Desktop 100", # Stock RM + target="Work In Progress - _TC", + qty=4, basic_rate=100 + ) + + se = frappe.get_doc(make_se_from_wo(wo.name, "Manufacture", 2)) + se.submit() # Finish WO + + mr.reload() + wo.reload() + so.reload() + self.assertEqual(so.items[0].work_order_qty, wo.produced_qty) + self.assertEqual(mr.status, "Manufactured") + def automatically_fetch_payment_terms(enable=1): accounts_settings = frappe.get_doc("Accounts Settings") accounts_settings.automatically_fetch_payment_terms = enable diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 103e8d6a88..b39328f85b 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -533,6 +533,7 @@ def raise_work_orders(material_request): "stock_uom": d.stock_uom, "expected_delivery_date": d.schedule_date, "sales_order": d.sales_order, + "sales_order_item": d.get("sales_order_item"), "bom_no": get_item_details(d.item_code).bom_no, "material_request": mr.name, "material_request_item": d.name,