Skip to content

Supply Planning

Table of Contents


Supply Planning is accessible from multiple places:

  • The Supply tab within the Time Series Viewer (single series view)
  • The Supply Planning tab within the Pipelines screen (scenario management)
  • The Dashboard supply plan panel (aggregate view)

Supply planning translates the demand forecast into a week-by-week inventory projection and generates replenishment orders to keep projected on-hand above zero.

Figure: the MRP flow nets multi-source demand, explodes the BOM with pegging, generates planned orders, applies lot-sizing and capacity checks, and firms them into PIPE_supply_orders.

flowchart LR
  DEM["Demand<br/>(stat + maint + causal forecast)"] --> NET["Netting v2<br/>(firm orders, returns)"]
  NET --> EXP["BOM explosion + pegging"]
  EXP --> PO["Planned orders"]
  PO --> LS["Lot-sizing<br/>(min/max/mult)"]
  LS --> CAP["Capacity check<br/>(scheduling.rs)"]
  CAP --> FIRM["Firm / PO<br/>(PIPE_supply_orders)"]

Supply projection chart

The Supply tab in the Time Series Viewer shows projected on-hand, on-order quantities and expected receipts.


Core Concepts

On-Hand (OH)

On-hand is the physical stock currently in a warehouse or stocking location. In the supply projection, "on-hand" refers to the projected future on-hand — what the model predicts the physical stock will be in each future period, after accounting for expected demand and planned replenishments.

When projected on-hand drops below zero, this represents a stockout: demand is expected to exceed available supply in that period.

On-Order (OO)

On-order is the quantity currently ordered from suppliers or manufacturing, not yet received. On-order stock is expected to arrive and increase on-hand when it lands (on the expected receipt date).

Projected On-Hand

Projected on-hand is calculated week by week:

Projected OH[week N] = Projected OH[week N-1] 
                     - Demand Forecast[week N] 
                     + Orders arriving[week N]

A week with negative projected OH is a stockout period. These appear as red markers in the supply charts and as Supply exceptions in the Exceptions screen.


Route Types

Mirabelle supports five route types that define how an item is replenished:

Route Type Label Meaning
PURCHASE Buy Order from an external supplier. Creates a purchase order.
TRANSFER Transfer Move stock from another internal location. Creates a transfer order.
MAKE Build Manufacture the item internally. Creates a work order.
RETURN Return Returns from customers create supply. Applies a return rate fraction.
REPAIR Repair Returned units that can be refurbished. Applies a repair yield fraction.

A single item can have multiple routes (e.g. primary = PURCHASE, secondary = TRANSFER from another warehouse). The supply engine prioritises routes based on the supply.source_priority configuration: Repair → Return → Transfer → Build → Buy (repair is used first if available).

Lead Times

Each route has a lead time: the number of days between placing the order and receiving the stock. The supply engine uses the route lead time to determine when to place orders:

Order placement date = Required receipt date − Lead time

Lead times can vary over time (historical actuals) and are shown in the Supply Cell sparkline in the Exceptions screen.


Supply Projections

The supply projection is the output of a supply planning run. It shows, for each item-site and each week in the planning horizon:

Column Description
Period Week start date
Demand Forecast Netted demand for that week (from the selected forecast type)
On-Hand (start) Projected on-hand at the beginning of the week
On-Order Quantity expected to arrive during the week
On-Hand (end) Projected on-hand at the end of the week, after demand and receipts
Safety Stock The MEIO-recommended safety stock buffer
Orders Placed Any replenishment orders placed in this period

Weeks where On-Hand (end) is negative are highlighted in red.


Orders

The supply planning engine generates three types of planned orders:

Purchase Orders

Created when an item needs to be bought from a supplier. The order shows:

  • Item and site
  • Quantity
  • Expected order date (when to place the order)
  • Expected availability date (when the stock is expected to arrive)
  • Supplier route
  • Status: planned, suggested, approved

Transfer Orders

Created when stock should be moved from one internal location to another. The order shows source site and destination site in addition to quantity and dates.

Work Orders

Supply summary

The Dashboard Supply tab gives an aggregated view of open orders, shortages and coverage.

Created for manufactured items (MAKE route). Represent internal production orders.

Order Status

Status Meaning
Planned System-generated based on the supply projection; no human action yet
Suggested System has recommended this order; waiting for planner approval
Approved Planner has approved; typically means the order will be communicated to the supplier

Figure: a planned order moves through suggestion to planner approval; edits or rejection return it to the planned pool.

stateDiagram-v2
  [*] --> Planned: system-generated
  Planned --> Suggested: recommended
  Suggested --> Approved: planner approves
  Suggested --> Planned: edited / rejected
  Approved --> [*]

Planners can approve suggested orders directly from the Exceptions screen (Supply Cell) or from the Supply Planning screen.


BOM Explosion

For manufactured items or assemblies, the supply engine performs BOM explosion: the demand for the parent item is propagated down to its component items.

For example, if a finished assembly needs 100 units, and its BOM requires 2 units of Component A and 3 units of Component B, the explosion creates demand of 200 units for Component A and 300 units for Component B. These component demands are added to any direct customer demand for those components.

