From c215a95331285eb6c2801d9e0ba243f95e0d533c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 12 Nov 2025 23:53:11 +0530 Subject: [PATCH 1/2] fix: validation for delivery schedule --- .../master_production_schedule.js | 9 +++++++++ .../master_production_schedule.py | 14 ++++++-------- .../selling/doctype/sales_order/sales_order.js | 10 ++++++++++ .../selling/doctype/sales_order/sales_order.py | 18 ++++++++++++------ 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/erpnext/manufacturing/doctype/master_production_schedule/master_production_schedule.js b/erpnext/manufacturing/doctype/master_production_schedule/master_production_schedule.js index 020f2c6db7..2373ba2a2c 100644 --- a/erpnext/manufacturing/doctype/master_production_schedule/master_production_schedule.js +++ b/erpnext/manufacturing/doctype/master_production_schedule/master_production_schedule.js @@ -53,6 +53,15 @@ frappe.ui.form.on("Master Production Schedule", { set_custom_buttons(frm) { if (!frm.is_new()) { frm.add_custom_button(__("View MRP"), () => { + if (!frm.doc.items?.length && !frm.doc.sales_forecast) { + frappe.throw( + __( + "Please set actual demand or sales forecast to generate Material Requirements Planning Report." + ) + ); + return; + } + frappe.set_route("query-report", "Material Requirements Planning Report", { company: frm.doc.company, from_date: frm.doc.from_date, diff --git a/erpnext/manufacturing/doctype/master_production_schedule/master_production_schedule.py b/erpnext/manufacturing/doctype/master_production_schedule/master_production_schedule.py index cecfcfe4af..a558d2bd19 100644 --- a/erpnext/manufacturing/doctype/master_production_schedule/master_production_schedule.py +++ b/erpnext/manufacturing/doctype/master_production_schedule/master_production_schedule.py @@ -190,8 +190,8 @@ class MasterProductionSchedule(Document): ignore_orders = [] if sales_order_schedules: for row in sales_order_schedules: - if row.sales_order not in ignore_orders: - ignore_orders.append(row.sales_order) + if row.sales_order_item and row.sales_order_item not in ignore_orders: + ignore_orders.append(row.sales_order_item) sales_orders = self.get_items_from_sales_orders(ignore_orders) @@ -221,14 +221,14 @@ class MasterProductionSchedule(Document): if self.sales_orders: names = [s.sales_order for s in self.sales_orders if s.sales_order] - if ignore_orders: - names = [name for name in names if name not in ignore_orders] - if not names: return [] query = query.where(doctype.parent.isin(names)) + if ignore_orders: + query = query.where(doctype.name.notin(ignore_orders)) + return query.run(as_dict=True) def get_sales_order_schedules(self): @@ -239,6 +239,7 @@ class MasterProductionSchedule(Document): doctype.stock_uom, doctype.delivery_date, doctype.sales_order, + doctype.sales_order_item, doctype.stock_qty.as_("qty"), ) @@ -249,9 +250,6 @@ class MasterProductionSchedule(Document): if self.from_date: query = query.where(doctype.delivery_date >= self.from_date) - if self.to_date: - query = query.where(doctype.delivery_date <= self.to_date) - return query.run(as_dict=True) def get_item_wise_mps_data(self, data): diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 4d849cbec3..73b1ac8e9d 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -58,6 +58,12 @@ frappe.ui.form.on("Sales Order", { }, refresh: function (frm) { + frm.fields_dict["items"].grid.update_docfield_property( + "add_schedule", + "hidden", + frm.is_new() || frm.doc.docstatus === 1 ? true : false + ); + if (frm.doc.docstatus === 1) { if ( frm.doc.status !== "Closed" && @@ -894,6 +900,10 @@ frappe.ui.form.on("Sales Order Item", { add_schedule(frm, cdt, cdn) { let row = locals[cdt][cdn]; + if (row.__islocal) { + frappe.throw(__("Please save the Sales Order before adding a delivery schedule.")); + } + frappe.call({ method: "get_delivery_schedule", doc: frm.doc, diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 541321b976..a24e06d098 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -498,6 +498,7 @@ class SalesOrder(SellingController): def on_submit(self): self.check_credit_limit() self.update_reserved_qty() + self.delete_removed_delivery_schedule_items() frappe.get_cached_doc("Authorization Control").validate_approving_authority( self.doctype, self.company, self.base_grand_total, self @@ -516,10 +517,12 @@ class SalesOrder(SellingController): if self.get("reserve_stock") and not self.get("is_subcontracted"): self.create_stock_reservation_entries() - if self.base_grand_total == 0.0 and not frappe.flags.in_test: - self.update_status("Closed") - - self.make_subscription() + def delete_removed_delivery_schedule_items(self): + items = [d.name for d in self.get("items")] + doctype = frappe.qb.DocType("Delivery Schedule Item") + frappe.qb.from_(doctype).delete().where( + (doctype.sales_order == self.name) & (doctype.sales_order_item.notin(items)) + ).run() def on_cancel(self): self.ignore_linked_doctypes = ( @@ -953,7 +956,7 @@ class SalesOrder(SellingController): names.append(doc.name) if names: - self.delete_delivery_schedule_items(names) + self.delete_delivery_schedule_items(child_row.name, names) if first_delivery_date: self.update_delivery_date_based_on_schedule(child_row, first_delivery_date) @@ -967,7 +970,7 @@ class SalesOrder(SellingController): self.save() - def delete_delivery_schedule_items(self, ignore_names=None): + def delete_delivery_schedule_items(self, sales_order_item=None, ignore_names=None): """Delete delivery schedule items.""" doctype = frappe.qb.DocType("Delivery Schedule Item") @@ -976,6 +979,9 @@ class SalesOrder(SellingController): if ignore_names: query = query.where(doctype.name.notin(ignore_names)) + if sales_order_item: + query = query.where(doctype.sales_order_item == sales_order_item) + query.run() -- GitLab From 8d771e2f81b34f4397c76c04fa3dd6ec42f2d76d Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Thu, 13 Nov 2025 18:03:13 +0100 Subject: [PATCH 2/2] fix: merge conflict --- erpnext/selling/doctype/sales_order/sales_order.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index a24e06d098..4a9ec788cb 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -517,6 +517,11 @@ class SalesOrder(SellingController): if self.get("reserve_stock") and not self.get("is_subcontracted"): self.create_stock_reservation_entries() + if self.base_grand_total == 0.0 and not frappe.flags.in_test: # @dokos + self.update_status("Closed") + + self.make_subscription() # @dokos + def delete_removed_delivery_schedule_items(self): items = [d.name for d in self.get("items")] doctype = frappe.qb.DocType("Delivery Schedule Item") -- GitLab