Functional Weave
Code in Rust

Suites · insurance

Insurance

General insurance from quote to claim: rating factors, IPT, instalments, mid-term changes, cancellations, no-claims discount, excess, underinsurance and reserves.

65 capabilities (28 core, 37 optional) and 13 gaps, in build order. Put each line in the file that calls it, then fune build.

Not insurance or regulatory advice. Rating tables, scales and factors are the insurer's own, supplied by the caller; IPT follows UK HMRC rates.

1. Rating and quotes

Price a policy from the risk's details.

insurance.rating-factors core Unreviewed
Premium from a base rate times factor-table lookups, loadings and discounts, within a minimum and maximum.
Premium from a base rate times factor-table lookups, loadings and discounts, within minimum and maximum premiums.
fune!(insurance.rating-factors@^1);  // then call rate_premium(…)
insurance.ipt core Unreviewed
UK Insurance Premium Tax at the standard or higher rate in force, added to or taken out of a premium.
UK Insurance Premium Tax at the standard or higher rate in force on a date, added to or taken out of a premium.
fune!(insurance.ipt@^1);  // then call insurance_premium_tax(…)
insurance.age-banding optional Unreviewed
Age at inception and the rating band it falls in, from your age bands.
Age at policy inception and the rating band it falls in, from a table of age bands.
fune!(insurance.age-banding@^1);  // then call age_band(…)
insurance.no-claims-discount optional Unreviewed
No-claims discount at renewal: earn a year or step back after claims on your scale, with protection.
No-claims discount at renewal: earn a year, or step back after claims on the insurer's scale, with NCD protection.
fune!(insurance.no-claims-discount@^1);  // then call next_no_claims_discount(…)
dates.age optional
Age in whole years on a date, for age-based rating.
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(…)
  • risk-data External risk data (vehicle lookups, flood and crime scores, address data) comes from data providers.

2. Paying the premium

Let customers pay annually or by monthly instalments.

insurance.instalment-plan core Unreviewed
Deposit, flat credit charge, instalments to the penny and the APR for paying monthly.
Pay a premium by monthly instalments: deposit, flat credit charge, instalments exact to the penny, and the APR.
fune!(insurance.instalment-plan@^1);  // then call instalment_plan(…)
lending.apr optional Unreviewed
The consumer-credit APR from drawdowns and repayments, for credit agreements beyond the instalment plan.
Consumer-credit APR from drawdowns and repayments, by the FCA CONC App 1.2.6 equation, to one decimal place.
fune!(lending.apr@^1);  // then call apr(…)

3. Mid-term changes and cancellations

Re-price a policy when it changes or ends early.

insurance.mid-term-adjustment core Unreviewed
Additional or return premium for a mid-term change, pro rata on the days left, plus an admin fee.
Additional or return premium for a mid-term policy change, pro rata on the days left, plus an admin fee.
fune!(insurance.mid-term-adjustment@^1);  // then call mid_term_adjustment(…)
insurance.premium-proration core Unreviewed
Return premium on cancellation, pro rata or on a short-period scale, with a minimum retained premium.
Return premium on mid-term cancellation, pro rata or on a short-period scale, with an optional minimum retained premium.
fune!(insurance.premium-proration@^1);  // then call cancellation_return_premium(…)
finance.proration optional
Splits any amount into used and unused parts over a period.
Split an amount into the part used and the part unused over a billing period, losing nothing.
fune!(finance.proration@^1);  // then call prorate(…)

4. Claims

Settle claims and keep reserves.

insurance.excess-apply core Unreviewed
Claim payout after the compulsory and voluntary excess, capped at the policy limit.
Claim payout after the compulsory and voluntary excess, capped at the policy limit.
fune!(insurance.excess-apply@^1);  // then call apply_excess(…)
insurance.claim-reserve core Unreviewed
Paid, recovered, outstanding and incurred after each claim transaction, and the movement.
Claim reserve history: paid, recovered, outstanding and incurred after each transaction, and the movement.
fune!(insurance.claim-reserve@^1);  // then call claim_reserve_history(…)
insurance.sum-insured-average optional Unreviewed
Average clause settlement when the property was underinsured.
Average clause settlement for underinsurance: the loss times sum insured over value at risk, capped at the loss.
fune!(insurance.sum-insured-average@^1);  // then call average_clause_settlement(…)
  • fraud-checks Claims fraud detection and industry databases (CUE, IFB) are services caps does not have.

5. Portfolio reporting

Report how the book is performing.

