Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import math
from typing import Optional
from .inventory_safety_stock_data import Z_SCORES ← this capability’s own data, compiled from data/z-scores.json into the same file by fune build
from .inventory_safety_stock_types import SafetyStockInput
def _amount(name: str, value: Optional[float], method: str) -> float:
if value is None:
raise ValueError("%s needs %s" % (method, name))
if isinstance(value, bool) or not isinstance(value, (int, float)) or not math.isfinite(value) or value < 0:
raise ValueError("%s must be a finite number, not negative, received %r" % (name, value))
return float(value)
def _whole_units_up(x: float) -> int:
# Six decimal places, half away from zero, then up to whole units: float
# noise such as 55.00000000000001 must not cost a whole extra unit.
y = x * 1e6
micro = math.floor(y)
if y - micro >= 0.5:
micro += 1
return (micro + 999999) // 1000000
def safety_stock(input: SafetyStockInput) -> int:
"""Safety stock in whole units, by the service-level or max-minus-average method."""
lead_time = _amount("leadTime", input.lead_time, input.method)
if input.method == "service-level":
level = input.service_level_basis_points
if level is None:
raise ValueError("service-level needs serviceLevelBasisPoints")
row = next((r for r in Z_SCORES if r.service_level_basis_points == level), None)
if row is None:
raise ValueError(
"no z-score for a service level of %s basis points: use one of %s"
% (level, ", ".join(str(r.service_level_basis_points) for r in Z_SCORES))
)
sd = _amount("demandStdDev", input.demand_std_dev, input.method)
variance = lead_time * sd * sd
sl = _amount("leadTimeStdDev", 0.0 if input.lead_time_std_dev is None else input.lead_time_std_dev, input.method)
if sl > 0:
d = _amount("averageDemand", input.average_demand, "service-level with a varying lead time")
variance = variance + d * d * sl * sl
return _whole_units_up((row.z_ten_thousandths / 10000) * math.sqrt(variance))
if input.method == "max-minus-average":
max_demand = _amount("maxDemand", input.max_demand, input.method)
max_lead_time = _amount("maxLeadTime", input.max_lead_time, input.method)
average_demand = _amount("averageDemand", input.average_demand, input.method)
worst = max_demand * max_lead_time
usual = average_demand * lead_time
if worst < usual:
raise ValueError(
"maxDemand x maxLeadTime (%s) must not be less than averageDemand x leadTime (%s)" % (worst, usual)
)
return _whole_units_up(worst - usual)
raise ValueError('unknown safety stock method "%s"' % (input.method,))