insurance.excess-apply Unreviewed
Claim payout after the compulsory and voluntary excess, capped at the policy limit.
1.0.1 · published 2026-10-03 by charlie · Anterra
Pinned by 16 tests, run in TypeScript, Python and Rust.
Unreviewed. This capability’s implementations agree in every language and pass its published test vectors, which were worked out from the official sources cited. But no qualified actuary has yet checked those vectors, or confirmed that the capability covers the cases it claims. Treat it as a draft. Do not use it for real people, money or decisions without your own expert review. Once a qualified reviewer signs off, this notice is replaced with their name, qualification and the date. Each new version needs fresh sign-off.
Not professional advice. This capability calculates insurance figures from published rules. It is a software component for developers, not financial advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed in its README, and have an actuary review how you use it, before anyone relies on the output. Provided “as is” under its licence, without warranty.
What it does
What the insurer pays on a claim once the excess (deductible) has been taken off and the policy limit applied, and what the policyholder is left bearing.
## The excess
For example
applyExcess(£2,500.00, £250.00, £250.00, —, false)→ total excess £500.00, excess applied £500.00, payout £2,000.00, limit applied false, uninsured £500.00 a 2,500.00 loss less 250.00 compulsory and 250.00 voluntary excess pays 2,000.00applyExcess(£300.00, £250.00, £250.00, —, false)→ total excess £500.00, excess applied £300.00, payout £0.00, limit applied false, uninsured £300.00 a loss smaller than the excess pays nothingapplyExcess(£500.00, £250.00, £250.00, —, false)→ total excess £500.00, excess applied £500.00, payout £0.00, limit applied false, uninsured £500.00 a loss exactly the excess pays nothing
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 applyExcess(loss: Money, compulsoryExcess: Money, voluntaryExcess: Money, policyLimit: Money | null, excessWithinLimit: boolean): ExcessSettlement
| loss | Money | the covered loss, 0 or more |
| compulsoryExcess | Money | the excess the insurer sets, 0 or more |
| voluntaryExcess | Money | the extra excess the policyholder chose, 0 or more |
| policyLimit | Money? | the most the policy pays for the claim, or null for no limit |
| excessWithinLimit | bool | false: the limit caps what is paid after the excess; true: the excess comes off the limited loss |
| returns | ExcessSettlement |
The type it declares, generated into your project
/** What the insurer pays and what the policyholder bears. */
export interface ExcessSettlement {
/** compulsory plus voluntary */
readonly totalExcess: Money;
/** the part of the excess the loss used up, never more than the loss */
readonly excessApplied: Money;
readonly payout: Money;
/** true when the policy limit reduced the payout */
readonly limitApplied: boolean;
/** loss minus payout: what the policyholder bears */
readonly uninsured: Money;
}
Your code names it in one line, in the file that uses it
import { applyExcess } from "#fune/insurance.excess-apply@^1";
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { type Money, assertSameCurrency, money } from "./money_amount.ts"; ← from money.amount ^1.0.0 · built alongside by fune
import { type ExcessSettlement } from "./insurance_excess_apply_types.ts";
/**
* The claim payout after the excess, capped at the policy limit.
*
* The excess is the compulsory and voluntary excess together, and a loss
* smaller than it pays nothing. Where the limit sits relative to the excess is
* a policy wording question, so the caller says which: by default the limit
* caps what is paid after the excess; with excessWithinLimit the excess is
* taken off the loss after the limit, so it erodes the limit.
*/
export function applyExcess(
loss: Money,
compulsoryExcess: Money,
voluntaryExcess: Money,
policyLimit: Money | null,
excessWithinLimit: boolean
): ExcessSettlement {
const hasLimit = policyLimit !== null && policyLimit !== undefined;
assertSameCurrency(loss, compulsoryExcess);
assertSameCurrency(loss, voluntaryExcess);
if (hasLimit) assertSameCurrency(loss, policyLimit);
if (loss.minor < 0) throw new RangeError(`loss must not be negative, received ${loss.minor}`);
if (compulsoryExcess.minor < 0 || voluntaryExcess.minor < 0) {
throw new RangeError("excess must not be negative");
}
if (hasLimit && policyLimit.minor < 0) {
throw new RangeError(`policyLimit must not be negative, received ${policyLimit.minor}`);
}
const excess = compulsoryExcess.minor + voluntaryExcess.minor;
let payout: number;
let limitApplied = false;
if (excessWithinLimit) {
let covered = loss.minor;
if (hasLimit && policyLimit.minor < covered) {
covered = policyLimit.minor;
limitApplied = true;
}
payout = Math.max(covered - excess, 0);
} else {
payout = Math.max(loss.minor - excess, 0);
if (hasLimit && policyLimit.minor < payout) {
payout = policyLimit.minor;
limitApplied = true;
}
}
const c = loss.currency;
return {
totalExcess: money(excess, c),
excessApplied: money(Math.min(excess, loss.minor), c),
payout: money(payout, c),
limitApplied,
uninsured: money(loss.minor - payout, c),
};
}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 insurance.excess-apply
The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./insurance.excess-apply-1.0.1-typescript.fune, or fetch it from a terminal with fune pull insurance.excess-apply@1.0.1:typescript.
The whole function, every language, is one file too: insurance.excess-apply-1.0.1.fune, 22,577 bytes, sha256 7c4367145a5a64ec36e9208bdc8f7522251b18bf68a78e588c94314952f4e6b1. 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 insurance.excess-apply
after — your function gets the result and the arguments, and returns the final result.
// fune: after insurance.excess-apply
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 money.amount in insurance.excess-apply
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 insurance.excess-apply --steps.
// fune: step insurance.excess-apply 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.
| Case | Arguments | Expected | |
|---|---|---|---|
| a 2,500.00 loss less 250.00 compulsory and 250.00 voluntary excess pays 2,000.00 | £2,500.00, £250.00, £250.00, —, false | → | total excess £500.00, excess applied £500.00, payout £2,000.00, limit applied false, uninsured £500.00 |
| a loss smaller than the excess pays nothing | £300.00, £250.00, £250.00, —, false | → | total excess £500.00, excess applied £300.00, payout £0.00, limit applied false, uninsured £300.00 |
| a loss exactly the excess pays nothing | £500.00, £250.00, £250.00, —, false | → | total excess £500.00, excess applied £500.00, payout £0.00, limit applied false, uninsured £500.00 |
| a zero loss | £0.00, £250.00, £0.00, —, false | → | total excess £250.00, excess applied £0.00, payout £0.00, limit applied false, uninsured £0.00 |
| no excess pays the whole loss | £1,234.56, £0.00, £0.00, —, false | → | total excess £0.00, excess applied £0.00, payout £1,234.56, limit applied false, uninsured £0.00 |
| the limit caps the payout after the excess | £12,000.00, £250.00, £250.00, £10,000.00, false | → | total excess £500.00, excess applied £500.00, payout £10,000.00, limit applied true, uninsured £2,000.00 |
| with the excess within the limit it erodes the limit | £12,000.00, £250.00, £250.00, £10,000.00, true | → | total excess £500.00, excess applied £500.00, payout £9,500.00, limit applied true, uninsured £2,500.00 |
| a loss just over the limit: after the excess it is under, so the limit does not bite | £10,400.00, £500.00, £0.00, £10,000.00, false | → | total excess £500.00, excess applied £500.00, payout £9,900.00, limit applied false, uninsured £500.00 |
| the same loss with the excess within the limit: capped first, then the excess | £10,400.00, £500.00, £0.00, £10,000.00, true | → | total excess £500.00, excess applied £500.00, payout £9,500.00, limit applied true, uninsured £900.00 |
| well under the limit, the limit changes nothing | £5,000.00, £100.00, £0.00, £10,000.00, false | → | total excess £100.00, excess applied £100.00, payout £4,900.00, limit applied false, uninsured £100.00 |
Show the other 6 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| a limit of zero pays nothing | £5,000.00, £100.00, £0.00, £0.00, false | → | total excess £100.00, excess applied £100.00, payout £0.00, limit applied true, uninsured £5,000.00 |
| payout exactly the limit is not reduced by it | £10,500.00, £500.00, £0.00, £10,000.00, false | → | total excess £500.00, excess applied £500.00, payout £10,000.00, limit applied false, uninsured £500.00 |
| a negative loss is refused | -£0.01, £0.00, £0.00, —, false | → | error: loss must not be negative |
| a negative excess is refused | £10.00, -£1.00, £0.00, —, false | → | error: excess must not be negative |
| a negative limit is refused | £10.00, £0.00, £0.00, -£0.05, false | → | error: policyLimit must not be negative |
| an excess in another currency is refused | £10.00, €1.00, £0.00, —, false | → | error: currency mismatch |
More from the author
The excess is the compulsory excess (set by the insurer) plus the voluntary excess (chosen by the policyholder for a lower premium); UK motor and home policies quote them separately but apply them together. A loss smaller than the excess pays nothing, and `excessApplied` is only the part of the excess the loss used up.
## Where the limit sits
Policies differ on whether the limit is applied before or after the excess, so the caller says which:
- `excessWithinLimit = false` (the usual "the most we will pay" wording): the excess comes off the loss, then the payout is capped at the limit. payout = min(loss - excess, limit). - `excessWithinLimit = true` (the excess erodes the limit): the loss is capped at the limit, then the excess comes off it. payout = min(loss, limit) - excess.
They differ whenever the loss is above the limit: a 10,400.00 loss with a 500.00 excess and a 10,000.00 limit pays 9,900.00 the first way and 9,500.00 the second. `limitApplied` is true only when the limit actually reduced what is paid.
No rounding happens: every figure is a whole number of minor units already. For underinsurance, see `insurance.sum-insured-average`; whether average comes before or after the excess is a policy wording question too.
## Before you rely on this
**Not professional advice.** This capability calculates insurance figures from published rules. It is a software component for developers, not financial advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed above, and have an actuary review how you use it, before anyone relies on the output. Provided "as is" under its licence, without warranty.
**Unreviewed.** This capability's implementations agree in every language and pass its published test vectors, which were worked out from the official sources cited. But no qualified actuary has yet checked those vectors, or confirmed that the capability covers the cases it claims. Treat it as a draft. Do not use it for real people, money or decisions without your own expert review. Once a qualified reviewer signs off, this notice is replaced with their name, qualification and the date. Each new version needs fresh sign-off.
1.0.1 marks it unreviewed. The code and the tests are unchanged.
Files
| Path | Bytes |
|---|---|
| README.md | 2,522 |
| impl/python.py | 2,158 |
| impl/rust.rs | 2,951 |
| impl/typescript.ts | 2,101 |
| vectors.json | 8,313 |