Functional Weave
Code in Rust

agri.milk-price@1.0.0

impl/python.py

3,418 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 .agri_milk_price_types import MilkContract, MilkPrice, MilkSupply
from .math_round_div import round_div  ← from math.round-div ^1.0.0 · built alongside by fune
from .money_amount import money  ← from money.amount ^1.0.0 · built alongside by fune

MAX_PRICE = 1000000
MAX_LITRES = 1000000000


def _whole(value: int, name: str, low: int, high: int) -> int:
    if isinstance(value, bool) or not isinstance(value, int) or value < low or value > high:
        raise ValueError("%s must be a whole number from %d to %d, received %r" % (name, low, high, value))
    return value


def milk_price(contract: MilkContract, supply: MilkSupply) -> MilkPrice:
    """Milk price for one period under a buyer's contract.

    Prices are thousandths of a penny (or cent) per litre, because contracts
    quote to 0.01 or 0.001 ppl. Constituent adjustments are pro rata per 0.1
    percentage point and are each rounded to a thousandth, half away from
    zero; the payment is litres x price, rounded once to the penny.
    """
    money(0, contract.currency)
    base = _whole(contract.base_price, "basePrice", 0, MAX_PRICE)
    std_fat = _whole(contract.standard_butterfat_basis_points, "standardButterfatBasisPoints", 0, 10000)
    fat_value = _whole(contract.butterfat_value, "butterfatValue", -MAX_PRICE, MAX_PRICE)
    std_protein = _whole(contract.standard_protein_basis_points, "standardProteinBasisPoints", 0, 10000)
    protein_value = _whole(contract.protein_value, "proteinValue", -MAX_PRICE, MAX_PRICE)
    litres = _whole(supply.litres, "litres", 0, MAX_LITRES)
    fat = _whole(supply.butterfat_basis_points, "butterfatBasisPoints", 0, 10000)
    protein = _whole(supply.protein_basis_points, "proteinBasisPoints", 0, 10000)
    month = _whole(supply.month, "month", 1, 12)

    # 0.1 percentage point is 10 basis points.
    fat_adjustment = round_div((fat - std_fat) * fat_value, 10, "half-up")
    protein_adjustment = round_div((protein - std_protein) * protein_value, 10, "half-up")

    volume_bonus = 0
    previous = -1
    for i, tier in enumerate(contract.volume_bonuses):
        minimum = _whole(tier.minimum_litres, "volumeBonuses[%d].minimumLitres" % i, 0, MAX_LITRES)
        bonus = _whole(tier.bonus, "volumeBonuses[%d].bonus" % i, -MAX_PRICE, MAX_PRICE)
        if minimum <= previous:
            raise ValueError("volumeBonuses must be in strictly ascending order of minimumLitres")
        previous = minimum
        if litres >= minimum:
            volume_bonus = bonus

    seasonal = 0
    if len(contract.seasonality) == 12:
        for i, v in enumerate(contract.seasonality):
            _whole(v, "seasonality[%d]" % i, -MAX_PRICE, MAX_PRICE)
        seasonal = contract.seasonality[month - 1]
    elif len(contract.seasonality) != 0:
        raise ValueError("seasonality must have 12 monthly values or none, received %d" % len(contract.seasonality))

    price = base + fat_adjustment + protein_adjustment + volume_bonus + seasonal
    if price < 0:
        raise ValueError("price per litre must not be negative, received %d" % price)
    if price > MAX_PRICE:
        raise ValueError("price per litre must be at most %d, received %d" % (MAX_PRICE, price))
    return MilkPrice(
        base_price=base,
        butterfat_adjustment=fat_adjustment,
        protein_adjustment=protein_adjustment,
        volume_bonus=volume_bonus,
        seasonal_adjustment=seasonal,
        price_per_litre=price,
        total=money(round_div(price * litres, 1000, "half-up"), contract.currency),
    )