Skip to content

Instantly share code, notes, and snippets.

@ManotLuijiu
Created March 21, 2026 18:57
Show Gist options
  • Select an option

  • Save ManotLuijiu/97e826c04b8c316a9288751e016a4e9e to your computer and use it in GitHub Desktop.

Select an option

Save ManotLuijiu/97e826c04b8c316a9288751e016a4e9e to your computer and use it in GitHub Desktop.
ERPNext v16: เพิ่ม Daily Frequency ใน Sales Forecast → MRP (แก้ปัญหา MRP ปฏิเสธ Daily)

แก้ปัญหา ERPNext v16: เพิ่ม Daily Frequency ใน Sales Forecast → MRP

สรุปปัญหา

เมื่อเพิ่ม "Daily" เป็นตัวเลือก frequency ใน Sales Forecast:

  • ✅ สร้าง Sales Forecast ได้
  • ✅ สร้าง MPS ได้
  • ❌ สร้าง MRP ไม่ได้ (ข้อมูลหายไปเงียบๆ)

สาเหตุ

ปัญหาอยู่ที่ฟังก์ชัน convert_to_daily_bucket_data() ในไฟล์ MRP Report

erpnext/manufacturing/report/material_requirements_planning_report/material_requirements_planning_report.py

ฟังก์ชันนี้แปลงข้อมูล forecast ทั้งหมดเป็นรายวัน แต่รองรับแค่ Monthly กับ Weekly:

# บรรทัด ~1237-1277
def convert_to_daily_bucket_data(data):
    bucketed_data = []
    for row in data:
        if row.frequency == "Monthly":
            # แปลง monthly → daily (หาร qty ด้วยจำนวนวันในเดือน)
            ...
        elif row.frequency == "Weekly":
            # แปลง weekly → daily (หาร qty ด้วย 7)
            ...
        # ← ไม่มี else สำหรับ "Daily" → ข้อมูลถูกข้ามไปเงียบๆ!
    return bucketed_data  # ← list ว่าง ถ้าทุก row เป็น Daily

เมื่อ MRP ได้ข้อมูลว่าง → ไม่สามารถสร้าง Production Plan ต่อได้

วิธีแก้ — ต้องแก้ 4 จุด

1. Sales Forecast JSON — เพิ่ม option

ไฟล์: erpnext/manufacturing/doctype/sales_forecast/sales_forecast.json บรรทัด: ~153

- "options": "Weekly\nMonthly",
+ "options": "Daily\nWeekly\nMonthly",

หรือใช้ Property Setter (ไม่ต้องแก้ไฟล์ core):

# รันใน bench console: bench --site yoursite.com console
frappe.make_property_setter({
    "doctype": "Sales Forecast",
    "fieldname": "frequency",
    "property": "options",
    "value": "Daily\nWeekly\nMonthly",
})
frappe.db.commit()

2. Sales Forecast Python — type hint

ไฟล์: erpnext/manufacturing/doctype/sales_forecast/sales_forecast.py บรรทัด: ~25

- frequency: DF.Literal["Weekly", "Monthly"]
+ frequency: DF.Literal["Daily", "Weekly", "Monthly"]

หมายเหตุ: บรรทัดนี้อยู่ในบล็อก auto-generated types — จะถูก overwrite ถ้ารัน bench generate-typestubs แต่ไม่กระทบการทำงานจริง

3. Sales Forecast Python — logic สร้าง demand

ไฟล์: sales_forecast.py → ฟังก์ชัน generate_manual_demand() บรรทัด: ~46-49

  if self.frequency == "Monthly":
      delivery_date = add_to_date(self.from_date, months=index + 1)
- else:
+ elif self.frequency == "Weekly":
      delivery_date = add_to_date(self.from_date, weeks=index + 1)
+ else:  # Daily
+     delivery_date = add_to_date(self.from_date, days=index + 1)

4. MRP Report — แปลง daily bucket (จุดสำคัญที่สุด!)

ไฟล์: material_requirements_planning_report.py ฟังก์ชัน: convert_to_daily_bucket_data() บรรทัด: ~1275 (เพิ่มก่อน return bucketed_data)

  elif row.frequency == "Weekly":
      # Convert weekly data to daily buckets
      start_date = getdate(row.delivery_date)
      for i in range(7):
          bucketed_data.append(
              frappe._dict(
                  {
                      "frequency": "Daily",
                      "item_code": row.item_code,
                      "delivery_date": add_days(start_date, i),
                      "sales_forecast": row.sales_forecast,
                      "stock_uom": row.stock_uom,
                      "qty": row.qty / 7,
                  }
              )
          )

