Capacity Planner
The Capacity Planner at /equipment/capacity is the supply-side counterpart to the forecast module. Forecast says what is needed and when. The planner says whether you can actually produce it given the routings, machine speeds, planned maintenance, and material lead times.
This is where production planners spend their time before publishing the weekly schedule.
Where it lives
- Page:
/equipment/capacity - Permissions:
CAPA_PERMITTEDmodule +capa_readto view,capa_writeto recompute. - Snapshot table:
ems_capasnapshot— stores the computed result per (work_center, period_start, period_end).
What you see
Work Center grid
A 4-column card grid of every Work Center in the tenant. Each card shows:
- Name + a red Critical badge if
critical_for_capa = true. - Current utilization % (from the latest CapaSnapshot — or "no snapshot" if never computed).
- A ring progress indicator color-coded by utilization (green <85%, orange 85-100%, red ≥100%).
Cards are sorted critical-first. You can click any card to drill in.
Empty state: if the tenant has zero Work Centers, an inline "Go to Work Centers" CTA appears. If WCs exist but none have snapshots yet, each card shows "no snapshot" — click into one and hit Recompute.
Detail panel (after clicking a card)
When a card is selected, a detail panel appears below the grid showing the latest CapaSnapshot for that WC:
- Risk level badge (
NONE/LOW/MEDIUM/HIGH) - Period (e.g.
2026-05-28 → 2026-06-27, 30 days) - Available minutes — total calendar minutes after availability factor.
- Effective minutes — available × OEE target.
- Demand minutes — total load from open routing operations.
- Overload minutes — demand minus effective (capped at 0).
- Utilization %
Plus two tables:
ETA by work order
Each row: work order number, projected completion datetime, risk badge. Sorted by ETA (earliest first). Hover any row to see the gating routing operation.
Bottleneck materials
Each row: item, shortage qty, projected arrival date, the WO it gates. Populated when the CAPA calculation determines a routing operation can't start because its BOM has uncovered demand and the next reorder won't arrive in time.
Recompute button
+ Recompute on the detail panel. Triggers BuildSvc.CalculateCapacity synchronously for the selected WC over the next 30 days. The result upserts ems_capasnapshot (unique on tenant_id, work_center_id, period_start, period_end) and refreshes the panel.
A success toast confirms the recompute; an error toast names what failed.
How utilization is computed
1. Calendar minutes from shift pattern, multiplied by availability_percent
calendar_min = expandShiftPattern(wc.shift_pattern, wc.rated_hours_per_day, start, end)
× (wc.availability_percent or 100) / 100
2. Subtract planned PM downtime that overlaps the window — but only if a
critical_asset (LEAF level) is the one being maintained. Non-critical PM
doesn't down the WC.
pm_minutes = sum(overlap(event, start, end)
for event in OpenMaintenanceEvents(assets)
if anyCriticalLeafAffected(event.asset_id))
3. Subtract current unplanned DOWN time
down_min = (end - max(now, start)) if wc.runtime_status == DOWN else 0
available_minutes = max(0, calendar_min - pm_minutes - down_min)
effective_minutes = available_minutes × (wc.oee_target_percent or 85) / 100
4. Demand: sum across open routing_operations routed to this WC, in priority order.
Each op adds setup (or default_setup_minutes if unset) + runtime_per_unit × target_qty.
A changeover penalty applies when the next op produces a different item.
5. Material-availability gating per WO
For each routing op's parent work order, walk the BOM. For any line whose
required qty exceeds qty_on_hand and whose item.lead_time_days pushes arrival
past the WO's start_datetime, add to bottlenecks[] and push ETA out.
6. Overload + risk
overload_min = max(0, demand_minutes - effective_minutes)
util_pct = demand_minutes / effective_minutes × 100
risk = NONE if util<85, LOW if util<100, MEDIUM if util<120, HIGH otherwise
On-demand vs nightly
| Trigger | When |
|---|---|
| Manual recompute | Operator hits the button. Synchronous. ~3-10s per WC depending on routing complexity. |
Nightly cron (build-ms 02:00) | Loops every critical_for_capa = true WC. Bulk-recomputes [today, today+30d] and upserts snapshots. Fires workcenter_overloaded SSE on any WC that flipped to overload since the last run. |
The nightly job is the source of truth for the Equipment Hub's "WCs at Risk" KPI and capacity heatmap. Manual recompute updates a single WC's snapshot — the hub picks it up on next poll (60s).
When to recompute manually
- After adding/changing a critical routing operation.
- After accepting a new sales order with a tight ETA.
- After flagging an asset critical or DOWN.
- After editing a WC's CAPA fields (shift pattern, OEE, availability).
- When the planner needs the latest utilization, not yesterday's snapshot.
Reading the snapshot
| Field | Means |
|---|---|
available_minutes | Working minutes after planned downtime and current unplanned DOWN. |
effective_minutes | available × OEE target. The "honest" capacity. |
demand_minutes | Total load from open ops queued at this WC. |
overload_minutes | demand − effective (≥0). What's not going to fit. |
utilization_pct | demand ÷ effective × 100. |
eta_by_workorder | JSON map of WO ID → projected completion datetime. |
bottleneck_materials | JSON array of {item_id, shortage_qty, arrival_date, workorder_id}. |
risk_level | Bucketed: NONE / LOW / MEDIUM / HIGH. |
overload | Boolean: overload_minutes > 0. |
Recipes
"Are we going to ship on time?"
- Open the planner.
- Look at the WCs with risk badges MEDIUM / HIGH — those are the gating constraints.
- Click the worst one → review the ETA-by-workorder table.
- The bottom rows are the WOs that won't ship.
"Why is this WO late?"
- Recompute the WC that owns the WO's gating operation.
- Read the ETA row for that WO. If it's far past
due_date, two things could be true:- Capacity overload — the WC has more total minutes queued than effective.
- Material bottleneck — appears in
bottleneck_materials. The arrival date pushes the ETA.
- Mitigation:
- Reroute the op to a backup WC (modify routing).
- Expedite the BOM material (purchasing).
- Reduce the WO target_qty (sales).
"We just flagged a press as critical and it's about to go down for a 4-hour PM. Is the line OK?"
- Make sure the PM event has scheduled_start / scheduled_end set.
- Make sure the asset has
critical_asset = true. - Recompute its WC.
- The snapshot now shows 4 fewer available_minutes. If utilization is still <100%, you're fine. If it pushed to overload, you need to reschedule the PM or the WOs.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| Page is blank | Permission missing (CAPA_PERMITTED and/or capa_read). |
| All WCs show "no snapshot" | Nightly cron hasn't run yet (or critical_for_capa = false on all WCs). Use the manual Recompute button on one WC to seed. |
| Recompute throws an error | Check the network response. Common causes: period_start ≥ period_end, no routing operations exist for the WC, or backend service down. |
| Utilization seems wildly high | oee_target_percent or availability_percent may be too low. Verify the WC's CAPA settings. |
| Bottleneck table is empty even though we know parts are short | Item's lead_time_days isn't set on the master, OR the WO's BOM references items not currently in stock with qty_on_hand sufficient by coincidence. Audit the BOM lines. |
| ETAs show today even for far-future WOs | The WO's routing has only ops with runtime_per_unit = 0. Set realistic runtime values on routing ops. |
Related
- Work Centers for CAPA — the inputs to the calculation
- Asset Management — the critical-asset flag that gates downtime cascading
- Equipment Hub — the heatmap surfaces snapshot data
- Maintenance Events — scheduled events reduce effective capacity