from typing import List, Optional, Sequence from .dates_add_days import epoch_day_from_iso from .insurance_claim_reserve_types import ClaimTransaction, ReservePosition from .money_amount import assert_same_currency, money 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