BOM explosion is handled automatically by the supply planning step. The BomExplorer component in the Time Series Viewer provides a visual tree of any item's BOM structure.


Negative On-Hand = Stockout Risk

Any week where projected on-hand is negative represents a potential stockout. The system surfaces these as:

Figure: a negative projected on-hand triggers the stockout signal, which fans out to UI markers, exceptions, and a likely-cause diagnosis.

flowchart TD
  OH["Projected on-hand[week N]"] --> Q{"< 0?"}
  Q -- "no" --> OK["healthy stock"]
  Q -- "yes" --> ST["Stockout signal"]
  ST --> E1["Red marker on lead-time sparkline"]
  ST --> E2["Supply exception"]
  ST --> E3["Red cell in projection table"]
  ST --> CA{"Likely cause"}
  CA --> C1["safety stock too low"]
  CA --> C2["order delayed / cancelled"]
  CA --> C3["demand spike"]
  CA --> C4["lead time increase"]
  1. Red markers on the lead time sparkline in the Supply Cell of the Exceptions screen
  2. Supply type exceptions in the exception list
  3. Red cell highlighting in the supply projection table

The on-hand quantity shown (e.g. −34) is the magnitude of the shortfall: 34 units of unmet demand.

The most common causes of projected stockouts:

  • Safety stock is too low (MEIO has set insufficient buffer)
  • A replenishment order was delayed or cancelled
  • Demand spiked above forecast
  • A lead time increase means an order will arrive too late

On-Hand at Repair Warehouses

Repair warehouses (sites whose only inbound route type is REPAIR) have a different interpretation of "good stock": because they receive returns and rework them, the useful throughput is the repair output rate rather than a static on-hand balance.

In the supply overview, the OH Bad metric for repair-only sites is calculated from actual REPAIR order quantities rather than from a physical good/bad inventory split. This avoids misleading spikes from units that are in-process rather than truly bad stock.


Approving Orders from the Exceptions Screen

From the Exceptions Supply Cell:

  1. The next planned order is shown with its ID, quantity, and expected availability date.
  2. Edit the Qty field if you want to approve a different quantity than planned.
  3. Edit the Date field if the availability date needs to change.
  4. Click Approve.

The order status changes to approved. The supply projection does not automatically recalculate at this point — you need to re-run the supply planning step for the projection to reflect the approved order.


Netting

The supply engine does not use the raw statistical forecast directly. It uses a netted forecast that adjusts for:

  • Firm orders — known confirmed demand that is more reliable than the forecast
  • Return forecast — expected returns from customers, which reduce net demand
  • Open purchase orders already placed — these represent supply already committed
  • In-transit stock — stock that has been shipped but not yet received
  • Multi-source demand — statistical, maintenance, and causal forecasts are combined according to their configured netting modes before consumption logic is applied

The forecast type used for base netting (supply.netting_forecast_type) can be:

  • statistical — use the statistical forecast from the forecasting pipeline
  • causal — use the causal forecast (if causal forecasting has been run)
  • maintenance — use the maintenance/event-driven forecast
  • blended — use a weighted combination

The netted forecast is visible in the Netting tab of the Time Series Viewer.

Netting Version

The current supply planning pipeline runs Netting v2 (2026-06). This version introduced safety-stock-aware carry-forward in the Rust engine, multi-source demand handling, two-step netting sequences, parallel computation, ClickHouse-primary persistence, and return forecast projection.

If you are upgrading from an older pipeline version, regenerate the netting table by re-running the supply planning step. The v2 schema adds causal_* columns and changes the netting_direction vocabulary from forward_first / backward_first to forward / backward.

Multi-Source Demand

The netting engine now consumes three independent demand sources:

Source Description
Statistical forecast The baseline forecast produced by the statistical forecasting pipeline
Maintenance forecast Event-driven or maintenance-driven demand (e.g. scheduled service parts)
Causal forecast Causal model output (e.g. promotions, weather, external regressors)

Each source is passed into the netting engine with its own quantity per week. The engine applies the configured netting mode (see below) to each source before computing final net demand.

Netting Modes (add vs net)

For maintenance and causal forecasts, the pipeline configuration (netting_mode_maintenance, netting_mode_causal) can be set to:

Mode Behaviour
add The forecast is added to the soft (statistical) forecast. The combined quantity is then consumed by firm orders during netting.
net The forecast is treated as firm-like demand that consumes the soft forecast first. Any remaining soft forecast after net-mode demands are applied is then available for firm customer orders.

add mode is appropriate when the forecast source represents incremental demand that should be merged with the statistical baseline. net mode is appropriate when the forecast source represents high-certainty demand (e.g. a signed promotion) that should take priority over the statistical forecast.

Two-Step Netting Sequence

When netting_sequence = 'two_step' and at least one of maintenance or causal is configured in net mode, the engine performs netting in two passes:

  1. First passnet-mode demands (maintenance and/or causal) are netted against the soft (statistical) forecast. The soft forecast is consumed forward or backward by these demands, producing a pre-netted remainder.
  2. Second pass — Firm customer orders are netted against the pre-netted remainder (not the original soft forecast). This gives net-mode forecast demands priority over firm customer orders.

