Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Material Requirements Planning (MRP)

Material Requirements Planning — MRP for short — is the part of Beelocity that answers two questions on a regular cadence:

  1. What do we need to make, given the orders we’ve taken and the safety stock we want to keep?
  2. What do we need to buy, given the components our work orders will consume?

An MRP run is a snapshot. It captures demand and supply at a moment in time, walks every product from the highest level of the BOM tree down to raw materials, and produces two outputs: planned orders (proposals to create new supply) and action messages (advice on existing supply that no longer lines up with demand). The planner reviews the output, converts the proposals they want into real work orders or purchase requisitions, and ignores the rest.

When to run MRP

Most teams run MRP on a regular cadence — weekly is typical, often as an overnight batch. Trigger an ad-hoc run when:

  • A new sales order with an aggressive due date arrives and you need to know whether you can hit it.
  • A purchase order is delayed and you want to see which downstream work orders are at risk.
  • You’ve just released a batch of new BOMs and want to recompute the levels of the items they touch.
  • A planner is starting a new shift and wants the latest picture of “what’s late, what’s expediting, what’s in surplus”.

The dev-stack default is to run MRP on demand from the MRP Runs page. Production deployments typically schedule the run via cron.

Creating and running a run

Beelocity splits run creation from run execution so you can stage parameters, review them, and run later — useful for long runs that need a quiet window or for queueing a NET_CHANGE alongside the standard nightly FULL.

  1. Navigate to Manufacturing > MRP Runs.
  2. Click + New Run. The Run page opens with editable defaults pre-filled:
    • Run # — auto-generated as MRP-YYYY-MM-DD-XXXX. Override it if you want to group a planned series (e.g. MRP-WEEKLY-2026-W19).
    • TypeFULL re-plans every item; NET_CHANGE only re-plans items whose demand or supply has changed since the last run.
    • Horizon (days) — defaults to 90. Set it to cover your longest cumulative lead time plus a buffer.
    • Demand Time Fence (days) — defaults to 7.
    • Planning Time Fence (days) — defaults to 30. Must be at least as large as the demand fence and at most the horizon.
  3. Click Create. The run lands in PENDING state with no snapshots yet — it’s just configuration.
  4. Click Execute on the run page when you’re ready. The status flips through PENDING → RUNNING → COMPLETED as Beelocity snapshots demand, snapshots supply, recomputes BOM levels, and walks every item from the top down. The page polls every two seconds while the run is live, so you can watch the row counts on the Overview tab populate.
  5. When the run completes, the Demand, Supply, and Planned tabs populate.
  6. While a run is RUNNING, the Cancel button on the run page aborts it. A failed run stays in FAILED state with the error message captured on the Overview tab; you can re-execute it after fixing the underlying data (e.g. breaking a BOM cycle).

Reading the output

The summary

The Summary card on the Overview tab is the at-a-glance view:

  • Demand lines — how many demand records the run snapshotted.
  • Supply lines — how many supply records the run snapshotted.
  • Planned orders — how many proposals the engine emitted.
  • Action messages — how many pieces of advice on existing supply.
  • Max LLC walked — how deep the BOM tree went. A typical assembled product run reaches LLC 3–5; raw-material-only runs stay at LLC 0–1.

Demand and Supply tabs

Two read-only views of the snapshots.

The Demand tab shows what the org owes the world: open sales-order lines, safety-stock targets, and dependent demand from manufactured items at higher levels. Each row carries a source (SALES_ORDER, SAFETY_STOCK, DEPENDENT), a quantity, and a due date.

The Supply tab shows what the org expects to have available: on-hand stock (less reservations), open purchase-order lines, active work orders, and in-transit transfers. Each row carries a source (ON_HAND, PURCHASE_ORDER, WORK_ORDER, TRANSFER), a quantity, and an available date.

Planned tab

This is where the work happens. Each row is one of two things:

  • A planned order — a proposal to create new supply. PLANNED_WO for items with an active BOM (we’d manufacture); PLANNED_PO for items without (we’d purchase from a supplier).
  • An action message — advice on existing supply: RESCHEDULE_EARLIER (move it forward), EXPEDITE (pull it in inside the planning fence), CANCEL (no longer needed because demand evaporated).

The grid is sorted by LLC (lowest first — the deepest raw materials) and then by priority score (lower = more urgent). Convert the rows you want at the top of the list first.

“Why was this proposed?”

Every planned order carries a pegging trace — the chain of demand records that drove it. Click the why? button on a row to open the trace. You’ll see the sales orders, safety-stock targets, or dependent-demand records (from higher-level planned WOs) that contributed to this need, with their own quantities and dates. This is the answer to “why is the engine asking me to make 47 of these?” — it’s the literal demand chain.

Time fences

Two fences guard short-horizon stability. Both are configurable per run.

Demand-time fence (default 7 days)

No demand record dated inside the next 7 days produces a new planned order. Sales orders dated this week are still satisfied from on-hand stock if available, but they don’t create new work orders. The reasoning: anything that close to today is execution territory, not planning. If you need to react to a same-day order, do it manually through the Work Orders page.

Forecast demand inside the fence is dropped entirely.

