From 963d1e502eb70b7867651c9ffc00892fef2d0221 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 12 May 2025 23:21:57 +0530 Subject: [PATCH] fix: not able to reserve extra items against the work order (#47511) --- .../doctype/work_order/work_order.py | 51 +++++++++++++++---- .../work_order_item/work_order_item.json | 26 ++++++++-- .../work_order_item/work_order_item.py | 4 +- .../work_order_consumed_materials.py | 4 ++ .../stock/doctype/stock_entry/stock_entry.py | 5 ++ 5 files changed, 77 insertions(+), 13 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 0aaaf78f50c..58ac314f209 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1362,13 +1362,6 @@ class WorkOrder(Document): ) ) - if details.reserved_qty < details.transferred_qty: - frappe.throw( - _("Transferred Qty {0} cannot be greater than Reserved Qty {1} for item {2}").format( - details.transferred_qty, details.reserved_qty, item.item_code - ) - ) - @frappe.whitelist() def make_bom(self): data = frappe.db.sql( @@ -1414,7 +1407,7 @@ class WorkOrder(Document): def get_list_of_materials_for_reservation(self, stock_entry): items = frappe._dict() - vocher_detail_no = {d.item_code: d.name for d in self.required_items} + voucher_detail_no = {d.item_code: d.name for d in self.required_items} for row in stock_entry.items: if row.item_code not in items: @@ -1422,7 +1415,7 @@ class WorkOrder(Document): { "voucher_no": self.name, "voucher_type": self.doctype, - "voucher_detail_no": vocher_detail_no.get(row.item_code), + "voucher_detail_no": voucher_detail_no.get(row.item_code), "item_code": row.item_code, "warehouse": row.t_warehouse, "stock_qty": row.transfer_qty, @@ -1590,6 +1583,46 @@ class WorkOrder(Document): if sre_list: cancel_stock_reservation_entries(self, sre_list) + def remove_additional_items(self, stock_entry): + for row in stock_entry.items: + for item in self.required_items: + if row.item_code == item.item_code and row.name == item.voucher_detail_reference: + item.delete() + + def add_additional_items(self, stock_entry): + if stock_entry.purpose != "Material Transfer for Manufacture": + return + + required_items = [d.item_code for d in self.required_items] + + additional_items = frappe._dict() + for row in stock_entry.items: + if row.item_code not in required_items: + additional_items.setdefault(row.item_code, []).append(row) + + for item_code, rows in additional_items.items(): + for row in rows: + child_row = self.append( + "required_items", + { + "item_code": item_code, + "source_warehouse": row.s_warehouse, + "item_name": row.item_name, + "required_qty": row.transfer_qty, + "stock_uom": row.stock_uom, + "rate": row.basic_rate, + "amount": row.amount, + "description": row.description, + "docstatus": 1, + "is_additional_item": 1, + "voucher_detail_reference": row.name, + }, + ) + + child_row.insert() + + stock_entry.reload() + @frappe.whitelist() def make_stock_reservation_entries(doc, items=None, table_name=None, notify=False): diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json index f74f60574de..81de6619422 100644 --- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json +++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json @@ -27,7 +27,9 @@ "available_qty_at_source_warehouse", "available_qty_at_wip_warehouse", "column_break_jash", - "stock_reserved_qty" + "stock_reserved_qty", + "is_additional_item", + "voucher_detail_reference" ], "fields": [ { @@ -172,19 +174,37 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_additional_item", + "fieldtype": "Check", + "label": "Is Additional Item", + "no_copy": 1, + "read_only": 1 + }, + { + "depends_on": "is_additional_item", + "fieldname": "voucher_detail_reference", + "fieldtype": "Data", + "label": "Voucher Detail Reference", + "no_copy": 1, + "read_only": 1 } ], + "grid_page_length": 50, "istable": 1, "links": [], - "modified": "2024-11-20 15:48:16.823384", + "modified": "2025-05-12 17:36:00.115181", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order Item", "owner": "Administrator", "permissions": [], "quick_entry": 1, + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.py b/erpnext/manufacturing/doctype/work_order_item/work_order_item.py index 9bc62a8a832..6f48de25358 100644 --- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.py +++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.py @@ -22,6 +22,7 @@ class WorkOrderItem(Document): consumed_qty: DF.Float description: DF.Text | None include_item_in_manufacturing: DF.Check + is_additional_item: DF.Check item_code: DF.Link | None item_name: DF.Data | None operation: DF.Link | None @@ -33,9 +34,10 @@ class WorkOrderItem(Document): required_qty: DF.Float returned_qty: DF.Float source_warehouse: DF.Link | None - stock_uom: DF.Link | None stock_reserved_qty: DF.Float + stock_uom: DF.Link | None transferred_qty: DF.Float + voucher_detail_reference: DF.Data | None # end: auto-generated types pass diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py index a5690c477e4..adcdcb9a094 100644 --- a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py +++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py @@ -29,6 +29,9 @@ def get_data(report_filters): if d.consumed_qty and d.consumed_qty > d.required_qty: d.extra_consumed_qty = d.consumed_qty - d.required_qty + if d.is_additional_item: + d.extra_consumed_qty = d.consumed_qty + if d.extra_consumed_qty or not report_filters.show_extra_consumed_materials: wo_items.setdefault((d.name, d.production_item), []).append(d) @@ -81,6 +84,7 @@ def get_fields(): "`tabWork Order Item`.`required_qty`", "`tabWork Order Item`.`transferred_qty`", "`tabWork Order Item`.`consumed_qty`", + "`tabWork Order Item`.`is_additional_item`", "`tabWork Order`.`status`", "`tabWork Order`.`name`", "`tabWork Order`.`production_item`", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index b8f23be201e..d15f1943895 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1635,6 +1635,11 @@ class StockEntry(StockController): _validate_work_order(pro_doc) if self.fg_completed_qty: + if self.docstatus == 1: + pro_doc.add_additional_items(self) + else: + pro_doc.remove_additional_items(self) + pro_doc.run_method("update_work_order_qty") if self.purpose == "Manufacture": pro_doc.run_method("update_planned_qty")