When netting_sequence = 'single' (or no sources are in net mode), all demands are combined into a single soft forecast and firm orders consume that total in one pass.

The netting direction (whether firm orders consume from future forecast periods forward or backward) is controlled by the direction key in the forecast parameter set's netting section: forward or backward.

Safety-Stock-Aware Carry Forward

In the Rust netting engine (files/supply/src/netting.rs), projected on-hand is carried forward week by week using:

carry = max(0, available - demand)

where available = previous_carry + supply and demand is the net demand for the period. Previously, whenever available < required (with required = demand + safety_stock), the carry was set to 0.0. This caused false zero-inventory signals and phantom shortages in downstream periods. The v2 engine preserves physical inventory even when the safety-stock buffer is violated, and only reports a shortage when physical stock itself would go negative.

A small epsilon (NETTING_EPS = 1e-4) is used internally to prevent floating-point rounding drift.

Parallel Computation

When more than 50 series are processed in a single netting run, the Python layer parallelises series-level computation via a ThreadPoolExecutor with up to min(cpu_count, 16) workers. Smaller runs remain single-threaded to avoid executor overhead.

Return Forecast Projection

Projected returns are computed from demand (including maintenance forecast when it is in add mode), time-shifted by return_lt_weeks and scaled by return_rate (taken from RETURN routes). The return forecast reduces net demand before the supply engine plans replenishment orders.

Netting Columns

The netting table stores the following per-week quantities:

Column Description
stat_forecast_qty Raw statistical forecast
return_forecast_qty Expected return quantity (reduces net demand)
maint_forecast_qty Maintenance-driven forecast quantity
causal_forecast_qty Causal forecast quantity
firm_demand_qty Confirmed firm orders
total_netted_qty Final net demand sent to the supply engine
stat_netted_qty Stat portion after netting
return_netted_qty Return portion after netting
maint_netted_qty Maintenance portion after netting
causal_netted_qty Causal portion after netting
return_consumed_qty Returns that consumed firm demand
causal_consumed_qty Causal demand that consumed the soft forecast
consumption_enabled Whether demand consumption logic is active
netting_direction forward or backward
is_past 1 for historical rows, 0 for future. The supply engine only reads is_past = 0 rows.

Historical rows in netting

PIPE_forecast_netting stores both past and future rows. The supply engine always filters AND is_past = 0. If you query the netting table directly, add this filter to avoid treating historical demand as future demand.

ClickHouse-primary persistence

Starting with v2, PIPE_forecast_netting is persisted to ClickHouse as the primary store. The pipeline uses drop_ch_partition followed by bulk_insert_ch for full-table refresh. Querying the netting table should target the ClickHouse instance.


Supply Planning Scenarios

Like forecasting, supply planning supports multiple scenarios. Different supply scenarios can use:

  • Different forecast scenarios as input
  • Different MEIO results (safety stock levels)
  • Different lead time assumptions
  • Different source priority rules

This allows planners to run "what-if" analyses: "what would our inventory projection look like if lead times increased by 2 weeks?" or "what is the impact of switching from a 95% to a 98% service level target?"


Reading the Supply Plan on the Dashboard

The Dashboard's Supply Plan panel (at the bottom, lazy-loaded) shows an aggregate view of the supply projection across all items in the active pipeline. Key KPIs shown:


Advanced Supply Planning Features

Beyond the core projection and ordering logic described above, Mirabelle offers several advanced capabilities that extend the supply planning workflow:

Feature Description
Capacity & Resource Network Model production capacities and resource constraints to ensure the supply plan respects real-world throughput limits. See Supply Advanced → Capacity & Resource Network.
Calendar Closure Handling Account for warehouse and plant closures (holidays, maintenance windows) so that orders are not placed on non-working days. See Supply Advanced → Calendar Closure.
Sales Returns Incorporate customer return forecasts as a supply source, reducing net demand and improving on-hand projections. See Supply Advanced → Sales Returns.
Demand Profile Builder Define custom demand profiles (seasonality patterns, growth curves) that override or supplement the statistical forecast for specific item-site combinations. See Supply Advanced → Demand Profile Builder.
Consumption Preview Preview how demand will be consumed by firm orders, returns, and other netting adjustments before running the full supply plan. See Supply Advanced → Consumption Preview.
Pegging & Traceability Trace which demand orders each supply order fulfills, and vice versa — full upstream/downstream linkage for audit and root-cause analysis. See Supply Advanced → Pegging & Traceability.
Firm Supply Orders Treat ERP-originated executed/blocked orders as INPUT to the engine. Two new flags (firm_orders_block_horizon, respect_lead_time) tighten how the engine generates recommendations around them. See Firm Supply Orders.

Allocation Engine Trigger

Once the supply planning run completes and replenishment orders are generated, the Allocation engine is triggered automatically. The allocation step distributes available supply (on-hand + on-order) across competing demand streams according to prioritisation rules defined in the allocation configuration. This ensures that constrained stock is allocated to the highest-priority channels, customers, or regions first.

For details on allocation rules, priority configuration, and the allocation workflow, see Allocation.