Functional Weave
Code in Python

insurance.sum-insured-average@1.0.1

impl/typescript.ts

2,377 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, assertSameCurrency, money } from "./money_amount.ts";  ← from money.amount ^1.0.0 · built alongside by fune
import { type AverageSettlement } from "./insurance_sum_insured_average_types.ts";

/**
 * Settle a claim under an average clause.
 *
 * A property insured for less than it is worth is paid only the share of the
 * loss that the sum insured bears to the value at risk: insured for 75% of
 * its value, it is paid 75% of any loss. With a special condition of average
 * the clause only applies once the sum insured falls below that share of the
 * value. The payout never exceeds the loss or the sum insured.
 */
export function averageClauseSettlement(
  loss: Money,
  sumInsured: Money,
  valueAtRisk: Money,
  conditionBasisPoints: number,
  mode: RoundingMode
): AverageSettlement {
  assertSameCurrency(loss, sumInsured);
  assertSameCurrency(loss, valueAtRisk);
  if (valueAtRisk.minor <= 0) {
    throw new RangeError(`valueAtRisk must be more than zero, received ${valueAtRisk.minor}`);
  }
  if (loss.minor < 0) throw new RangeError(`loss must not be negative, received ${loss.minor}`);
  if (sumInsured.minor < 0) throw new RangeError(`sumInsured must not be negative, received ${sumInsured.minor}`);
  if (loss.minor > valueAtRisk.minor) throw new RangeError("loss must not exceed the value at risk");
  if (!Number.isInteger(conditionBasisPoints) || conditionBasisPoints < 1 || conditionBasisPoints > 10000) {
    throw new RangeError(`conditionBasisPoints must be from 1 to 10000, received ${conditionBasisPoints}`);
  }
  const averageApplied = sumInsured.minor * 10000 < valueAtRisk.minor * conditionBasisPoints;
  let payout = loss.minor;
  if (averageApplied) {
    // A loss and a sum insured in pence multiply past 2^53, so divide in bigint
    // and round the remainder the way roundDiv would.
    const wide = BigInt(loss.minor) * BigInt(sumInsured.minor);
    const value = BigInt(valueAtRisk.minor);
    payout = Number(wide / value) + roundDiv(Number(wide % value), valueAtRisk.minor, mode);
  }
  payout = Math.min(payout, loss.minor, sumInsured.minor);
  const c = loss.currency;
  return {
    payout: money(payout, c),
    averageApplied,
    insuredProportionBasisPoints: Math.min(roundDiv(sumInsured.minor * 10000, valueAtRisk.minor, "down"), 10000),
    shortfall: money(loss.minor - payout, c),
  };
}