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 020f2c6db7262fb43ad70b826cbc0367934b465c..2373ba2a2c70090d7359f351a034399ac00c35b6 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 cecfcfe4af208a22c46c2a77afdd8d3e3a76635c..a558d2bd19a80bc7afecf6c525888eef6dbb394f 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 4d849cbec332d1ac35195bfd17968a0f2cc6efbc..73b1ac8e9d795ad2717296ad0d26d7b76bf5b0c3 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 541321b976b38c2942c4054c05bf6830babe4430..4a9ec788cb6c6105ff919d51bcce587d503d262f 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,17 @@ 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: + if self.base_grand_total == 0.0 and not frappe.flags.in_test: # @dokos self.update_status("Closed") - self.make_subscription() + 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") + 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 +961,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 +975,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 +984,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()