Functional Weave
Code in TypeScript

inventory.reorder-point

Reorder point: demand over the supplier lead time, rounded up to whole units, plus safety stock.

1.0.0 · published 2026-10-03 by charlie · Anterra

Pinned by 13 tests, run in TypeScript, Python and Rust.

What it does

The stock level at which to place a replenishment order:

reorder point = lead-time demand + safety stock lead-time demand = demand x leadTimeDays / demandPeriodDays, rounded up

For example

  • reorderPoint(70, 7, 10, 20) → 120 70 a week over a 10-day lead time, plus 20 safety stock
  • reorderPoint(1,000, 30, 7, 50) → 284 1000 a month over 7 days is 233.33, rounded up to 234, plus 50
  • reorderPoint(2, 7, 2, 0) → 1 a fraction under a half still rounds up: 2 a week over 2 days is 0.57, so 1

The function

The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.

export function reorderPoint(demand: number, demandPeriodDays: number, leadTimeDays: number, safetyStock: number): number
demandintunits used in one demand period, e.g. 1000 a month
demandPeriodDaysintdays in that period: 1, 7, 30, 365; lets demand per day be fractional without floats
leadTimeDaysintdays from placing an order to having the stock available
safetyStockintbuffer in units, e.g. from inventory.safety-stock
returnsintreorder when stock on hand plus on order falls to this many units

Your code names it in one line, in the file that uses it

import { reorderPoint } from "#fune/inventory.reorder-point@^1";
impl/typescript.ts · 23 lines · open · raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { roundDiv } from "./math_round_div.ts";  ← from math.round-div ^1.0.0 · built alongside by fune

function wholeUnits(name: string, value: number): void {
  if (!Number.isInteger(value) || value < 0) {
    throw new RangeError(`${name} must be a whole number, not negative, received ${value}`);
  }
}

/**
 * Lead-time demand, rounded up to whole units, plus safety stock.
 *
 * Rounding up rather than to nearest: 233.33 units of lead-time demand needs
 * 234 in hand, or the last third of a unit is a stock-out.
 */
export function reorderPoint(demand: number, demandPeriodDays: number, leadTimeDays: number, safetyStock: number): number {
  wholeUnits("demand", demand);
  wholeUnits("leadTimeDays", leadTimeDays);
  wholeUnits("safetyStock", safetyStock);
  if (!Number.isInteger(demandPeriodDays) || demandPeriodDays <= 0) {
    throw new RangeError(`demandPeriodDays must be a whole number of days, at least 1, received ${demandPeriodDays}`);
  }
  return roundDiv(demand * leadTimeDays, demandPeriodDays, "up") + safetyStock;
}

Install

fune build

With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and its 1 dependency, pins them in fune.lock, downloads only the TypeScript package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:

fune add inventory.reorder-point
Download for TypeScript inventory.reorder-point-1.0.0-typescript.fune · 5,694 bytes sha256 940ba6cae2009db3c00041d8d70c6df8dfc909a0617d898322a79b95a72126e5

The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./inventory.reorder-point-1.0.0-typescript.fune, or fetch it from a terminal with fune pull inventory.reorder-point@1.0.0:typescript.

The whole function, every language, is one file too: inventory.reorder-point-1.0.0.fune, 8,362 bytes, sha256 04b058af629a2ff2051b887513bb1b28d44cd180350dc6a5fb183a40448df0e0. It installs into a project of any language.

Customise it in your app

The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.

before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.

// fune: before inventory.reorder-point

after — your function gets the result and the arguments, and returns the final result.

// fune: after inventory.reorder-point

replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.

// fune: replace math.round-div in inventory.reorder-point

step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show inventory.reorder-point --steps.

// fune: step inventory.reorder-point after <n|label>

Tests

A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.

CaseArgumentsExpected
70 a week over a 10-day lead time, plus 20 safety stock 70, 7, 10, 20 → 120
1000 a month over 7 days is 233.33, rounded up to 234, plus 50 1,000, 30, 7, 50 → 284
a fraction under a half still rounds up: 2 a week over 2 days is 0.57, so 1 2, 7, 2, 0 → 1
an exact half rounds up: 15 per 2 days over 1 day is 7.5, so 8 15, 2, 1, 0 → 8
a yearly figure: 3650 a year over 14 days is exactly 140 3,650, 365, 14, 0 → 140
a daily rate: 10 a day over 5 days, no safety stock 10, 1, 5, 0 → 50
zero lead time leaves only the safety stock 1,000, 30, 0, 25 → 25
zero demand leaves only the safety stock 0, 30, 14, 25 → 25
a demand period of zero days is an error 1,000, 0, 7, 0 → error: demandPeriodDays must be a whole number of days, at least 1
negative demand is an error -10, 7, 7, 0 → error: demand must be a whole number
Show the other 3 tests
CaseArgumentsExpected
negative lead time is an error 10, 7, -1, 0 → error: leadTimeDays must be a whole number
negative safety stock is an error 10, 7, 7, -5 → error: safetyStock must be a whole number
a fractional lead time is an error: express it in the period's days 10, 7, 2.5, 0 → error: leadTimeDays must be a whole number

More from the author

Demand is given per period (units a week, a month, a year) with the period's length in days, so a daily rate of 33.3 units (1000 a month) needs no float: the division happens once, exactly, with `math.round-div`.

**Lead-time demand is rounded up**, never to nearest. 1000 a month over a 7-day lead time is 233.33 units; ordering at 233 plans to run out a third of a unit early. Safety stock is already whole units and is added unchanged.

Compare the result with stock on hand **plus stock already on order**, or an order placed yesterday triggers another today.

Zero lead time or zero demand leaves just the safety stock. Negative values and a demand period of zero days are errors.

Source: the standard textbook definition, e.g. APICS Dictionary ("order point") and Silver, Pyke and Thomas, *Inventory and Production Management in Supply Chains*, 4th ed., ch. 6.

Files

PathBytes
README.md1,082
impl/python.py1,066
impl/rust.rs1,475
impl/typescript.ts986
vectors.json1,600