Functional Weave
Code in Python

construction.retention@1.0.0

impl/python.py

1,726 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 .construction_retention_types import Retention, RetentionStage
from .math_round_div import 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

MAX_SAFE = 9007199254740991


def retention(cumulative_value: Money, basis_points: int, stage: RetentionStage) -> Retention:
    """Retention on a valuation, the way JCT-style contracts release it.

    Retention is always worked out afresh on the cumulative value, never
    accumulated valuation by valuation, so a later valuation corrects an earlier
    one. At practical completion half the percentage applies (not half of a
    figure rounded earlier), and after the defects are made good none does.
    """
    if isinstance(basis_points, bool) or not isinstance(basis_points, int) or basis_points < 0 or basis_points > 10000:
        raise ValueError("basisPoints must be a whole number from 0 to 10000, received %r" % (basis_points,))
    if cumulative_value.minor < 0:
        raise ValueError("cumulativeValue must not be negative, received %d" % (cumulative_value.minor,))
    product = cumulative_value.minor * basis_points
    # Python would carry on, but TypeScript cannot, and all three must agree.
    if product > MAX_SAFE:
        raise ValueError("the retention calculation exceeds 2^53 - 1")
    full = round_div(product, 10000, "half-up")
    if stage == "interim":
        held = full
    elif stage == "practical-completion":
        held = round_div(product, 20000, "half-up")
    elif stage == "final":
        held = 0
    else:
        raise ValueError('unknown retention stage "%s"' % (stage,))
    currency = cumulative_value.currency
    return Retention(full=money(full, currency), held=money(held, currency), released=money(full - held, currency))