mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-13 02:01:21 +00:00
Merge pull request #50318 from rohitwaghchaure/fixed-mrp-issues
fix: multiple MRP issues
This commit is contained in:
@@ -284,9 +284,6 @@ class MasterProductionSchedule(Document):
|
|||||||
row = data[key]
|
row = data[key]
|
||||||
row.cumulative_lead_time = math.ceil(row.cumulative_lead_time)
|
row.cumulative_lead_time = math.ceil(row.cumulative_lead_time)
|
||||||
row.order_release_date = add_days(row.delivery_date, -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.planned_qty = row.qty
|
||||||
row.uom = row.stock_uom
|
row.uom = row.stock_uom
|
||||||
row.warehouse = row.warehouse or self.parent_warehouse
|
row.warehouse = row.warehouse or self.parent_warehouse
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ class SalesForecast(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
for index in range(self.demand_number):
|
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)
|
delivery_date = add_to_date(self.from_date, months=index + 1)
|
||||||
else:
|
else:
|
||||||
delivery_date = add_to_date(self.from_date, weeks=index + 1)
|
delivery_date = add_to_date(self.from_date, weeks=index + 1)
|
||||||
@@ -95,15 +95,13 @@ class SalesForecast(Document):
|
|||||||
"delivery_date": delivery_date,
|
"delivery_date": delivery_date,
|
||||||
"item_name": item_details.item_name,
|
"item_name": item_details.item_name,
|
||||||
"uom": item_details.uom,
|
"uom": item_details.uom,
|
||||||
"demand_qty": 0.0,
|
"demand_qty": 1.0,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
for demand in forecast_demand:
|
for demand in forecast_demand:
|
||||||
self.append("items", demand)
|
self.append("items", demand)
|
||||||
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def generate_demand(self):
|
def generate_demand(self):
|
||||||
from statsmodels.tsa.holtwinters import ExponentialSmoothing
|
from statsmodels.tsa.holtwinters import ExponentialSmoothing
|
||||||
@@ -124,7 +122,7 @@ class SalesForecast(Document):
|
|||||||
seasonal_periods = self.get_seasonal_periods(data)
|
seasonal_periods = self.get_seasonal_periods(data)
|
||||||
pd_sales_data = pd.DataFrame({"item": data.item, "date": data.date, "qty": data.qty})
|
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"]
|
_sales_data = pd_sales_data.set_index("date").resample(resample_val).sum()["qty"]
|
||||||
|
|
||||||
model = ExponentialSmoothing(
|
model = ExponentialSmoothing(
|
||||||
@@ -164,7 +162,7 @@ class SalesForecast(Document):
|
|||||||
|
|
||||||
def get_seasonal_periods(self, data):
|
def get_seasonal_periods(self, data):
|
||||||
days = date_diff(data["end_date"], data["start_date"])
|
days = date_diff(data["end_date"], data["start_date"])
|
||||||
if self.horizon_type == "Monthly":
|
if self.frequency == "Monthly":
|
||||||
months = (days / 365) * 12
|
months = (days / 365) * 12
|
||||||
seasonal_periods = cint(months / 2)
|
seasonal_periods = cint(months / 2)
|
||||||
if seasonal_periods > 12:
|
if seasonal_periods > 12:
|
||||||
|
|||||||
@@ -77,13 +77,6 @@ frappe.query_reports["Material Requirements Planning Report"] = {
|
|||||||
default: "All",
|
default: "All",
|
||||||
options: "\nFinished Goods\nRaw Materials\nAll",
|
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",
|
fieldname: "add_safety_stock",
|
||||||
label: __("Add Safety Stock"),
|
label: __("Add Safety Stock"),
|
||||||
|
|||||||
@@ -41,8 +41,9 @@ class MaterialRequirementsPlanningReport:
|
|||||||
self.rm_items = []
|
self.rm_items = []
|
||||||
self.dates = self.get_dates()
|
self.dates = self.get_dates()
|
||||||
self.mps_data = self.get_mps_data()
|
self.mps_data = self.get_mps_data()
|
||||||
|
|
||||||
items = self.get_items_from_mps(self.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.item_rm_details = self.get_raw_materials_data(items)
|
||||||
|
|
||||||
self._bin_details = self.get_item_wise_bin_details()
|
self._bin_details = self.get_item_wise_bin_details()
|
||||||
@@ -52,7 +53,6 @@ class MaterialRequirementsPlanningReport:
|
|||||||
self._po_details = self.get_purchase_order_data()
|
self._po_details = self.get_purchase_order_data()
|
||||||
self._so_details = self.get_sales_order_data()
|
self._so_details = self.get_sales_order_data()
|
||||||
|
|
||||||
self.update_sales_forecast_data()
|
|
||||||
data, chart = self.get_mrp_data()
|
data, chart = self.get_mrp_data()
|
||||||
|
|
||||||
return data, chart
|
return data, chart
|
||||||
@@ -850,6 +850,23 @@ class MaterialRequirementsPlanningReport:
|
|||||||
if row.item_code not in items:
|
if row.item_code not in items:
|
||||||
items.append(row.item_code)
|
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
|
return items
|
||||||
|
|
||||||
def get_raw_materials_data(self, items):
|
def get_raw_materials_data(self, items):
|
||||||
@@ -1139,7 +1156,12 @@ class MaterialRequirementsPlanningReport:
|
|||||||
query = query.where(doctype.delivery_date <= self.filters.to_date)
|
query = query.where(doctype.delivery_date <= self.filters.to_date)
|
||||||
|
|
||||||
if self.filters.warehouse:
|
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:
|
if self.filters.item_code:
|
||||||
query = query.where(doctype.item_code == self.filters.item_code)
|
query = query.where(doctype.item_code == self.filters.item_code)
|
||||||
|
|||||||
@@ -347,7 +347,7 @@
|
|||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Production",
|
"label": "Production",
|
||||||
"link_count": 7,
|
"link_count": 8,
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Card Break"
|
"type": "Card Break"
|
||||||
@@ -385,16 +385,6 @@
|
|||||||
"onboard": 1,
|
"onboard": 1,
|
||||||
"type": "Link"
|
"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": "",
|
"dependencies": "",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -406,6 +396,26 @@
|
|||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Link"
|
"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": "",
|
"dependencies": "",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -428,7 +438,7 @@
|
|||||||
"type": "Link"
|
"type": "Link"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2025-10-30 11:49:35.589944",
|
"modified": "2025-11-03 17:02:26.882516",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Manufacturing",
|
"name": "Manufacturing",
|
||||||
|
|||||||
@@ -119,12 +119,6 @@
|
|||||||
"default_item_manufacturer",
|
"default_item_manufacturer",
|
||||||
"default_manufacturer_part_no",
|
"default_manufacturer_part_no",
|
||||||
"total_projected_qty",
|
"total_projected_qty",
|
||||||
"lead_time_in_days_section",
|
|
||||||
"procurement_time",
|
|
||||||
"manufacturing_time",
|
|
||||||
"column_break_whvr",
|
|
||||||
"planning_buffer",
|
|
||||||
"cumulative_time",
|
|
||||||
"capacity_in_days_section",
|
"capacity_in_days_section",
|
||||||
"production_capacity"
|
"production_capacity"
|
||||||
],
|
],
|
||||||
@@ -894,36 +888,6 @@
|
|||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Deferred Accounting"
|
"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",
|
"fieldname": "capacity_in_days_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@@ -953,7 +917,7 @@
|
|||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"links": [],
|
"links": [],
|
||||||
"make_attachments_public": 1,
|
"make_attachments_public": 1,
|
||||||
"modified": "2025-10-13 16:58:40.946604",
|
"modified": "2025-11-03 17:01:24.555003",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item",
|
"name": "Item",
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ class Item(Document):
|
|||||||
brand: DF.Link | None
|
brand: DF.Link | None
|
||||||
country_of_origin: DF.Link | None
|
country_of_origin: DF.Link | None
|
||||||
create_new_batch: DF.Check
|
create_new_batch: DF.Check
|
||||||
cumulative_time: DF.Int
|
|
||||||
customer: DF.Link | None
|
customer: DF.Link | None
|
||||||
customer_code: DF.SmallText | None
|
customer_code: DF.SmallText | None
|
||||||
customer_items: DF.Table[ItemCustomerDetail]
|
customer_items: DF.Table[ItemCustomerDetail]
|
||||||
@@ -120,7 +119,6 @@ class Item(Document):
|
|||||||
item_name: DF.Data | None
|
item_name: DF.Data | None
|
||||||
last_purchase_rate: DF.Float
|
last_purchase_rate: DF.Float
|
||||||
lead_time_days: DF.Int
|
lead_time_days: DF.Int
|
||||||
manufacturing_time: DF.Int
|
|
||||||
max_discount: DF.Float
|
max_discount: DF.Float
|
||||||
min_order_qty: DF.Float
|
min_order_qty: DF.Float
|
||||||
naming_series: DF.Literal["STO-ITEM-.YYYY.-"]
|
naming_series: DF.Literal["STO-ITEM-.YYYY.-"]
|
||||||
@@ -129,8 +127,6 @@ class Item(Document):
|
|||||||
opening_stock: DF.Float
|
opening_stock: DF.Float
|
||||||
over_billing_allowance: DF.Float
|
over_billing_allowance: DF.Float
|
||||||
over_delivery_receipt_allowance: DF.Float
|
over_delivery_receipt_allowance: DF.Float
|
||||||
planning_buffer: DF.Int
|
|
||||||
procurement_time: DF.Int
|
|
||||||
production_capacity: DF.Int
|
production_capacity: DF.Int
|
||||||
purchase_uom: DF.Link | None
|
purchase_uom: DF.Link | None
|
||||||
quality_inspection_template: 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.validate_auto_reorder_enabled_in_stock_settings()
|
||||||
self.cant_change()
|
self.cant_change()
|
||||||
self.validate_item_tax_net_rate_range()
|
self.validate_item_tax_net_rate_range()
|
||||||
self.set_cumulative_time()
|
|
||||||
|
|
||||||
if not self.is_new():
|
if not self.is_new():
|
||||||
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
|
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):
|
def on_update(self):
|
||||||
self.update_variants()
|
self.update_variants()
|
||||||
self.update_item_price()
|
self.update_item_price()
|
||||||
|
|||||||
Reference in New Issue
Block a user