Skip to content
FlowScript templates

Supply — inventory policy (EOQ, safety stock, service level)

An order policy stated as the four numbers that actually run it: how much to order, when to order it, how much to hold against variability, and what that costs per year. Typed FlowScript for supply chains. Keywords: supply chain, logistics, network, supplier.

Template previewFlowScript
SUPPLY CHAIN · MARIS OTTER PALE ALE MALT, BULKMalt supply into the brewhouseΣ link cost: GBP 0.07Max lead: 14 daysSUPPLIERFACTORYWAREHOUSEGBP 0.02 · 14d · truckGBP 0.04 · 7d · truckGBP 0.01 · 1d · augerWarminster MaltingsWiltshire4,000,000 u/periodBulk tanker haulageContracted, 2 slots per week120,000 u/periodBrewery malt silo, 45 tBermondsey45,000 u/period40 hL brewhouseBermondsey10,000 u/period

Make it your own.

// An order policy stated as the four numbers that actually run it: how
// much to order, when to order it, how much to hold against variability,
// and what that costs per year.
//
// Bulk base malt for a 40 hL brewhouse. The economic order quantity is
// about two tanker loads; the reorder point is three weeks of demand plus
// four and a half days of safety stock. Every one of those follows from
// demand, cost and lead time, so raising the holding rate or shortening
// the lead time moves the whole policy rather than one cell of it.
//
// Money carries no unit here — the unit table has no currencies — so the
// currency is declared once as text and the cost arithmetic stays on plain
// numbers. Physical quantities keep their units: lead time in days, stock
// in kilogrammes.

inventory_policy pale_malt {
  title: "Base malt order policy — Maris Otter, bulk"
  item: "Maris Otter pale ale malt, delivered in 15 t bulk tankers"
  currency: "GBP"
  stock_unit: "kg"
  policy: "Continuous review (s, Q) — order a fixed quantity when stock on hand plus on order falls to the reorder point"

  annual_demand: 480000
  weeks_operating: 48
  weekly_demand: = annual_demand / weeks_operating
  demand_sd_weekly: 1800 source "Standard deviation of weekly issues, 2 brewing years"

  order_cost: 120 source "Goods-in labour, tanker slot and QA sampling per delivery"
  unit_cost: 0.58
  holding_rate: 22%
  holding_cost: = unit_cost * holding_rate / 100[%]

  economic_order_quantity: = sqrt(2 * annual_demand * order_cost / holding_cost)
  orders_per_year: = annual_demand / economic_order_quantity
  cycle_stock: = economic_order_quantity / 2
  tanker_load: 15000
  tankers_per_order: = economic_order_quantity / tanker_load

  lead_time: 21 d
  lead_time_weeks: = lead_time / 7[d]
  demand_over_lead_time: = weekly_demand * lead_time_weeks
  // Demand variability accumulates as the square root of the lead time,
  // which is why halving the lead time does not halve the safety stock.
  sigma_over_lead_time: = demand_sd_weekly * sqrt(lead_time_weeks)
  service_level: 98%
  z: 2.054 source "Standard normal one-sided 98%"
  safety_stock: = z * sigma_over_lead_time
  reorder_point: = demand_over_lead_time + safety_stock

  average_stock: = cycle_stock + safety_stock
  weeks_of_cover: = average_stock / weekly_demand
  annual_holding_cost: = average_stock * holding_cost
  annual_ordering_cost: = orders_per_year * order_cost
  total_policy_cost: = annual_holding_cost + annual_ordering_cost
  silo_capacity: 45000
  headroom: = silo_capacity - (reorder_point + economic_order_quantity - demand_over_lead_time)
}

// What a shorter lead time would be worth, since the supplier has offered
// a fortnightly slot at a price.
policy_option shorter_lead_time {
  of: pale_malt
  label: "Fortnightly contracted slot — lead time 14 days"
  lead_time: 14 d
  lead_time_weeks: = lead_time / 7[d]
  demand_over_lead_time: = pale_malt.weekly_demand * lead_time_weeks
  sigma_over_lead_time: = pale_malt.demand_sd_weekly * sqrt(lead_time_weeks)
  safety_stock: = pale_malt.z * sigma_over_lead_time
  reorder_point: = demand_over_lead_time + safety_stock
  safety_stock_released: = pale_malt.safety_stock - safety_stock
  annual_saving: = safety_stock_released * pale_malt.holding_cost
  supplier_premium: 900
  net_benefit: = annual_saving - supplier_premium
  verdict: "Refuse it. A shorter lead time releases about 1.2 t of safety stock, and malt is cheap enough to hold that the released stock is worth GBP 150 a year against a GBP 900 premium. The same fourteen days on a hop contract, where the unit cost is forty times higher, would be worth signing."
}

policy_option higher_service {
  of: pale_malt
  label: "99.5% cycle service level — no stockout in a brewing year"
  z: 2.576 source "Standard normal one-sided 99.5%"
  safety_stock: = z * pale_malt.sigma_over_lead_time
  additional_stock: = safety_stock - pale_malt.safety_stock
  additional_cost: = additional_stock * pale_malt.holding_cost
  verdict: "About 8 days of extra cover for roughly the cost of one lost brew. Worth it only if a stockout genuinely stops the brewhouse, which it does: there is no second malt."
}

supply_chain malt_supply {
  product: "Maris Otter pale ale malt, bulk"
  currency: "GBP"
  title: "Malt supply into the brewhouse"
}

sc_node maltings {
  name: "Warminster Maltings"
  role: "supplier"
  location: "Wiltshire"
  capacity: 4000000
}

sc_node haulier {
  name: "Bulk tanker haulage"
  role: "supplier"
  location: "Contracted, 2 slots per week"
  capacity: 120000
}

sc_node silo {
  name: "Brewery malt silo, 45 t"
  role: "warehouse"
  location: "Bermondsey"
  capacity: 45000
}

sc_node brewhouse {
  name: "40 hL brewhouse"
  role: "factory"
  location: "Bermondsey"
  capacity: 10000
}

sc_link malt_to_haulier {
  from: maltings
  to: haulier
  lead_time_days: 14
  cost_per_unit: 0.02
  mode: "truck"
}

sc_link haulier_to_silo {
  from: haulier
  to: silo
  lead_time_days: 7
  cost_per_unit: 0.04
  mode: "truck"
}

sc_link silo_to_brewhouse {
  from: silo
  to: brewhouse
  lead_time_days: 1
  cost_per_unit: 0.01
  mode: "auger"
}

note eoq_assumption {
  text: "EOQ assumes demand is steady and the whole order lands at once, which is true here and often is not. It is also famously flat near the optimum: ordering 25000 or 36000 kg instead of the computed 30000 costs under 2% of the policy cost, so round to whole tanker loads without guilt."
  anchor: pale_malt
}

view chain: supply_map