Functional Weave
Code in TypeScript

Suites · inventory

Inventory and warehouse

Stock control and warehousing: reorder points, safety stock and order quantities, FIFO or weighted average valuation, ABC classes, cycle counts, expiry picking and pallets.

52 capabilities (22 core, 30 optional) and 10 gaps, in build order. Put each line in the file that calls it, then fune build.

1. Items and barcodes

Identify each stock item and check the codes scanned against it.

retail.sku-generate core
Builds a deterministic SKU code from a prefix and product attributes.
Build a deterministic stock-keeping unit code from a prefix and product attributes.
import { generateSku } from "#fune/retail.sku-generate@^1";
validation.gtin core
Checks an EAN/UPC/GTIN barcode number's check digit.
Check an EAN-8, UPC-A, EAN-13 or GTIN-14 barcode number against the GS1 mod-10 check digit.
import { validateGtin } from "#fune/validation.gtin@^1";
validation.gs1-check-digit optional
Computes or checks the check digit of any GS1 key, GLN and SSCC included.
Compute or check the GS1 mod-10 check digit of any GS1 key: GTIN, SSCC, GLN, GSRN, GDTI, GSIN and the rest.
import { gs1CheckDigit, isGs1CheckDigitValid } from "#fune/validation.gs1-check-digit@^1";
  • barcode-scanning Reading barcodes from a camera or scanner is device work and not in caps.
  • stock-ledger The stock movements ledger itself (receipts, issues, transfers, adjustments per location): Functional Weave values it, it does not store it.

2. Replenishment

Reorder at the right time in the right quantity.

inventory.reorder-point core
Reorder point: lead-time demand plus safety stock, in whole units.
Reorder point: demand over the supplier lead time, rounded up to whole units, plus safety stock.
import { reorderPoint } from "#fune/inventory.reorder-point@^1";
inventory.safety-stock core
Safety stock by service level and demand variability, or max minus average.
Safety stock in whole units, by service level and demand variability (z-score) or by max-minus-average.
import { safetyStock } from "#fune/inventory.safety-stock@^1";
inventory.eoq optional
Economic order quantity that minimises ordering plus holding cost.
Economic order quantity (Harris/Wilson): the order size that minimises ordering plus holding cost, in whole units.
import { economicOrderQuantity } from "#fune/inventory.eoq@^1";
stats.standard-deviation optional
Demand variability for the safety stock calculation.
Population or sample standard deviation of a list of numbers, two-pass, rounded to stated decimals.
import { standardDeviation } from "#fune/stats.standard-deviation@^2";
  • demand-forecasting Forecasting demand (seasonality, trends) beyond averages is not in caps.
  • purchase-orders Raising and receiving purchase orders with suppliers.

3. Stock valuation

Value stock and cost of sales for the accounts.

inventory.valuation-fifo core
FIFO stock valuation and cost of sales from a movement ledger.
FIFO stock valuation and cost of sales from a stock movement ledger, in exact money.
import { fifoValuation } from "#fune/inventory.valuation-fifo@^1";
inventory.valuation-weighted-average optional
Perpetual weighted average cost and cost of sales.
Perpetual weighted average cost: stock value and cost of sales from a movement ledger, rounding once per issue.
import { weightedAverageValuation } from "#fune/inventory.valuation-weighted-average@^1";
inventory.stock-turnover optional
Stock turnover, days inventory outstanding and days of cover.
Stock turnover ratio, days inventory outstanding and days of cover for a period, computed exactly.
import { stockTurnover } from "#fune/inventory.stock-turnover@^1";

4. Counting and analysis

Know which items matter most and count them on a schedule.

