MANUFACTURING MODULE SPEC

02 — Planning Layer

The brain. Translates expected/actual demand into a concrete, feasible production plan. Inherits all rules from 00. Three sequential components: MPS → MRP → CRP, with a closed feedback loop.


2.1 The Three Outputs of Planning

Planning, as a whole, produces exactly three deliverables. Keep these as the mental model:

Output 1: Can we accept the order? By when can we deliver?   (MPS + CTP)
Output 2: Material schedule — what to buy/make, how much, when (MRP)
Output 3: Detailed production plan — when each stage starts    (MRP draft + CRP confirm)

Inputs: customer order, master data (BOM/Routing/WC), current inventory, item settings, material ownership.


2.2 Component: MPS (Master Production Schedule)

Purpose: Decides what to produce, how much, when. Under make-to-order it shifts from "forecaster" to "order translator + acceptance/capacity checker."

Behaviour branches by production_type

if MakeToStock:
    demand = apply_forecast_consumption(forecast, actual_orders)
    PAB[t] = PAB[t-1] + planned_production[t] - demand
    if PAB[t] < safety_stock: suggest production
if MakeToOrder:
    ignore forecast; planned_production = confirmed_orders; safety_stock = 0
    run CTP check (capacity-based promise)
if AssembleToOrder:
    components → MakeToStock; finished assembly → MakeToOrder
