Functional Weave
Code in Rust

insurance.ipt@1.0.2

impl/typescript.ts

2,722 bytes · the TypeScript implementation · view raw

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

import { type RoundingMode, roundDiv } from "./math_round_div.ts";  ← from math.round-div ^1.0.0 · built alongside by fune
import { type Money, money } from "./money_amount.ts";  ← from money.amount ^1.0.0 · built alongside by fune
import { IPT_RATES, IPT_RATES_HISTORY } from "./insurance_ipt_data.ts";  ← this capability’s own data, compiled from data/ipt-rates.json into the same file by fune build
import { type IptBand, type IptBreakdown } from "./insurance_ipt_types.ts";

const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;

/**
 * Add UK Insurance Premium Tax to a net premium, or take it out of a gross one,
 * at the rate in force on the date the insurer receives the premium.
 *
 * Only one figure is ever rounded - the tax when adding, the net when taking
 * out - and the other is the difference, so net + tax is always the gross.
 * Taking 12% off a gross premium is the classic error: the tax inside 112.00
 * is 12.00, not 13.44.
 */
export function insurancePremiumTax(
  amount: Money,
  includesTax: boolean,
  band: IptBand,
  onDate: string,
  mode: RoundingMode
): IptBreakdown {
  if (band !== "standard" && band !== "higher") {
    throw new RangeError(`unknown IPT band "${band}": use standard or higher`);
  }
  if (typeof onDate !== "string" || !ISO_DATE.test(onDate)) {
    throw new RangeError(`onDate must be an ISO date (YYYY-MM-DD), received "${onDate}"`);
  }
  let rule: (typeof IPT_RATES)[number] | null = null;
  for (const r of IPT_RATES) {
    if (r.band !== band || onDate < r.validFrom) continue;
    if (r.validTo !== null && onDate > r.validTo) continue;
    rule = r;
  }
  if (rule === null) {
    // A history=current build keeps only the rates in force; answering an older
    // date with today's rate would be exactly the wrong answer, so say so.
    if (IPT_RATES_HISTORY !== "full") {
      let earliest: string | null = null;
      for (const r of IPT_RATES) {
        if (r.band === band && (earliest === null || r.validFrom < earliest)) earliest = r.validFrom;
      }
      if (earliest !== null && onDate < earliest) {
        throw new RangeError(
          `no IPT rule for ${band} on ${onDate}: this build was installed with history=${IPT_RATES_HISTORY}, ` +
            `so it only carries rates from ${earliest}. Reinstall with history=full for older premiums.`
        );
      }
    }
    throw new RangeError(`no IPT rule for ${band} on ${onDate}`);
  }
  const bp = rule.basisPoints;
  let net: Money;
  let tax: Money;
  let gross: Money;
  if (includesTax) {
    gross = amount;
    net = money(roundDiv(amount.minor * 10000, 10000 + bp, mode), amount.currency);
    tax = money(amount.minor - net.minor, amount.currency);
  } else {
    net = amount;
    tax = money(roundDiv(amount.minor * bp, 10000, mode), amount.currency);
    gross = money(amount.minor + tax.minor, amount.currency);
  }
  return { net, tax, gross, basisPoints: bp, band, validFrom: rule.validFrom };
}