Functional Weave
Code in Python

construction.day-rate-quote@1.0.0

impl/python.py

2,082 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 typing import Optional

from .construction_day_rate_quote_types import DayRateQuote
from .finance_markup_price import markup_price  ← from finance.markup-price ^1.0.0 · built alongside by fune
from .finance_tax_add_vat import add_vat  ← from finance.tax.add-vat ^1.0.0 · built alongside by fune
from .math_round_div import round_div  ← from math.round-div ^1.0.0 · built alongside by fune
from .money_add import add_money  ← from money.add ^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 day_rate_quote(
    labour_days_hundredths: int,
    day_rate: Money,
    materials_cost: Money,
    materials_markup_basis_points: int,
    vat_category: Optional[str],
    on_date: str,
) -> DayRateQuote:
    """A quote from labour days, a day rate, marked-up materials and VAT.

    The markup goes on the materials only: the day rate already is the price of
    labour. Labour is rounded once, to the penny, from days x rate; VAT is one
    rounding on the net total, as on the invoice that follows.
    """
    if (
        isinstance(labour_days_hundredths, bool)
        or not isinstance(labour_days_hundredths, int)
        or labour_days_hundredths < 0
    ):
        raise ValueError(
            "labourDaysHundredths must be a whole number of hundredths, not negative, received %r"
            % (labour_days_hundredths,)
        )
    if day_rate.minor < 0:
        raise ValueError("dayRate must not be negative, received %d" % (day_rate.minor,))
    product = day_rate.minor * labour_days_hundredths
    # Python would carry on, but TypeScript cannot, and all three must agree.
    if product > MAX_SAFE:
        raise ValueError("the labour calculation exceeds 2^53 - 1")
    labour = money(round_div(product, 100, "half-up"), day_rate.currency)
    materials = markup_price(materials_cost, materials_markup_basis_points, "markup", "half-up")
    net = add_money(labour, materials)
    if vat_category is None:
        return DayRateQuote(labour=labour, materials=materials, net=net, vat=money(0, net.currency), gross=net, vat_basis_points=0)
    vat = add_vat(net, "GB", vat_category, on_date)
    return DayRateQuote(
        labour=labour, materials=materials, net=net, vat=vat.tax, gross=vat.gross, vat_basis_points=vat.basis_points
    )