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