From 1778e2c564cda3ba582a70daafdd81985e8d8682 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 17 Dec 2025 14:41:09 +0530 Subject: [PATCH] refactor: pick list from material request --- erpnext/controllers/status_updater.py | 36 +++++++++++-------- .../material_request/material_request.js | 12 ++++--- .../material_request/material_request.py | 16 ++++----- .../material_request_item.json | 12 ++++++- .../material_request_item.py | 1 + erpnext/stock/doctype/pick_list/pick_list.py | 18 +++++++++- 6 files changed, 65 insertions(+), 30 deletions(-) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 00c424200b8..b12741487cd 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -393,12 +393,16 @@ class StatusUpdater(Document): self.item_allowance, self.global_qty_allowance, self.global_amount_allowance, - ) = get_allowance_for( - item["item_code"], - self.item_allowance, - self.global_qty_allowance, - self.global_amount_allowance, - qty_or_amount, + ) = ( + get_allowance_for( + item["item_code"], + self.item_allowance, + self.global_qty_allowance, + self.global_amount_allowance, + qty_or_amount, + ) + if args["source_dt"] != "Pick List Item" + else (0, {}, None, None) ) role_allowed_to_over_deliver_receive = frappe.get_single_value( @@ -436,14 +440,17 @@ class StatusUpdater(Document): ): return - if qty_or_amount == "qty": - action_msg = _( - 'To allow over receipt / delivery, update "Over Receipt/Delivery Allowance" in Stock Settings or the Item.' - ) + if args["source_dt"] != "Pick List Item": + if qty_or_amount == "qty": + action_msg = _( + 'To allow over receipt / delivery, update "Over Receipt/Delivery Allowance" in Stock Settings or the Item.' + ) + else: + action_msg = _( + 'To allow over billing, update "Over Billing Allowance" in Accounts Settings or the Item.' + ) else: - action_msg = _( - 'To allow over billing, update "Over Billing Allowance" in Accounts Settings or the Item.' - ) + action_msg = None frappe.throw( _( @@ -455,8 +462,7 @@ class StatusUpdater(Document): frappe.bold(_(self.doctype)), frappe.bold(item.get("item_code")), ) - + "

" - + action_msg, + + ("

" + action_msg if action_msg else ""), OverAllowanceError, title=_("Limit Crossed"), ) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 1a269648ce3..1975343c90b 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -139,11 +139,13 @@ frappe.ui.form.on("Material Request", { if (flt(frm.doc.per_ordered, precision) < 100) { let add_create_pick_list_button = () => { - frm.add_custom_button( - __("Pick List"), - () => frm.events.create_pick_list(frm), - __("Create") - ); + if (frm.doc.items.some((item) => item.stock_qty - item.picked_qty > 0)) { + frm.add_custom_button( + __("Pick List"), + () => frm.events.create_pick_list(frm), + __("Create") + ); + } }; if (frm.doc.material_request_type === "Material Transfer") { diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 3eac6f94f04..b98870788a5 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -911,11 +911,7 @@ def raise_work_orders(material_request, company): @frappe.whitelist() def create_pick_list(source_name, target_doc=None): def update_item(obj, target, source_parent): - qty = ( - flt(flt(obj.stock_qty) - flt(obj.ordered_qty)) / target.conversion_factor - if flt(obj.stock_qty) > flt(obj.ordered_qty) - else 0 - ) + qty = flt((obj.stock_qty - obj.picked_qty) / target.conversion_factor, obj.precision("qty")) target.qty = qty target.stock_qty = qty * obj.conversion_factor target.conversion_factor = obj.conversion_factor @@ -931,11 +927,15 @@ def create_pick_list(source_name, target_doc=None): }, "Material Request Item": { "doctype": "Pick List Item", - "field_map": {"name": "material_request_item", "stock_qty": "stock_qty"}, + "field_map": { + "name": "material_request_item", + "stock_qty": "stock_qty", + "from_warehouse": "warehouse", + }, "postprocess": update_item, "condition": lambda doc: ( - flt(doc.ordered_qty, doc.precision("ordered_qty")) - < flt(doc.stock_qty, doc.precision("ordered_qty")) + flt(doc.picked_qty, doc.precision("picked_qty")) + < flt(doc.stock_qty, doc.precision("stock_qty")) ), }, }, 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 51eca0d3892..d8db1d9ce01 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -29,6 +29,7 @@ "qty_info_sec_break", "min_order_qty", "projected_qty", + "picked_qty", "qty_info_col_break", "actual_qty", "ordered_qty", @@ -518,13 +519,22 @@ "fieldtype": "Float", "label": "Projected On Hand", "read_only": 1 + }, + { + "fieldname": "picked_qty", + "fieldtype": "Float", + "hidden": 1, + "label": "Picked Qty", + "no_copy": 1, + "non_negative": 1, + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-12-02 14:14:45.972664", + "modified": "2025-12-17 13:47:27.317226", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.py b/erpnext/stock/doctype/material_request_item/material_request_item.py index e36d4e66549..d801cf5a246 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.py +++ b/erpnext/stock/doctype/material_request_item/material_request_item.py @@ -41,6 +41,7 @@ class MaterialRequestItem(Document): parent: DF.Data parentfield: DF.Data parenttype: DF.Data + picked_qty: DF.Float price_list_rate: DF.Currency production_plan: DF.Link | None project: DF.Link | None diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 1bf954323b2..83149143791 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -19,7 +19,6 @@ from erpnext.selling.doctype.sales_order.sales_order import ( ) from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import ( get_auto_batch_nos, - get_picked_serial_nos, ) from erpnext.stock.get_item_details import get_company_total_stock, get_conversion_factor from erpnext.stock.serial_batch_bundle import ( @@ -66,6 +65,21 @@ class PickList(TransactionBase): work_order: DF.Link | None # end: auto-generated types + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.status_updater = [ + { + "source_dt": "Pick List Item", + "target_dt": "Material Request Item", + "target_field": "picked_qty", + "target_parent_dt": "Material Request", + "target_parent_field": "", + "join_field": "material_request_item", + "target_ref_field": "stock_qty", + "source_field": "stock_qty", + } + ] + def onload(self) -> None: if frappe.get_cached_value("Stock Settings", None, "enable_stock_reservation"): if self.has_unreserved_stock(): @@ -228,6 +242,7 @@ class PickList(TransactionBase): self.update_bundle_picked_qty() self.update_reference_qty() self.update_sales_order_picking_status() + self.update_prevdoc_status() def validate_expired_batches(self): batches = [] @@ -305,6 +320,7 @@ class PickList(TransactionBase): self.update_reference_qty() self.update_sales_order_picking_status() self.delink_serial_and_batch_bundle() + self.update_prevdoc_status() def delink_serial_and_batch_bundle(self): for row in self.locations: