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))