Functional Weave
Code in Rust

insurance.claim-reserve@1.0.0

impl/python.py

2,622 bytes · the Python 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.

from typing import List, Optional, Sequence

from .dates_add_days import epoch_day_from_iso  ← from dates.add-days ^1.0.0 · built alongside by fune
from .insurance_claim_reserve_types import ClaimTransaction, ReservePosition
from .money_amount import assert_same_currency, money  ← from money.amount ^1.0.0 · built alongside by fune


def claim_reserve_history(transactions: Sequence[ClaimTransaction], currency: str) -> List[ReservePosition]:
    """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.
    """
    zero = money(0, currency)
    paid = 0
    recovered = 0
    outstanding = 0
    incurred = 0
    previous_day: Optional[int] = None
    previous_date = ""
    out: List[ReservePosition] = []
    for t in transactions:
        day = epoch_day_from_iso(t.date)
        if previous_day is not None and day < previous_day:
            raise ValueError("transactions must be in date order: %s comes after %s" % (t.date, previous_date))
        previous_day = day
        previous_date = t.date
        assert_same_currency(zero, t.amount)
        if t.kind == "reserve":
            if t.amount.minor < 0:
                raise ValueError("a reserve must not be negative, received %d on %s" % (t.amount.minor, t.date))
            outstanding = t.amount.minor
        elif t.kind in ("payment", "recovery"):
            if t.amount.minor <= 0:
                raise ValueError("a %s must be more than zero, received %d on %s" % (t.kind, t.amount.minor, t.date))
            if t.kind == "payment":
                paid += t.amount.minor
                outstanding = max(outstanding - t.amount.minor, 0)
            else:
                recovered += t.amount.minor
        else:
            raise ValueError('unknown transaction kind "%s": use reserve, payment or recovery' % (t.kind,))
        now = paid - recovered + outstanding
        out.append(
            ReservePosition(
                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