inventory.abc-classification optional
ABC classes by annual consumption value, with configurable cut-offs.
ABC classes by annual consumption value (Pareto), with configurable cut-offs such as 80/15/5 and deterministic ties.
import { abcClassification } from "#fune/inventory.abc-classification@^1";
inventory.cycle-count-schedule optional
Spreads each item's counts over a period's working days by ABC class.
Cycle count schedule: spread each item's counts evenly over the working days of a period, by ABC class frequency.
import { cycleCountSchedule } from "#fune/inventory.cycle-count-schedule@^1";

5. Warehouse operations

Pick, pack and palletise.

inventory.expiry-fefo optional
First-expiry-first-out pick list, skipping expired and short-dated stock.
First-expiry-first-out pick list for a quantity across batches, skipping expired and short-dated stock.
import { fefoPick } from "#fune/inventory.expiry-fefo@^1";
inventory.pallet-fit optional
Cartons per layer and per pallet within height and weight limits.
How many cartons fit per layer and per pallet (UK 1200x1000 or EUR 1200x800), within a height and weight limit.
import { palletFit } from "#fune/inventory.pallet-fit@^1";
logistics.sscc optional
The SSCC number for a pallet label.
Build an 18-digit SSCC pallet label number from extension digit, GS1 company prefix and serial, with its check digit.
import { buildSscc } from "#fune/logistics.sscc@^1";
  • bin-locations Bin and location management and pick routing in the warehouse.

6. Amounts from money-basics

Hold money as currency-tagged integer minor units and do plain arithmetic on it safely.

money.amount core
The Money type ({ minor, currency }) every other money capability takes and returns.
A currency-tagged monetary amount held in integer minor units.
import { money } from "#fune/money.amount@^1";
money.add core
Adds and subtracts amounts, refusing to mix currencies by accident.
Add, subtract and negate monetary amounts, refusing to mix currencies.
import { addMoney } from "#fune/money.add@^1";
money.sum core
Totals a list of amounts, with an explicit currency for an empty list.
Total a list of monetary amounts, with an explicit currency for the empty case.
import { sumMoney } from "#fune/money.sum@^1";
money.compare core
Compares, mins, maxes and clamps amounts of one currency without unwrapping them.
Compare two amounts of one currency, refusing to compare across currencies; min, max and clamp too.
import { compareMoney, minMoney, maxMoney, clampMoney } from "#fune/money.compare@^2";

7. Rates and rounding from money-basics

Apply percentages and divide amounts with the rounding stated, never a float.

money.apply-rate core
Applies a basis-point rate (VAT, discount, commission) to an amount with explicit rounding.
Apply a rate expressed in basis points to a monetary amount, with an explicit rounding mode.
import { applyRate } from "#fune/money.apply-rate@^1";
math.round-div core
Integer division with a named rounding mode, for any money maths the others do not cover.
Integer division with an explicit rounding mode, for money arithmetic that must not drift.
import { roundDiv } from "#fune/math.round-div@^1";
math.basis-points optional
Converts "12.5%" to and from basis points exactly, for rates typed in by users.
Convert a rate between percent, basis points and a plain ratio exactly, as decimal text or integer basis points.
import { convertRate, toBasisPoints, fromBasisPoints } from "#fune/math.basis-points@^2";
math.percent-change optional
Percentage change between two figures in basis points, for "up 4.2% on last month".
Percentage change from one value to another, in basis points, with an explicit rounding mode.
import { percentChange } from "#fune/math.percent-change@^1";
math.rational optional
Exact fractions for rates that must not drift across many steps.
Exact fraction arithmetic, always reduced, for rates and ratios that must not drift.
import { calculateRational, rational, addRational, subtractRational, multiplyRational, divideRational, compareRational, rationalToInteger } from "#fune/math.rational@^2";
math.round-div-big optional
Rounded division on numbers past 2^53, for very large totals.
Divide two integers of any size with an explicit rounding mode, on decimal strings, for exact money sums past 2^53.
import { roundDivBig } from "#fune/math.round-div-big@^1";

8. Splitting from money-basics

Divide an amount between people, periods or lines so the parts add back to the whole.