+ elif row.frequency == "Daily":
+     # ข้อมูลเป็นรายวันอยู่แล้ว — ส่งผ่านตรงๆ
+     bucketed_data.append(
+         frappe._dict(
+             {
+                 "frequency": "Daily",
+                 "item_code": row.item_code,
+                 "delivery_date": getdate(row.delivery_date),
+                 "sales_forecast": row.sales_forecast,
+                 "stock_uom": row.stock_uom,
+                 "qty": row.qty,
+             }
+         )
+     )

  return bucketed_data

คำแนะนำ

❌ ไม่ควรแก้ ERPNext core โดยตรง

เพราะเมื่อรัน bench update การแก้ไขจะถูก overwrite ทั้งหมด

✅ วิธีที่แนะนำ: สร้าง Custom App

bench new-app daily_manufacturing
bench --site yoursite.com install-app daily_manufacturing

ใน custom app สร้างไฟล์ daily_manufacturing/overrides.py:

import frappe
from frappe.utils import getdate
import erpnext.manufacturing.report.material_requirements_planning_report.material_requirements_planning_report as mrp_report
from erpnext.manufacturing.doctype.sales_forecast.sales_forecast import SalesForecast
from frappe.utils import add_to_date

# --- Override 1: เพิ่ม Daily ใน generate_manual_demand() ---

original_generate = SalesForecast.generate_manual_demand

def patched_generate_manual_demand(self):
    forecast_demand = []
    for row in self.selected_items:
        item_details = frappe.db.get_value(
            "Item", row.item_code, ["item_name", "stock_uom as uom"], as_dict=True
        )
        for index in range(self.demand_number):
            if self.frequency == "Monthly":
                delivery_date = add_to_date(self.from_date, months=index + 1)
            elif self.frequency == "Weekly":
                delivery_date = add_to_date(self.from_date, weeks=index + 1)
            else:  # Daily
                delivery_date = add_to_date(self.from_date, days=index + 1)

            forecast_demand.append({
                "item_code": row.item_code,
                "delivery_date": delivery_date,
                "item_name": item_details.item_name,
                "uom": item_details.uom,
                "demand_qty": 1.0,
            })

    for demand in forecast_demand:
        self.append("items", demand)

SalesForecast.generate_manual_demand = patched_generate_manual_demand


# --- Override 2: เพิ่ม Daily ใน convert_to_daily_bucket_data() ---

original_convert = mrp_report.convert_to_daily_bucket_data

def patched_convert_to_daily_bucket_data(data):
    daily_rows = [r for r in data if r.frequency == "Daily"]
    non_daily = [r for r in data if r.frequency != "Daily"]

    # ให้ฟังก์ชันเดิมจัดการ Monthly + Weekly
    result = original_convert(non_daily)

    # เพิ่ม Daily rows ตรงๆ (ไม่ต้องแปลง — เป็นรายวันอยู่แล้ว)
    for row in daily_rows:
        result.append(frappe._dict({
            "frequency": "Daily",
            "item_code": row.item_code,
            "delivery_date": getdate(row.delivery_date),
            "sales_forecast": row.sales_forecast,
            "stock_uom": row.stock_uom,
            "qty": row.qty,
        }))

    return result

mrp_report.convert_to_daily_bucket_data = patched_convert_to_daily_bucket_data

ใน daily_manufacturing/__init__.py:

# Import overrides on app load
from daily_manufacturing import overrides  # noqa: F401

เพิ่ม Property Setter สำหรับ frequency field ใน daily_manufacturing/setup.py:

import frappe

def after_install():
    frappe.make_property_setter({
        "doctype": "Sales Forecast",
        "fieldname": "frequency",
        "property": "options",
        "value": "Daily\nWeekly\nMonthly",
    })
    frappe.db.commit()

ใน hooks.py:

after_install = "daily_manufacturing.setup.after_install"

ทดสอบ

หลังติดตั้ง:

  1. สร้าง Sales Forecast → เลือก frequency = Daily ✅
  2. สร้าง MPS ✅
  3. สร้าง MRP → ต้องมีข้อมูลแสดง ✅
  4. View MRP → เห็นข้อมูลรายวัน ✅
  5. สร้าง Purchase Order / Work Order ✅

วิเคราะห์โดย Claude Code — Bunchee Online (bunchee.online)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment