Functional Weave
Code in Python

insurance.ipt@1.0.1

impl/python.py

2,738 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.

import re
from typing import Optional

from .insurance_ipt_data import IPT_RATES, IPT_RATES_HISTORY  ← this capability’s own data, compiled from data/ipt-rates.json into the same file by fune build
from .insurance_ipt_types import IptBand, IptBreakdown
from .math_round_div import RoundingMode, round_div  ← from math.round-div ^1.0.0 · built alongside by fune
from .money_amount import Money, money  ← from money.amount ^1.0.0 · built alongside by fune

ISO_DATE = re.compile(r"[0-9]{4}-[0-9]{2}-[0-9]{2}")


def insurance_premium_tax(
    amount: Money, includes_tax: bool, band: IptBand, on_date: str, mode: RoundingMode
) -> IptBreakdown:
    """Add UK Insurance Premium Tax to a net premium, or take it out of a gross one,
    at the rate in force on the date the insurer receives the premium.

    Only one figure is ever rounded - the tax when adding, the net when taking
    out - and the other is the difference, so net + tax is always the gross.
    Taking 12% off a gross premium is the classic error: the tax inside 112.00
    is 12.00, not 13.44.
    """
    if band not in ("standard", "higher"):
        raise ValueError('unknown IPT band "%s": use standard or higher' % (band,))
    if not isinstance(on_date, str) or not ISO_DATE.fullmatch(on_date):
        raise ValueError('onDate must be an ISO date (YYYY-MM-DD), received "%s"' % (on_date,))
    rule = None
    for r in IPT_RATES:
        if r.band != band or on_date < r.valid_from:
            continue
        if r.valid_to is not None and on_date > r.valid_to:
            continue
        rule = r
    if rule is None:
        # A history=current build keeps only the rates in force; answering an
        # older date with today's rate would be exactly the wrong answer.
        if IPT_RATES_HISTORY != "full":
            earliest: Optional[str] = None
            for r in IPT_RATES:
                if r.band == band and (earliest is None or r.valid_from < earliest):
                    earliest = r.valid_from
            if earliest is not None and on_date < earliest:
                raise ValueError(
                    "no IPT rule for %s on %s: this build was installed with history=%s, "
                    "so it only carries rates from %s. Reinstall with history=full for older premiums."
                    % (band, on_date, IPT_RATES_HISTORY, earliest)
                )
        raise ValueError("no IPT rule for %s on %s" % (band, on_date))
    bp = rule.basis_points
    if includes_tax:
        gross = amount
        net = money(round_div(amount.minor * 10000, 10000 + bp, mode), amount.currency)
        tax = money(amount.minor - net.minor, amount.currency)
    else:
        net = amount
        tax = money(round_div(amount.minor * bp, 10000, mode), amount.currency)
        gross = money(amount.minor + tax.minor, amount.currency)
    return IptBreakdown(net=net, tax=tax, gross=gross, basis_points=bp, band=band, valid_from=rule.valid_from)