How it works

A walk through every number the planner produces — what it means, where it comes from, and a worked example for each. Live figures below (service level, cover, container size, lead times) are read from Settings, so the examples reflect your current configuration. The through-line: because a container takes about 2–3 months to make and ship, you can't reorder faster than the boat, so the plan holds stock in three places at once — in the pipeline (being made / on the water), working stock on the shelf, and a safety reserve beneath it.

Figures here reflect your current Settings. The Planning horizon reflects the last planner run — so after you change a setting, re-run the planner before the two agree. (Right now: service level 92%, safety floor on, seasonality on.)
1 · Previous 12 months demand

The starting point: what actually sold

Everything begins with real sales history. For each product the planner totals the units sold over the trailing 12 full months (the in-progress month is excluded — it would drag the average down) and divides by the number of months in that window to get an average per-month run-rate. (A product launched partway through the window divides by the months it actually has, not 12, so a young SKU isn't understated.) Three rules make the figure honest:

  • Order date, not invoice date. Demand is counted when the customer placed the order — that's when they wanted the stock. It won't tie out to an accounting "units shipped" report, and that's deliberate: the forecast's job is to predict demand, not revenue.
  • Packs become loose units. A sale of one "SL195-STD-GRY-PK (112)" counts as 112 sleepers, so pack and loose sales of the same product add up on one ruler.
  • Cancelled / parked orders are dropped — drafts and credits aren't demand.
Worked example
SL195-STD-GRY averaged 5,582/month across the 12 months to Jun 26 (≈66,984 sleepers total = 5,582 × 12) — the figure shown as avg 5,582/mo on the Planning horizon. Nothing is forecast yet; this is just the measured baseline.
2 · Sales targets

Growing the baseline on purpose

A run-rate only tells you the past. To plan for growth you set a target on the Sales targets page — an annual growth percentage. The planner multiplies the baseline by it. Two design choices matter for a detail reader:

  • Targets peg to a FIXED baseline — the 12 months ending Jun 26 — not a rolling average. If you grew off a rolling window, this year's achieved growth would fold back into next month's base and compound on itself. Pegging to a fixed year means "the Jun 26 average, plus your growth %" stays exactly that all year. You bump the baseline deliberately each new financial year.
  • Product beats category. A target can be set on a whole product group or on one product; the product-level one wins. A product with no target simply follows its rolling 12-month run-rate.
Worked example
SL195's baseline is 5,582/mo. With a +50% target: 5,582 × 1.50 = 8,373/month — shown as +50% → plans 8,373. That 8,373 is the level the plan provisions for, before seasonality spreads it across the year.
3 · Seasonality

Spreading the level across the year — without changing the total

The target gives a level (a per-month average). Seasonality decides how that level is distributed month to month. The engine builds 12 multipliers (one per calendar month) from history, and — this is the key property — normalises them to average exactly 1.0. A summer month might be 1.4, a winter month 0.6, but the twelve always average to 1.

The consequence surprises people: the annual total is identical whether seasonality is on or off. Over a full year you touch every calendar month once, so the multipliers sum to 12 either way. Seasonality only moves demand between months; it never adds or removes any. Turning it off just sets every multiplier to 1.0 (a flat line).

Worked example
Say a product plans at 100/month (1,200/year).
MonthMultiplierFlat (off)Seasonal (on)
Jan (peak)1.40100140
Jun (trough)0.6010060
…other 10 months…avg 1.00100 eachvaries
Year totalsum = 12.01,2001,200
Same 1,200 either way. If you sum a whole year of the Sales value row and it doesn't change when you toggle seasonality — that's correct, not a bug.

Seasonality is currently ON. The shape is fitted from the last 24 months of history (classical decomposition with a 24-month+ window; a shorter window uses a year-over-year de-trend). One caveat the run enforces: a lookback of 12 months fits 12 multipliers on 12 data points and absorbs all variation, which collapses the safety floor to ~0 — so keep the lookback well above 12, or the run flags it. Seasonality also quietly changes the floor itself; see Traps & gotchas.

4 · Planning horizon

A month-by-month simulation of your stock

The Planning horizon is a 12-month forward walk of each product's stock — no black-box solver, just arithmetic you can redo by hand. Every month, in this exact order:

opening − demand + arriving = closing (last month's close) (forecast) (POs + planned orders)

Closing becomes next month's opening, and the walk repeats. Expand any product to see those four rows, then a combined Working cover & safety floor line (sections 5–6) — the level orders aim to top stock back up to. Only opening, demand and arriving move closing; the floor/cover line beneath is a target to compare against, not part of the subtraction.

Worked example
A month opening at 11,528 with 6,283 arriving and 9,904 forecast demand closes at 11,528 + 6,283 − 9,904 = 7,907.
5 · Working cover

The stock you actually trade on

Working cover is 1 month of demand (Settings → Target cover) held above the safety floor. Crucially it's forward-looking: the cover for a month is the next month's forecast demand — the amount you must have on the shelf now to trade through until the next boat lands. It's the day-to-day stock a shipment-a-month replenishes as it sells. Raise it to 2–3 months when cash allows a bigger cushion; drop it to run leaner.

Worked example
At 1 month cover, a product whose next month forecasts 8,000 units carries 8,000 units of working cover on top of its floor going into that month. Because it uses next month's demand, the cover figure rises heading into a busy season — by design (see Traps & gotchas).
6 · Safety floor & service level

The reserve beneath the working stock

The safety floor is the buffer kept under your working stock for the unexpected — a month that outsells the forecast, or a container that runs late — while you wait 2–3 months on the next boat. It is not a fixed number of months; it's sized statistically per product from how choppy that product's own demand is:

safety floor = z(service level) × σ × √(lead time in months)

σ (sigma) is the product's month-to-month demand scatter around its seasonal expectation — a steady seller has a small σ and a small floor; a lumpy one needs more. √lead time scales it up because a longer pipeline exposes you to more uncertain months before relief arrives. And z(service level) converts your chosen service level into a number of standard deviations.

What the % means. A service level of 92% means the floor is sized so that demand over the lead time exceeds what you've stocked only 8% of the time — i.e. you expect to cover demand from stock in about 92 of every 100 replenishment cycles, and dip into (or through) the reserve in the other 8. Chasing the last few percent gets expensive fast because z climbs steeply near 100%:

Service levelzMeaningFloor in the example →
90%1.28~10 in 100 cycles dip into the reserve266 units
92% ← yours1.41~8 in 100 cycles dip into the reserve292 units
95%1.64~5 in 100 cycles dip into the reserve342 units
97.5%1.96~2.5 in 100 cycles dip into the reserve407 units
99%2.33~1 in 100 cycles dip into the reserve484 units
Worked example
Take a product whose demand scatters by σ = 120 units/month around its seasonal expectation, made on a 3-month lead time. The floor is z × 120 × √3 (√31.73). At your 92% service level that's 1.41 × 120 × 1.73 = 292 units. Push to 99% and the same product needs 484; drop to 90% and it needs only 266 — same product, same demand, just a different appetite for risk.

The floor can be switched off entirely (Settings → Safety floor) — it is currently ON. Off means the floor is 0 for every product and the plan runs on working cover alone: a deliberate, lower-capital stance where you'd rather hold ~1 month of stock and no reserve underneath.

7 · The ordering system

Little and often — top back up to floor + cover

Each month the planner reviews every product and, if needed, raises an order to bring it back up to an order-up-to level:

order-up-to = safety floor + demand over the cover window (forward) need = order-up-to − (stock already on hand + inbound POs)

The "cover window" is normally 1 month of the coming months' demand — but it stretches automatically when the next shipment can't land next month (e.g. a Chinese New Year shutdown blocks a landing), so the last order before the factories close is sized to bridge the whole gap. This is a flow policy: a shipment lands roughly every month, so on-hand hovers near floor + cover rather than sawtoothing from full to floor. A few refinements:

  • MOQ / run rounding. The raw need is rounded up to the mould's minimum run (in linear metres for profile moulds, allocated back to products by length). Shared moulds split a run across their products by relative need.
  • Emergency orders. A product already below its floor with nothing inbound in time will order even though it lands late — the breach months stay visible rather than being hidden.
  • Inbound POs are never double-covered. Anything already on the water inside the cover window is subtracted before sizing.
Worked example
A product with a 292-unit floor, forecasting 8,000 next month at 1 month cover, has an order-up-to of ~8,292. If it's projected to have 6,000 on hand when the order lands (and no inbound POs), the need is ~2,292 units — then rounded up to the next whole run.
8 · What goes in a container

Volume-first packing, and a freight-vs-holding trade-off

Orders to the same manufacturer are consolidated into 40′ HC container loads. There is no pallet abstraction and no fill target — capacity is measured two ways and the load takes as many containers as the tighter one demands:

containers = max( ceil(total volume / 72 m³) , ceil(total weight / 27.5 t) )

Volume is the primary constraint — real loads in the PO history cube out (~69–76 m³ of goods) well before they hit the ~27.5 t weight cap. Each product contributes its own packed volume and weight per unit, resolved from ERP data through a fallback cascade: a "-PK (n)" bundle ÷ n is best (the real bundle as loaded); failing that, box dimensions W×H×D × a nesting factor (~0.75, because C-section posts stack inside each other); failing that, weight ÷ group density. A sanity check rejects any dimension whose implied density is more than 4× off the group norm (catches fat-fingered ERP values).

Consolidation is an economic decision, not bin-packing. Freight is charged per container with no bulk discount, so pulling a future order forward only pays when it genuinely eliminates a part-empty container:

merge a future order ⇔ freight saved > holding cost freight saved = (containers eliminated) × (freight + $550 unpack fee) holding cost = months pulled forward × (order capital × 20%/yr + volume × $10/m³/mo)

Two full monthly orders that each fill their own container stay staggered — merging saves no freight and only adds holding cost. Fill rate (by volume and by weight) is reported as an output, never chased as a goal.

Worked example
A 40′ HC holds ~72 m³ usable. SL195 sleepers pack at roughly 0.020 m³ each, so a full container is ~3,600 sleepers weighing ~19 t — under the 27.5 t cap, so it cubes out before it weighs out. Bulky posts with no pack code take their volume from box dims × 0.75, which is why a container of posts holds fewer units by count but fills the same m³.
9 · Data accuracy

Garbage in, garbage out — and the one error the tool can't catch

Every number on every page is only as good as the ERP data behind it. The planner fails loud on missing data — no cost, an unconvertible currency, no sales history, a product with no mould — it flags these rather than guessing. But it cannot catch data that is wrong but plausible. By far the most important of those is the container landing date.

Why the landing date matters more than almost anything. For every open purchase order the plan reads the ERP's expected delivery date and treats it as the month the stock lands. That single date drives three things:

  • which month a product's arriving row lights up,
  • whether the product looks like it's breaching its floor in the meantime, and
  • the subtle one — whether a new order is raised at all, because stock already inbound inside the cover window is subtracted from what needs ordering.

So a landing date that's a month too optimistic can make the plan skip an order it should place — it believes relief is arriving sooner than it is — and hide a stockout that's actually coming. When the ERP has no delivery date the plan derives one (order date + manufacturing + shipping weeks + any CNY push) — a sound estimate, but only an estimate. Keep delivery dates current, and update them the moment a shipment slips. You can also correct a landing date per-PO on the Purchase orders page.

Worked example
A container of 3,000 sleepers is dated to land in March but really arrives in May. Through March and April the plan counts 3,000 units it doesn't have, so it under-orders — and because those units sit "inbound" inside the cover window, it may skip the top-up order entirely. The shortfall is invisible until the stock simply doesn't show up. Fix the date (or override it) and the plan re-plans around May on the next run.

The rest of the inputs, by impact:

  • Stock on hand — the opening balance the whole 12-month walk builds on; a miscount cascades through every month.
  • Sales by order date — the forecast is only as honest as the history; miscoded or missing orders move the baseline.
  • Lead times (manufacturing + shipping weeks) — size the safety floor (√lead) and the derived landing date when the ERP has none; optimistic lead times give thin floors and orders placed too late.
  • Pack quantities & product config — a wrong pack size double- or half-counts demand; wrong dimensions mis-pack containers.

The tool will tell you when a number is missing. Only you can tell it when a number is wrong.

10 · Traps & gotchas

Things that surprise people

The non-obvious behaviours worth internalising before you trust — or debug — a number.

a · Seasonality moves cover and the floor in opposite directions

You'd expect seasonality to inflate everything. It doesn't. Working cover uses the coming month's demand, so it rises heading into a busy month and falls in the quiet season — the combined floor + cover target swings with it (for a product it might run 7k in the trough and 20k into the peak). But the safety floor moves the other way: it's sized on demand scatter around the seasonal expectation, so once seasonality explains the swing, σ shrinks and the floor drops. Turn seasonality off and floors go up (the swing is now counted as volatility). So seasonality on = higher peak cover, lower floor. Neither is a bug.

b · Your capital peaks BEFORE the busy season

Because cover is forward-looking and the plan stock-builds ahead of the peak, the Capital out row bulges in the months before your season, not during it. That's correct — you're buying the stock you'll sell — but if working capital is tight, this is the cash-flow spike to plan for. It's the flip side of "a month of cover looks high in the pre-peak months."

c · The target is what orders AIM for, not a guarantee

The floor + cover line is the level orders top up to. Actual closing sawtooths around it — above just after a container lands, drifting down before the next — because shipments arrive in whole-container lots on a 2–3 months lead time, not in smooth monthly trickles. A closing below the target line isn't a breach; only closing below the safety floor is. Don't read the target as a line stock should sit exactly on.

d · A 12-month seasonal lookback silently kills the floor

Fitting 12 seasonal multipliers on exactly 12 data points absorbs every wiggle, so residual σ → 0 and the safety floor collapses to ~0. Keep the seasonal lookback comfortably above 12 months (the default is 24); the run raises a flag if it's too short, but it's easy to miss.

e · Settings change instantly; the plan doesn't

Saving a setting stores it immediately, but every product-level number (forecasts, floors, orders, this page's live figures vs the horizon grid) only recomputes on the next planner run. If this page says the floor is off but the horizon still shows floors, that just means the plan hasn't been re-run since you toggled it.

f · Annual totals ignore seasonality

Because the multipliers average to 1.0, toggling seasonality reshuffles demand between months but leaves any full-year total (e.g. a year of the Sales value row) unchanged. Same total, different monthly spread — expected, not a fault.

Every value the engine uses is live config on the Settings page — no business number is hard-coded. Change one, re-run the planner, and every figure above and across the app moves with it.