if TollManufacturing:
    plan capacity only (material is customer's, see 2.3)

Core formula — PAB (Projected Available Balance)

PAB[t] = PAB[t-1] + scheduled_production[t] - demand[t]
demand[t] = (see forecast_consumption + time_fence rules below)

Forecast Consumption (avoid double-counting)

Actual orders consume the forecast, they don't add to it:

consumed = MIN(actual_orders[t], forecast[t])
remaining_forecast = forecast[t] - consumed
total_demand[t] = actual_orders[t] + remaining_forecast
if actual_orders[t] > forecast[t]: total_demand[t] = actual_orders[t]

Config: forecast_consumption_method ∈ {None, Backward, Forward, Both}, consumption_window.

Time Fences (state by distance from today)

distance = t - today
if distance <= DTF (Demand Time Fence):   zone=FROZEN  → demand = actual_orders only (ignore forecast)
elif distance <= PTF (Planning Time Fence): zone=SLUSHY → MAX/consumption blend, changes need approval
else:                                       zone=LIQUID → demand = forecast (no orders yet)

Prevents "system nervousness": near-term plan is stable, far-term is flexible.

CTP (Capable to Promise) — core of make-to-order

CTP_Check(product P, qty Q, due D):
    for op in routing(P):
        if op.wc is PRESS-like:
            cycles = Q / op.cavity_count
            load[wc] += cycles × op.cycle_time + op.setup_time
        else:
            load[wc] += Q × op.run_time + op.setup_time
    for wc in required: earliest[wc] = find_earliest_slot(wc, load[wc])
    promised = MAX(earliest[wc])           # bottleneck决定
    return promised <= D ? "on time" : ("earliest: " + promised)

Order splitting & batching (Many-to-Many)

Pegging: { production_order_id, sales_order_id, allocated_quantity }

MPS data model

MPS_Header: { id, plan_name, plant_id, time_bucket∈{Day,Week,Month},
              start_date, end_date, frozen_fence, slushy_fence, status }
MPS_Line:   { product_id, period_date, forecast_qty, confirmed_orders_qty,
              planned_production, projected_balance, safety_stock_target,
              available_to_promise }

ATP calculation (3 modes — config)

Discrete:   ATP[t] = production[t] - orders until next production
Cumulative: ATP[t] = Σproduction≤t - Σorders≤t
Look-ahead: ATP[t] = production[t] - orders(t .. next production)   ← safest

2.3 Component: MRP (Material Requirements Planning)

Purpose: Turn production orders into material needs: what to buy/make, how much, when. It is a process, not a table — produces transient Planned Orders.

Item MRP settings

Item_MRP_Settings: {
  item_id, mrp_type∈{Planned,ReorderPoint,NoPlanning},
  procurement_type∈{Buy,Make}, material_ownership∈{Own,Customer},
  lot_sizing_rule∈{Exact,Fixed,MinMax,EOQ,PeriodOrder}, lot_size, min_lot, max_lot,
  safety_stock, lead_time_days, reorder_point
}

Algorithm (level-by-level, top to deepest)

MRP_Run():
  sort items by BOM level (finished=0, raw=deepest)
  for each item, for each period t:
    Gross[t]     = MPS_demand (finished) + dependent_demand_from_parents (component)
    Available[t] = on_hand + open_POs[t] + open_WOs[t] - reserved[t]
    Net[t]       = MAX(0, Gross[t] - Available[t] + safety_stock)
    Order_Qty    = apply_lot_sizing(Net[t], lot_rule)
    Release_Date = Required_Date - lead_time
    if procurement_type == Make:
        explode_BOM → dependent demand for components → recurse deeper

BOM Explosion (recursive, multi-level)

explode_BOM(product P, qty Q):
  for line in BOM(P):
    required = Q × line.quantity × (1 + line.scrap_pct)
    dependent_demand[line.component] += required
    if line.component.procurement_type == Make:
        explode_BOM(line.component, required)

Lot Sizing (key branch for tooling factories)

apply_lot_sizing(net, rule):
  Exact:       net
  Fixed:       round_up(net, lot_size)
  MinMax:      clamp(net, min, max)
  EOQ:         economic_order_qty()
  PeriodOrder: Σ net over several periods   ← consolidate to cut setup/mold changes

Tooling-heavy items use PeriodOrder/Fixed to batch demand and minimize mold mounts.

Toll manufacturing branch

when generating purchase for a component:
  if component.material_ownership == Customer:
      DO NOT raise purchase order   (customer supplies it)
      instead: verify customer-supplied quantity is sufficient; if short → alert
  else:
      raise normal purchase order

Outputs

- Planned Purchase Orders   → Procurement module
- Planned Production Orders → Execution layer
- Exception Messages        → "will be late", "reschedule in/out", "cancel"

2.4 Component: CRP (Capacity Requirements Planning)

Purpose: MRP plans assuming infinite capacity. CRP injects reality — verifies work centers (and tools, and labor) have enough capacity at the required time. In tooling factories, CRP matters more than MRP (materials are easy; scheduling N tools on M machines is the real problem).

Algorithm

CRP_Run():
  # 1. Load per planned order
  for order, op:
    if op.wc is PRESS-like:
      cycles = order.qty / op.cavity_count
      load = cycles × op.cycle_time + op.setup_time
    else:
      load = order.qty × op.run_time + op.setup_time
  # 2. Distribute over periods → 3. Aggregate per resource
  Total_Load[resource, period] = Σ loads
  # 4. Compare
  Util = Total_Load / effective_capacity × 100
  Util > 100 → OVERLOAD ; Util < 70 → UNDERLOAD ; else OK
  # 5. Resolve overload
  → reschedule / overtime / extra shift / defer order / use multi-cavity tool

Resource intersection (Machine + Tool + Labor)

earliest_start = MAX(machine_free_time, tool_free_time, labor_free_time)

The binding constraint is the minimum-availability resource. A tool is a movable resource that must be tracked across machines.

RCCP vs CRP (both supported)

RCCP (Rough Cut): on MPS, critical resources only → used by CTP for order acceptance
CRP  (Detailed):  on MRP, all resources → used for detailed scheduling

Melamine test case

WC-PRESS week3 capacity 90h. Order 6000 dishes, mold cavity=1, cycle 80s → 133h.
Util=148% → OVERLOAD. Resolution options surfaced, including:
  use 4-cavity mold variant → 6000/4 ×80s = 33h ✓
This links a scheduling decision (CRP) to a master-data choice (which mold).

2.5 Known Gap — Detailed Scheduling (APS)

CRP detects overload but does NOT sequence operations hour-by-hour on specific machines. A future Advanced Planning & Scheduling (APS) component should: - Sequence operations on each machine/tool on a timeline (Gantt). - Optimize mold-mount order to minimize changeovers (the hardest problem: ~50 molds on ~4 presses). - Respect machine warm-up, shift patterns, tool availability.

Interface: consumes Planned Orders + Routing + Resource calendars; emits a time-phased schedule that SFC (file 05) executes.


2.6 Closed Loop

CRP finds conflict → feeds back to MPS → adjusts promised_date → re-runs
Costing variances (file 04) → inform standards & future planning