money.allocate core
Splits an amount by ratios (60/40, by quantity) without losing or inventing a penny.
Split an amount across ratios without losing or inventing a single minor unit.
import { allocate } from "#fune/money.allocate@^1";
money.split-even optional
Splits an amount into n near-equal parts that sum exactly, for instalments or shares.
Split an amount into n near-equal parts that add back up to exactly the whole.
import { splitEven } from "#fune/money.split-even@^1";

9. Display and input from money-basics

Show amounts to people and read what they type.

money.format core
Renders an amount as text with the currency's real number of decimal places.
Render a monetary amount as text, using the currency's real minor-unit precision.
import { formatMoney } from "#fune/money.format@^1";
money.parse optional
Reads "£1,234.50" or "1.234,50 €" typed by a user into minor units, strictly.
Parse "£1,234.50" or "1.234,50 €" into integer minor units, strictly: no guessing, no rounding.
import { parseMoney } from "#fune/money.parse@^1";
money.currency-digits optional
How many decimal places each ISO 4217 currency has, for input boxes and validation.
How many decimal places a currency's minor unit has, from the ISO 4217 list.
import { currencyDigits } from "#fune/money.currency-digits@^1";

10. Other currencies from money-basics

Convert between currencies at a rate you hold.

money.convert optional
Converts an amount at a supplied exact rate with explicit rounding.
Convert an amount to another currency at a supplied exact rate, with an explicit rounding mode.
import { convertMoney } from "#fune/money.convert@^1";

11. Calendar arithmetic from dates-basics

Move and measure ISO dates exactly, with no date library and no clock reads.

dates.add-days core
Shifts a date by whole days, for due dates and windows.
Shift an ISO date by a whole number of days, forwards or backwards, with exact calendar arithmetic.
import { addDays } from "#fune/dates.add-days@^1";
dates.add-months core
Adds months clamping to month end, so 31 January plus a month is the end of February.
Add calendar months to an ISO date, clamping to the month end: 31 Jan + 1 month is 28 or 29 Feb.
import { addMonths } from "#fune/dates.add-months@^1";
dates.days-between core
Signed calendar days between two dates, for ages of invoices and stays.
Calendar days from one ISO date to another, signed: negative when the second date is earlier.
import { daysBetween } from "#fune/dates.days-between@^1";
dates.month-boundaries core
First and last day of a date's month, for monthly periods and statements.
The first day, last day and length of the calendar month containing a date.
import { monthBoundaries } from "#fune/dates.month-boundaries@^1";
dates.day-of-week core
ISO weekday of a date, for weekend rules and opening hours.
The ISO 8601 day of the week for a date: 1 is Monday through 7 is Sunday.
import { dayOfWeek } from "#fune/dates.day-of-week@^1";
dates.is-leap-year optional
Whether a year is a leap year, for 365/366-day calculations.
Whether a year is a Gregorian leap year: every 4th, except centuries, except every 400th.
import { isLeapYear } from "#fune/dates.is-leap-year@^1";
dates.overlap optional
Days two date ranges share, for prorating and clash checks.
Days two date ranges have in common, with the end of each range stated as inclusive or exclusive.
import { overlapDays } from "#fune/dates.overlap@^1";
dates.age optional
Age in whole years on a date, handling 29 February birthdays.
Age in whole years on a given date, with 29 February birthdays ageing on 1 March in other years.
import { age } from "#fune/dates.age@^1";

12. Periods and reporting dates from dates-basics

Put dates into the periods reports are grouped by.

dates.quarter core
Calendar and fiscal quarter of a date for any year-start month.
Calendar quarter and fiscal quarter of a date, with the fiscal year and quarter date ranges, for any start month.
import { quarter } from "#fune/dates.quarter@^1";
dates.week-number optional
ISO week number and week-year, for weekly reports.
ISO 8601 week number and week-numbering year of a date: weeks start Monday, week 1 holds the first Thursday.
import { weekNumber } from "#fune/dates.week-number@^1";
dates.day-count-fraction optional
Year fraction by ACT/365F, ACT/360, 30/360 or ACT/ACT, for interest periods.
Year fraction between two dates by ACT/365F, ACT/360, 30/360, 30E/360 or ACT/ACT ISDA, as an exact fraction.
import { dayCountFraction } from "#fune/dates.day-count-fraction@^1";

