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 9e7563543f6..cecfcfe4af2 100644 --- a/erpnext/manufacturing/doctype/master_production_schedule/master_production_schedule.py +++ b/erpnext/manufacturing/doctype/master_production_schedule/master_production_schedule.py @@ -284,9 +284,6 @@ class MasterProductionSchedule(Document): row = data[key] row.cumulative_lead_time = math.ceil(row.cumulative_lead_time) row.order_release_date = add_days(row.delivery_date, -row.cumulative_lead_time) - if getdate(row.order_release_date) < getdate(today()): - continue - row.planned_qty = row.qty row.uom = row.stock_uom row.warehouse = row.warehouse or self.parent_warehouse diff --git a/erpnext/manufacturing/doctype/sales_forecast/sales_forecast.py b/erpnext/manufacturing/doctype/sales_forecast/sales_forecast.py index 8bd06abc46c..86d9274052f 100644 --- a/erpnext/manufacturing/doctype/sales_forecast/sales_forecast.py +++ b/erpnext/manufacturing/doctype/sales_forecast/sales_forecast.py @@ -84,7 +84,7 @@ class SalesForecast(Document): ) for index in range(self.demand_number): - if self.horizon_type == "Monthly": + if self.frequency == "Monthly": delivery_date = add_to_date(self.from_date, months=index + 1) else: delivery_date = add_to_date(self.from_date, weeks=index + 1) @@ -95,15 +95,13 @@ class SalesForecast(Document): "delivery_date": delivery_date, "item_name": item_details.item_name, "uom": item_details.uom, - "demand_qty": 0.0, + "demand_qty": 1.0, } ) for demand in forecast_demand: self.append("items", demand) - self.save() - @frappe.whitelist() def generate_demand(self): from statsmodels.tsa.holtwinters import ExponentialSmoothing @@ -124,7 +122,7 @@ class SalesForecast(Document): seasonal_periods = self.get_seasonal_periods(data) pd_sales_data = pd.DataFrame({"item": data.item, "date": data.date, "qty": data.qty}) - resample_val = "M" if self.horizon_type == "Monthly" else "W" + resample_val = "M" if self.frequency == "Monthly" else "W" _sales_data = pd_sales_data.set_index("date").resample(resample_val).sum()["qty"] model = ExponentialSmoothing( @@ -164,7 +162,7 @@ class SalesForecast(Document): def get_seasonal_periods(self, data): days = date_diff(data["end_date"], data["start_date"]) - if self.horizon_type == "Monthly": + if self.frequency == "Monthly": months = (days / 365) * 12 seasonal_periods = cint(months / 2) if seasonal_periods > 12: diff --git a/erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.js b/erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.js index bab665c8277..5687e8e9483 100644 --- a/erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.js +++ b/erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.js @@ -77,13 +77,6 @@ frappe.query_reports["Material Requirements Planning Report"] = { default: "All", options: "\nFinished Goods\nRaw Materials\nAll", }, - { - fieldname: "safety_stock_check_frequency", - label: __("Safety Stock Check Frequency"), - fieldtype: "Select", - default: "Weekly", - options: "\nDaily\nWeekly\nMonthly", - }, { fieldname: "add_safety_stock", label: __("Add Safety Stock"), diff --git a/erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py b/erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py index a04ee0100e7..ee93cdc27e5 100644 --- a/erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py +++ b/erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py @@ -41,8 +41,9 @@ class MaterialRequirementsPlanningReport: self.rm_items = [] self.dates = self.get_dates() self.mps_data = self.get_mps_data() - items = self.get_items_from_mps(self.mps_data) + self.update_sales_forecast_data() + self.item_rm_details = self.get_raw_materials_data(items) self._bin_details = self.get_item_wise_bin_details() @@ -52,7 +53,6 @@ class MaterialRequirementsPlanningReport: self._po_details = self.get_purchase_order_data() self._so_details = self.get_sales_order_data() - self.update_sales_forecast_data() data, chart = self.get_mrp_data() return data, chart @@ -850,6 +850,23 @@ class MaterialRequirementsPlanningReport: if row.item_code not in items: items.append(row.item_code) + if self.filters.mps: + sales_forecasts = frappe.get_all( + "Master Production Schedule", + filters={"name": self.filters.mps}, + pluck="sales_forecast", + ) + + if sales_forecasts: + sales_forecast_items = frappe.get_all( + "Sales Forecast Item", + filters={"parent": ("in", sales_forecasts)}, + pluck="item_code", + ) + + if sales_forecast_items: + items.extend(sales_forecast_items) + return items def get_raw_materials_data(self, items): @@ -1139,7 +1156,12 @@ class MaterialRequirementsPlanningReport: query = query.where(doctype.delivery_date <= self.filters.to_date) if self.filters.warehouse: - query = query.where(doctype.warehouse == self.filters.warehouse) + warehouses = [self.filters.get("warehouse")] + if frappe.db.get_value("Warehouse", self.filters.get("warehouse"), "is_group"): + warehouses = get_descendants_of("Warehouse", self.filters.get("warehouse")) + warehouses.append(self.filters.get("warehouse")) + + query = query.where(forecast_doc.parent_warehouse.isin(warehouses)) if self.filters.item_code: query = query.where(doctype.item_code == self.filters.item_code) diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json index 75f6870ff22..3d16dac7acc 100644 --- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json @@ -347,7 +347,7 @@ "hidden": 0, "is_query_report": 0, "label": "Production", - "link_count": 7, + "link_count": 8, "link_type": "DocType", "onboard": 0, "type": "Card Break" @@ -385,16 +385,6 @@ "onboard": 1, "type": "Link" }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Master Production Schedule", - "link_count": 0, - "link_to": "Master Production Schedule", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, { "dependencies": "", "hidden": 0, @@ -406,6 +396,26 @@ "onboard": 0, "type": "Link" }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Item Lead Time", + "link_count": 0, + "link_to": "Item Lead Time", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Master Production Schedule", + "link_count": 0, + "link_to": "Master Production Schedule", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, { "dependencies": "", "hidden": 0, @@ -428,7 +438,7 @@ "type": "Link" } ], - "modified": "2025-10-30 11:49:35.589944", + "modified": "2025-11-03 17:02:26.882516", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index cc89b85b823..2d6fb2c3a58 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -119,12 +119,6 @@ "default_item_manufacturer", "default_manufacturer_part_no", "total_projected_qty", - "lead_time_in_days_section", - "procurement_time", - "manufacturing_time", - "column_break_whvr", - "planning_buffer", - "cumulative_time", "capacity_in_days_section", "production_capacity" ], @@ -894,36 +888,6 @@ "fieldtype": "Section Break", "label": "Deferred Accounting" }, - { - "fieldname": "lead_time_in_days_section", - "fieldtype": "Section Break", - "label": "Lead Time (In Days)" - }, - { - "fieldname": "procurement_time", - "fieldtype": "Int", - "label": "Procurement Time" - }, - { - "fieldname": "planning_buffer", - "fieldtype": "Int", - "label": "Planning Buffer" - }, - { - "fieldname": "column_break_whvr", - "fieldtype": "Column Break" - }, - { - "fieldname": "manufacturing_time", - "fieldtype": "Int", - "label": "Manufacturing Time" - }, - { - "fieldname": "cumulative_time", - "fieldtype": "Int", - "label": "Cumulative Time", - "read_only": 1 - }, { "fieldname": "capacity_in_days_section", "fieldtype": "Section Break", @@ -953,7 +917,7 @@ "image_field": "image", "links": [], "make_attachments_public": 1, - "modified": "2025-10-13 16:58:40.946604", + "modified": "2025-11-03 17:01:24.555003", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index f91beb8b488..51531faf990 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -81,7 +81,6 @@ class Item(Document): brand: DF.Link | None country_of_origin: DF.Link | None create_new_batch: DF.Check - cumulative_time: DF.Int customer: DF.Link | None customer_code: DF.SmallText | None customer_items: DF.Table[ItemCustomerDetail] @@ -120,7 +119,6 @@ class Item(Document): item_name: DF.Data | None last_purchase_rate: DF.Float lead_time_days: DF.Int - manufacturing_time: DF.Int max_discount: DF.Float min_order_qty: DF.Float naming_series: DF.Literal["STO-ITEM-.YYYY.-"] @@ -129,8 +127,6 @@ class Item(Document): opening_stock: DF.Float over_billing_allowance: DF.Float over_delivery_receipt_allowance: DF.Float - planning_buffer: DF.Int - procurement_time: DF.Int production_capacity: DF.Int purchase_uom: DF.Link | None quality_inspection_template: DF.Link | None @@ -221,16 +217,10 @@ class Item(Document): self.validate_auto_reorder_enabled_in_stock_settings() self.cant_change() self.validate_item_tax_net_rate_range() - self.set_cumulative_time() if not self.is_new(): self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group") - def set_cumulative_time(self): - self.cumulative_time = ( - cint(self.procurement_time) + cint(self.manufacturing_time) + cint(self.planning_buffer) - ) - def on_update(self): self.update_variants() self.update_item_price()