Suites · logistics
Logistics, shipping and freight
Ship goods: chargeable weight, freight and delivery rates, delivery dates, containers and pallets, labels, route distance, Incoterms, customs duty and demurrage.
52 capabilities (22 core, 30 optional) and 12 gaps, in build order. Put each line in the file that calls it, then fune build.
Customs duty and import VAT figures are estimates from the rates you supply; the declaration and the tariff classification are the importer's responsibility.
TypeScript Python Rust
1. Rating and quoting
Work out what a shipment costs to send.
retail.volumetric-weight core
Chargeable weight: the greater of actual and volumetric weight, rounded up to a billing step.
Chargeable parcel weight: the greater of actual and volumetric (dimensional) weight, rounded up to a billing step.
fune!(retail.volumetric-weight@^1 ); // then call chargeable_weight(…)
1.0.0
logistics.freight-rate core
Freight charge from a carrier's dated weight-break tariff for a zone, with the fuel surcharge.
Freight charge from a carrier's dated weight-break tariff for a zone, with the fuel surcharge in force on the ship date.
fune!(logistics.freight-rate@^1 ); // then call freight_rate(…)
1.0.1
retail.shipping-rate optional
Parcel delivery charge from a rate table by service, zone and weight, with a free-over threshold.
Delivery charge from a dated rate table by service, zone, chargeable weight and size, with a free-over threshold.
fune!(retail.shipping-rate@^1 ); // then call shipping_rate(…)
1.0.1
units.convert optional
Converts kg, lb, cm and inches exactly, for shippers' mixed units.
Convert length, mass, area, volume, temperature and energy between units, exactly, as decimal text.
fune!(units.convert@^1 ); // then call convert_units(…)
1.0.0
carrier-apis Live rates, booking and labels from carriers' APIs (DHL, UPS, Royal Mail, DPD) are network work and not in caps.
2. Dispatch and delivery dates
Promise delivery dates the operation can keep.
logistics.delivery-date core
Dispatch and delivery dates from order time, cut-off, service days, weekends and UK bank holidays.
Estimated dispatch and delivery dates from order time, dispatch cut-off, service days, weekends and UK bank holidays.
fune!(logistics.delivery-date@^1 ); // then call delivery_date(…)
1.0.0
tracking Tracking events and proof of delivery from carriers, and notifying customers.
3. Loads and labels
Plan how goods are packed and label them.
logistics.sscc core
Builds an 18-digit SSCC pallet number with its check digit for GS1 labels.
Build an 18-digit SSCC pallet label number from extension digit, GS1 company prefix and serial, with its check digit.
fune!(logistics.sscc@^1 ); // then call build_sscc(…)
1.1.0
logistics.container-fit optional
How many cartons fit a 20ft, 40ft or high-cube container or a trailer, by space and weight.
How many identical cartons fit in a 20ft, 40ft or 40ft high-cube container or a 13.6 m trailer, by space and by weight.
fune!(logistics.container-fit@^1 ); // then call container_fit(…)
1.0.0
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.
fune!(inventory.pallet-fit@^1 ); // then call pallet_fit(…)
1.0.0
validation.gtin optional
Checks the barcode numbers on cartons and items.
Check an EAN-8, UPC-A, EAN-13 or GTIN-14 barcode number against the GS1 mod-10 check digit.
fune!(validation.gtin@^1 ); // then call validate_gtin(…)
1.1.0
label-printing Rendering the label itself (GS1-128 barcodes, ZPL for thermal printers) is yours.
4. Routes
Measure the distance of a planned run.
logistics.route-distance optional
Total great-circle distance of an ordered list of stops, leg by leg.
Total great-circle distance of an ordered list of stops, leg by leg, optionally back to the start, to the millimetre.
fune!(logistics.route-distance@^1 ); // then call route_distance(…)
1.0.0
geo.distance optional
Great-circle distance between two points.
Great-circle distance in metres between two latitude/longitude points (haversine), to the millimetre.
fune!(geo.distance@^2 ); // then call distance(…)
2.0.0
route-optimisation Ordering stops to minimise distance or meet time windows, and road distances rather than straight lines.geocoding Turning addresses into coordinates is network work and not in caps.
5. International trade
Know who pays for what and what an import will cost at the border.
logistics.incoterm-responsibility core
Who pays which costs and where risk passes under an Incoterms 2020 rule.
Who pays which costs and where risk passes under an Incoterms 2020 rule, from EXW to DDP, FOB and CIF.
fune!(logistics.incoterm-responsibility@^1 ); // then call incoterm_responsibility(…)
1.0.1
logistics.duty-estimate optional Unreviewed
Estimates import duty and import VAT on a CIF customs value at the duty rate you supply.
Estimate import duty and import VAT on goods: customs value on a CIF basis, the caller's duty rate, VAT on top.
fune!(logistics.duty-estimate@^1 ); // then call duty_estimate(…)
1.0.1
logistics.hs-code-validate optional
Checks the format of an HS, CN or UK commodity code.
Check the format of a commodity code: 6-digit HS, 8-digit CN or 10-digit TARIC/UK code, with chapter and heading checks.
fune!(logistics.hs-code-validate@^1 ); // then call validate_hs_code(…)
1.0.0
money.convert optional
Converts invoice values at the customs exchange rate you hold.
Convert an amount to another currency at a supplied exact rate, with an explicit rounding mode.
fune!(money.convert@^1 ); // then call convert_money(…)
1.0.0
tariff-lookup The duty rate for a commodity code (UK Trade Tariff, TARIC): you supply it; caps does not hold the tariff.customs-declarations Filing declarations (CDS, ENS) with customs is not in caps.
6. Port charges
Charge containers kept beyond their free days.
logistics.demurrage optional
Demurrage or detention charge: free days, then a tiered daily rate.
Demurrage or detention charge for a container: free days, then a tiered daily rate from the carrier's tariff.
fune!(logistics.demurrage@^1 ); // then call demurrage(…)
1.0.0
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.
fune!(money.amount@^1 ); // then call money(…)
1.0.2
money.add core
Adds and subtracts amounts, refusing to mix currencies by accident.
Add, subtract and negate monetary amounts, refusing to mix currencies.
fune!(money.add@^1 ); // then call add_money(…)
1.0.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.
fune!(money.sum@^1 ); // then call sum_money(…)
1.0.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.
fune!(money.compare@^2 ); // then call compare_money, min_money, max_money, clamp_money
2.0.04 functions
8. 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.
fune!(money.apply-rate@^1 ); // then call apply_rate(…)
1.0.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.
fune!(math.round-div@^1 ); // then call round_div(…)
1.0.0
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.
fune!(math.basis-points@^2 ); // then call convert_rate, to_basis_points, from_basis_points
2.0.03 functions
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.
fune!(math.percent-change@^1 ); // then call percent_change(…)
1.0.0
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.
fune!(math.rational@^2 ); // then call calculate_rational, rational, add_rational, subtract_rational, multiply_rational, divide_rational, compare_rational, rational_to_integer
2.0.08 functions
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.
fune!(math.round-div-big@^1 ); // then call round_div_big(…)
1.0.0
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.
fune!(money.allocate@^1 ); // then call allocate(…)
1.0.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.
fune!(money.split-even@^1 ); // then call split_even(…)
1.0.0
10. 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.
fune!(money.format@^1 ); // then call format_money(…)
1.0.0
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.
fune!(money.parse@^1 ); // then call parse_money(…)
1.0.0
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.
fune!(money.currency-digits@^1 ); // then call currency_digits(…)
1.0.0
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.
fune!(dates.add-days@^1 ); // then call add_days(…)
1.0.0
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.
fune!(dates.add-months@^1 ); // then call add_months(…)
1.0.0
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.
fune!(dates.days-between@^1 ); // then call days_between(…)
1.0.0
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.
fune!(dates.month-boundaries@^1 ); // then call month_boundaries(…)
1.0.0
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.
fune!(dates.day-of-week@^1 ); // then call day_of_week(…)
1.0.0
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.
fune!(dates.is-leap-year@^1 ); // then call is_leap_year(…)
1.0.0
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.
fune!(dates.overlap@^1 ); // then call overlap_days(…)
1.0.0
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.
fune!(dates.age@^1 ); // then call age(…)
1.0.0
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.
fune!(dates.quarter@^1 ); // then call quarter(…)
1.0.0
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.
fune!(dates.week-number@^1 ); // then call week_number(…)
1.0.0
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.
fune!(dates.day-count-fraction@^1 ); // then call day_count_fraction(…)
1.0.0
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.
fune!(dates.bank-holidays@^1 ); // then call bank_holidays(…)
1.0.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.
fune!(dates.add-business-days@^1 ); // then call add_business_days(…)
1.0.0
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.
fune!(dates.business-days-between@^1 ); // then call business_days_between(…)
1.0.0
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.
fune!(dates.recurrence@^1 ); // then call recurrence(…)
1.0.0
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.
fune!(time.duration@^1 ); // then call duration(…)
1.0.0
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.
fune!(time.minutes-between@^1 ); // then call minutes_between(…)
1.0.0
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.
fune!(time.round-to-increment@^1 ); // then call round_to_increment(…)
1.0.0
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.
fune!(time.iso-to-unix@^1 ); // then call iso_to_unix(…)
1.0.0
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.
fune!(time.unix-to-iso@^1 ); // then call unix_to_iso(…)
1.0.0
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.
fune!(time.countdown@^1 ); // then call countdown(…)
1.0.0
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.