insurance.loss-ratio optional Unreviewed
Loss, expense and combined ratios in basis points from earned premium, claims and expenses.
Loss ratio, expense ratio and combined ratio in basis points, from earned premium, incurred claims and expenses.
fune!(insurance.loss-ratio@^1);  // then call loss_ratios(…)
stats.mean-median optional
Means and medians of claim sizes, exactly.
Mean, median and mode of a list of integers, with the mean and median as exact fractions.
fune!(stats.mean-median@^1);  // then call mean_median_mode(…)
  • earned-premium Earned and unearned premium reserves across the book at a reporting date are not computed by caps.

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.
fune!(money.amount@^1);  // then call money(…)
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(…)
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(…)
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

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.
fune!(money.apply-rate@^1);  // then call apply_rate(…)
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(…)
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
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(…)
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
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(…)

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.
fune!(money.allocate@^1);  // then call allocate(…)
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(…)

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.
fune!(money.format@^1);  // then call format_money(…)
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(…)
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(…)

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.
fune!(money.convert@^1);  // then call convert_money(…)

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(…)
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(…)
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(…)
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(…)
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(…)
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(…)
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(…)

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(…)
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(…)
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(…)

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(…)
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(…)
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(…)

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(…)

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(…)
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(…)
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(…)
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(…)
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(…)
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(…)

16. Contact details from validation-basics

Check and normalise a person's contact details before storing them.

validation.email core
Is an email address plausible, by a documented subset of RFC 5322.
Is this a plausible email address? A documented, pragmatic subset of RFC 5322, not the full grammar.
fune!(validation.email@^1);  // then call is_email(…)
validation.phone-e164 core
Normalises a phone number to +44… E.164 for a default country.
Normalise a phone number to E.164 (+447700900123) using a default country's trunk and dialling prefixes.
fune!(validation.phone-e164@^2);  // then call validate_phone_e164(…)
validation.uk-postcode core
Validates a UK postcode and puts it in canonical form.
Validate a UK postcode and normalise it to canonical upper case with one space before the inward code.
fune!(validation.uk-postcode@^2);  // then call validate_uk_postcode(…)
text.normalise-name optional
Tidies a typed name: spacing and title case with Mc, O' and hyphen rules.
Tidy a personal name: trim, collapse whitespace and title-case, with Mc, Mac, O', hyphen and van/de rules.
fune!(text.normalise-name@^1);  // then call normalise_name(…)
auth.normalise-email optional
Lower-cases an email's domain for storage and lookup.
Trim an email address and lower-case its domain for storage and lookup, or null if it is not a plausible address.
fune!(auth.normalise-email@^1);  // then call normalise_email(…)

17. Bank details from validation-basics

Check payee and customer bank details before money moves.

validation.iban core
Checks an IBAN's mod-97 checksum and its country's length.
Check an IBAN against the ISO 13616 mod-97-10 checksum and the registered length for its country.
fune!(validation.iban@^1);  // then call is_iban(…)
validation.bic optional
Checks a SWIFT/BIC code's format and splits it.
Check the format of a SWIFT/BIC code (8 or 11 characters) and split it into its parts.
fune!(validation.bic@^1);  // then call validate_bic(…)
validation.uk-sort-code-account optional
Checks a UK sort code and account number by the Vocalink modulus rules, against a table you supply.
Check a UK sort code and account number with the Vocalink/Pay.UK modulus rules, against a table you supply.
fune!(validation.uk-sort-code-account@^3);  // then call validate_uk_sort_code_account(…)
validation.uk-modulus-table optional
Parses Vocalink's VALACDOS and SCSUBTAB files into that table.
Parse Vocalink's VALACDOS.txt and SCSUBTAB.txt into the table UK sort code modulus checking needs.
fune!(validation.uk-modulus-table@^1);  // then call parse_uk_modulus_table(…)

18. Identifiers and check digits from validation-basics

Catch mistyped reference numbers by their check digits.

validation.luhn optional
Luhn mod-10 check, for card numbers and many reference numbers.
Check a digit string against the Luhn mod-10 checksum used by payment cards and many identifiers.
fune!(validation.luhn@^1);  // then call is_luhn(…)
validation.uk-company-number optional
Checks a Companies House number's format and prefix.
Check a Companies House company number's format and prefix (SC, NI, OC, LP...) and zero-pad it to eight characters.
fune!(validation.uk-company-number@^2);  // then call validate_uk_company_number(…)
validation.lei optional
Checks a Legal Entity Identifier's check digits.
Check a 20-character Legal Entity Identifier against its ISO 17442 / ISO 7064 MOD 97-10 check digits.
fune!(validation.lei@^1);  // then call validate_lei(…)
validation.gtin optional
Checks an EAN/UPC/GTIN barcode number.
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(…)

19. Displaying sensitive values from validation-basics

Show stored identifiers without exposing them.

text.mask optional
Masks all but the last digits of a card, account or phone number.
Mask all but the last n characters of a card, account or phone number, optionally keeping separators.
fune!(text.mask@^1);  // then call mask(…)

Gaps

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

  • policy-documents Policy schedules, certificates and statements of fact: caps gives figures, not documents.
  • renewal-pricing Renewal pricing rules (the FCA's price-walking ban: renewal no dearer than new business) are policy you implement.
  • 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.
  • address-lookup Turning a postcode into a list of addresses (Royal Mail PAF or a lookup API) is network work and not in caps.
  • email-deliverability Whether a mailbox exists (MX lookups, a confirmation email): validation.email checks the syntax only.
  • vocalink-data The Vocalink modulus tables themselves: download them from Pay.UK under its terms; caps parses them but does not ship them.