Functional Weave
Code in Rust

insurance.claim-reserve@1.0.1

impl/typescript.ts

2,514 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 { epochDayFromIso } from "./dates_add_days.ts";  ← from dates.add-days ^1.0.0 · built alongside by fune
import { assertSameCurrency, money } from "./money_amount.ts";  ← from money.amount ^1.0.0 · built alongside by fune
import { type ClaimTransaction, type ReservePosition } from "./insurance_claim_reserve_types.ts";

/**
 * A claim's position after each transaction: paid, recovered, the outstanding
 * case reserve, and incurred = paid - recovered + outstanding.
 *
 * A payment erodes the outstanding reserve by what it pays, never below zero:
 * a payment larger than the reserve raises incurred by the excess, which is
 * how an under-reserved claim shows up. A reserve transaction sets the
 * outstanding estimate outright. The movement is what each transaction did to
 * incurred, the figure that goes through the accounts.
 */
export function claimReserveHistory(transactions: readonly ClaimTransaction[], currency: string): readonly ReservePosition[] {
  const zero = money(0, currency);
  let paid = 0;
  let recovered = 0;
  let outstanding = 0;
  let incurred = 0;
  let previousDay: number | null = null;
  let previousDate = "";
  const out: ReservePosition[] = [];
  for (const t of transactions) {
    const day = epochDayFromIso(t.date);
    if (previousDay !== null && day < previousDay) {
      throw new RangeError(`transactions must be in date order: ${t.date} comes after ${previousDate}`);
    }
    previousDay = day;
    previousDate = t.date;
    assertSameCurrency(zero, t.amount);
    if (t.kind === "reserve") {
      if (t.amount.minor < 0) {
        throw new RangeError(`a reserve must not be negative, received ${t.amount.minor} on ${t.date}`);
      }
      outstanding = t.amount.minor;
    } else if (t.kind === "payment" || t.kind === "recovery") {
      if (t.amount.minor <= 0) {
        throw new RangeError(`a ${t.kind} must be more than zero, received ${t.amount.minor} on ${t.date}`);
      }
      if (t.kind === "payment") {
        paid += t.amount.minor;
        outstanding = Math.max(outstanding - t.amount.minor, 0);
      } else {
        recovered += t.amount.minor;
      }
    } else {
      throw new RangeError(`unknown transaction kind "${t.kind}": use reserve, payment or recovery`);
    }
    const now = paid - recovered + outstanding;
    out.push({
      date: t.date,
      kind: t.kind,
      paid: money(paid, currency),
      recovered: money(recovered, currency),
      outstanding: money(outstanding, currency),
      incurred: money(now, currency),
      movement: money(now - incurred, currency),
    });
    incurred = now;
  }
  return out;
}