Functional Weave
Code in Python

manufacturing.variance@1.0.0

impl/python.py

3,284 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 .manufacturing_variance_types import CostVariances, LabourActual, MaterialActual, Variance
from .math_rational import Rational, multiply_rational, rational, rational_to_integer  ← from math.rational ^1.0.0 · built alongside by fune
from .math_round_div import RoundingMode  ← from math.round-div ^1.0.0 · built alongside by fune
from .money_amount import Money, assert_same_currency, money  ← from money.amount ^1.0.0 · built alongside by fune

MAX_SAFE = 2**53 - 1


def _not_negative(name: str, value: Rational) -> Rational:
    r = rational(value.numerator, value.denominator)
    if r.numerator < 0:
        raise ValueError("%s must not be negative" % (name,))
    return r


def _cost_of(name: str, value: Money, unit: Money) -> int:
    assert_same_currency(unit, value)
    if value.minor < 0:
        raise ValueError("%s must not be negative, received %d" % (name, value.minor))
    return value.minor


def _variance(signed: int, currency: str) -> Variance:
    effect = "adverse" if signed > 0 else "favourable" if signed < 0 else "none"
    return Variance(amount=money(abs(signed), currency), effect=effect)


def cost_variances(material: MaterialActual, labour: LabourActual, actual_output: int, mode: RoundingMode) -> CostVariances:
    """Material price and usage, labour rate and efficiency variances, flexed
    to actual output. The four flexed costs are each rounded once and every
    variance is a difference of them, so the variances always sum exactly.
    """
    if isinstance(actual_output, bool) or not isinstance(actual_output, int) or actual_output < 0 or actual_output > MAX_SAFE:
        raise ValueError("actualOutput must be a whole number, not negative, received %r" % (actual_output,))
    unit = material.standard_price
    currency = unit.currency
    sp = _cost_of("material standardPrice", material.standard_price, unit)
    mat_actual = _cost_of("material actualCost", material.actual_cost, unit)
    sr = _cost_of("labour standardRate", labour.standard_rate, unit)
    lab_actual = _cost_of("labour actualCost", labour.actual_cost, unit)
    sq = _not_negative("material standardQuantity", material.standard_quantity)
    aq = _not_negative("material actualQuantity", material.actual_quantity)
    sh = _not_negative("labour standardHours", labour.standard_hours)
    ah = _not_negative("labour actualHours", labour.actual_hours)
    output = rational(actual_output, 1)

    actual_at_standard = rational_to_integer(multiply_rational(aq, rational(sp, 1)), mode)
    standard_material = rational_to_integer(multiply_rational(multiply_rational(sq, output), rational(sp, 1)), mode)
    hours_at_standard = rational_to_integer(multiply_rational(ah, rational(sr, 1)), mode)
    standard_labour = rational_to_integer(multiply_rational(multiply_rational(sh, output), rational(sr, 1)), mode)

    standard_cost = standard_material + standard_labour
    actual_cost = mat_actual + lab_actual
    return CostVariances(
        material_price=_variance(mat_actual - actual_at_standard, currency),
        material_usage=_variance(actual_at_standard - standard_material, currency),
        labour_rate=_variance(lab_actual - hours_at_standard, currency),
        labour_efficiency=_variance(hours_at_standard - standard_labour, currency),
        total=_variance(actual_cost - standard_cost, currency),
        standard_cost=money(standard_cost, currency),
        actual_cost=money(actual_cost, currency),
    )