13. Working days and holidays from dates-basics

Skip weekends and public holidays when counting or adding days.

dates.bank-holidays core
UK bank holidays for a region and year, as gov.uk publishes them.
UK bank and public holidays for a region and year, as published by gov.uk, including one-off holidays.
import { bankHolidays } from "#fune/dates.bank-holidays@^1";
dates.add-business-days core
Moves a date by working days, skipping weekends and a holiday list.
Move a date forward or back by working days, skipping weekends and a caller-supplied holiday list.
import { addBusinessDays } from "#fune/dates.add-business-days@^1";
dates.business-days-between core
Working days between two dates, for SLAs and notice periods.
Working days between two dates, excluding weekends and a caller-supplied holiday list.
import { businessDaysBetween } from "#fune/dates.business-days-between@^1";

14. Repeating schedules from dates-basics

Generate the dates of something that repeats.

dates.recurrence optional
The next n dates of a rule: monthly on the 31st, last Friday, every two weeks.
The next n dates of a repeating rule: monthly on a day, last weekday of the month, every k days or weeks, yearly.
import { recurrence } from "#fune/dates.recurrence@^1";

15. Times and durations from dates-basics

Work with times of day, durations and timestamps.

time.duration optional
Parses and adds durations like 1h30m in whole minutes, and formats the total.
Parse durations written as 1h30m, 90m or 01:30, add them up in whole minutes, and format the total.
import { duration } from "#fune/time.duration@^1";
time.minutes-between optional
Minutes between two local date-times, across midnight.
Minutes from one local date and time to another, correct across midnight; wall-clock time, no time zones.
import { minutesBetween } from "#fune/time.minutes-between@^1";
time.round-to-increment optional
Rounds minutes to a billing increment such as 6 or 15.
Round a duration in minutes to a billing increment such as 6 or 15 minutes, up, down or to nearest.
import { roundToIncrement } from "#fune/time.round-to-increment@^1";
time.iso-to-unix optional
RFC 3339 timestamps to Unix seconds.
An RFC 3339 timestamp with a Z or offset, such as 2026-09-26T13:00:00+01:00, to Unix time in whole seconds.
import { isoToUnix } from "#fune/time.iso-to-unix@^1";
time.unix-to-iso optional
Unix seconds to an ISO 8601 UTC timestamp.
Unix time in seconds to an ISO 8601 UTC timestamp such as 2026-09-26T12:00:00Z, for years 0001 to 9999.
import { unixToIso } from "#fune/time.unix-to-iso@^1";
time.countdown optional
Seconds left until a moment and a 4:05 style timer text.
Seconds left until a Unix time, whether it has passed, and a timer text like 4:05, for tokens or retry waits.
import { countdown } from "#fune/time.countdown@^1";

Gaps

What this kind of app usually needs that Functional Weave does not have yet: write these yourself, or use a service.

  • fx-rate-source Where exchange rates come from: money.convert takes a rate you supply; fetching and storing daily rates (ECB, a provider's API) is yours.
  • money-storage Storing amounts: the database columns (integer minor units plus a currency code) and their migrations are yours.
  • time-zones Time zones and daylight saving: caps works in ISO dates, UTC and wall-clock times; converting with the IANA tz rules is yours.
  • locale-formatting Showing dates in a user's language and format ("3 octobre 2026"): caps returns ISO dates, not localised text.
  • non-uk-holidays Public holidays outside the UK: dates.bank-holidays is UK only; pass another country's list to the working-day functions yourself.