Planning-time fence (default 30 days)

Existing supply (POs, WOs) inside the next 30 days is held constant. The engine may emit EXPEDITE action messages — “your supply is too late, try to pull it in” — but it will not propose RESCHEDULE_EARLIER or RESCHEDULE_LATER against a supply record inside the fence. The reasoning: rescheduling something already in execution is more disruptive than the demand savings, so the engine should be quiet about it.

You can shrink either fence per run if you want a more aggressive plan. Most teams leave the defaults alone.

Converting plans

Once you’ve reviewed the Planned tab and decided which proposals to act on, click the Promote button on each row you want. There’s no target-type picker — the engine already knows what the row should become:

  • PLANNED_PO rows become a draft Purchase Requisition (one PR per Promote click, one line on it). The line copies the planned quantity, unit, and due-date hint; the suggested supplier on the plan resolves an estimated_price from supplier_products.last_purchase_price so the planner has a starting number to refine. The PR header is created with priority Normal — edit the PR afterwards if you want to bump it up.
  • PLANNED_WO rows become a DRAFT Work Order using the engine’s suggested_bom_id and suggested_routing_id. The WO is tagged with source = MRP so you can trace it back to this run.

You don’t pre-create the destination — the server creates the PR or WO in the same call and back-links the promoted plan to it. The plan row flips from OPEN to CONVERTED and the Document column on that row becomes a link straight to the new PR or WO.

Rows on the Planned tab are listed alphabetically by product name across all pages, so suggestions for the same item stay together as you scroll. Use the search box to jump to a specific product.

Promotion rules

A few details that aren’t obvious from the button:

  • One row, one downstream document. Promote is per-row today — each click creates exactly one PR (for PLANNED_PO) or one WO (for PLANNED_WO). Bulk promotion will land later if the workflow demands it; for now this keeps the action predictable.
  • PR priority is always Normal at creation. If you need a different priority, open the PR afterwards and change it on the header. Per-line urgency stays driven by the planned order’s due date regardless.
  • ACTION_MESSAGE rows aren’t promotable from here. They’re advisory — the engine is telling you to expedite, reschedule, or cancel an existing PO/WO, not to create a new one. Act on the underlying document directly, then mark the action message IGNORED if you want it off the list.
  • Partial-quantity conversion is not supported. A plan is converted in full or not at all. If a planned PO of 1000 units needs to be split between two suppliers, promote it once, then edit the resulting PR (delete the line or change its quantity); the next MRP run resurfaces the uncovered remainder.
  • Plans don’t carry priority into promotion. The Planned tab is sorted by LLC ascending then priority score ascending (lowest score = most urgent), but Promote does not enforce that ordering — you can promote a low-priority row before a high-priority one. The sort is a recommendation; the planner can override it using domain knowledge MRP doesn’t have (a flagship customer, a long-lead supplier).
  • Pegging is purely informational. Clicking why? on a row shows the demand records that drove the proposal but does not affect promotion mechanics — it’s there to help the planner decide whether the proposal makes sense.

A promotion is one-way — once a plan is CONVERTED, subsequent runs treat the linked WO/PR as supply (so duplicate plans don’t appear) and the planner can’t unconvert. To redo a promotion, cancel the downstream document first; the next MRP run will surface the demand again.

What about the proposals you don’t want?

Each OPEN row has an Ignore button in the Decide column (next to Promote, when promotion applies). Click it to drop the row to IGNORED. ACTION_MESSAGE rows only show Ignore — they’re advisory, so you act on the underlying PO/WO directly, then Ignore the advisory to clear it from the list.

For planned orders you’ve decided not to act on, the same Ignore action works. Be aware that the next run will likely propose them again — MRP is stateless across runs, so unless the underlying demand changes, the same plan will reappear. Use Ignore for one-off rejections, not as a way to permanently squash a recurring proposal.

EXPIRED is not a user action. The engine stamps it automatically on a previous run’s plan when a later run supersedes it. You’ll see EXPIRED rows when browsing historical runs, but no button on the Planned tab produces this state.

Common pitfalls

  • Forgetting to keep BOMs up to date. MRP only recurses through manufactured items that have an active BOM. If a part has no BOM, the engine treats it as purchased and emits PLANNED_PO even when the team intends to make it. Activate the BOM before running.
  • Running before the supply snapshot is settled. If you trigger MRP just before goods receipts complete, the in-transit stock won’t be counted yet and you’ll see false PLANNED_PO rows. Save the run for after end-of-day stock processing.
  • Ignoring action messages without reading them. EXPEDITE and CANCEL are advisory — but they’re advisory because the engine spotted a real misalignment between supply and demand. Always read the message before clicking Ignore.
  • Treating MRP output as gospel. MRP is a planner’s tool, not an autopilot. The engine has limited context (no supplier capacity, no machine breakdowns, no holiday schedules), and its proposals are starting points. Expect to ignore 5–10% of rows in any run.
  • Running too aggressively on the time fences. Shrinking the demand fence to 0 days will cause MRP to propose work orders for tomorrow’s sales orders — which is rarely realistic given lead times. Keep the fence at or above your shortest cycle time.