from typing import Optional from .insurance_excess_apply_types import ExcessSettlement from .money_amount import Money, assert_same_currency, money def apply_excess( loss: Money, compulsory_excess: Money, voluntary_excess: Money, policy_limit: Optional[Money], excess_within_limit: bool, ) -> ExcessSettlement: """The claim payout after the excess, capped at the policy limit. The excess is the compulsory and voluntary excess together, and a loss smaller than it pays nothing. Where the limit sits relative to the excess is a policy wording question, so the caller says which: by default the limit caps what is paid after the excess; with excess_within_limit the excess is taken off the loss after the limit, so it erodes the limit. """ assert_same_currency(loss, compulsory_excess) assert_same_currency(loss, voluntary_excess) if policy_limit is not None: assert_same_currency(loss, policy_limit) if loss.minor < 0: raise ValueError("loss must not be negative, received %d" % (loss.minor,)) if compulsory_excess.minor < 0 or voluntary_excess.minor < 0: raise ValueError("excess must not be negative") if policy_limit is not None and policy_limit.minor < 0: raise ValueError("policyLimit must not be negative, received %d" % (policy_limit.minor,)) excess = compulsory_excess.minor + voluntary_excess.minor limit_applied = False if excess_within_limit: covered = loss.minor if policy_limit is not None and policy_limit.minor < covered: covered = policy_limit.minor limit_applied = True payout = max(covered - excess, 0) else: payout = max(loss.minor - excess, 0) if policy_limit is not None and policy_limit.minor < payout: payout = policy_limit.minor limit_applied = True c = loss.currency return ExcessSettlement( total_excess=money(excess, c), excess_applied=money(min(excess, loss.minor), c), payout=money(payout, c), limit_applied=limit_applied, uninsured=money(loss.minor - payout, c), )