Skip to content
FlowScript templates

Business — throughput accounting at the constraint

Throughput accounting for a machine shop, where the product mix is decided by throughput per constraint minute rather than by margin per unit — and the difference between the two is priced. Typed FlowScript for business strategy. Keywords: OKR, objective, key result, funnel.

Template previewFlowScript
Conversion funnel5 steps · base n = 16,065Constraint minutes available — 21 days, 2 shifts, 85% OEE16,065100.0% of baseAfter 220 aerospace brackets at GBP 4.46 per constraint minute10,78567.1% of base▲ 67.1% step convAfter 150 medical housings at GBP 3.764,63528.9% of base▲ 43.0% step convAfter 60 motorsport uprights at GBP 3.539155.7% of base▲ 19.7% step convAfter 9 of the 40 manifolds at GBP 2.79 — the constraint runs out here510.3% of base▲ 5.6% step conv

Make it your own.

// Throughput accounting for a machine shop, where the product mix is
// decided by throughput per constraint minute rather than by margin per
// unit — and the difference between the two is priced.
//
// The constraint is one 5-axis turn-mill. Everything the shop can sell has
// to pass through it, so a minute of that machine is the only genuinely
// scarce thing in the business. Ranking work by margin per unit is the
// instinct every quoting system encourages, and here it costs about GBP
// 4700 a month, which is a third of the net profit.
//
// Cost per constraint minute is deliberately built from the minutes read
// as a plain number — constraint_minutes / 1[min] — because dividing money
// by a duration would produce a unit no report can print and no currency
// the unit table has.

constraint_system machining_cell {
  title: "Contract machining cell — March 2026"
  constraint: "DMG MORI NTX 2000 5-axis turn-mill, single machine"
  currency: "GBP"
  period: "March 2026, 21 working days"
  working_days: 21
  shifts_per_day: 2
  shift_length: 450 min
  // Availability at the constraint, measured: setup, first-off inspection
  // and unplanned stops are all already inside this.
  oee: 0.85 source "Machine monitoring, rolling 13 weeks"
  constraint_minutes: = working_days * shifts_per_day * shift_length * oee
  constraint_hours: = constraint_minutes / 60[min]
  operating_expense: 48000
  cost_per_constraint_hour: = operating_expense / constraint_hours
}

product_line aero_bracket {
  of: machining_cell
  title: "Aerospace bracket, 7075-T6"
  price: 148
  material_cost: 34
  outsourced_cost: 7
  variable_cost: = material_cost + outsourced_cost
  throughput: = price - variable_cost
  constraint_time: 24 min
  throughput_per_constraint_minute: = throughput / (constraint_time / 1[min])
  demand: 220
  minutes_demanded: = demand * constraint_time
  build: 220
  minutes_used: = build * constraint_time
  throughput_earned: = build * throughput
}

product_line medical_housing {
  of: machining_cell
  title: "Medical pump housing, 316L"
  price: 212
  material_cost: 44
  outsourced_cost: 14
  variable_cost: = material_cost + outsourced_cost
  throughput: = price - variable_cost
  constraint_time: 41 min
  throughput_per_constraint_minute: = throughput / (constraint_time / 1[min])
  demand: 150
  minutes_demanded: = demand * constraint_time
  build: 150
  minutes_used: = build * constraint_time
  throughput_earned: = build * throughput
}

product_line motorsport_upright {
  of: machining_cell
  title: "Motorsport upright, 7075-T7351"
  price: 340
  material_cost: 96
  outsourced_cost: 25
  variable_cost: = material_cost + outsourced_cost
  throughput: = price - variable_cost
  constraint_time: 62 min
  throughput_per_constraint_minute: = throughput / (constraint_time / 1[min])
  demand: 60
  minutes_demanded: = demand * constraint_time
  build: 60
  minutes_used: = build * constraint_time
  throughput_earned: = build * throughput
}

product_line hydraulic_manifold {
  of: machining_cell
  title: "Hydraulic manifold, EN24T"
  price: 415
  material_cost: 118
  outsourced_cost: 29
  variable_cost: = material_cost + outsourced_cost
  throughput: = price - variable_cost
  constraint_time: 96 min
  throughput_per_constraint_minute: = throughput / (constraint_time / 1[min])
  demand: 40
  minutes_demanded: = demand * constraint_time
  // The marginal product: it has the highest throughput per unit and the
  // lowest per constraint minute, so it is the one that gets cut.
  build: 9
  minutes_used: = build * constraint_time
  throughput_earned: = build * throughput
}

mix_result throughput_first {
  of: machining_cell
  label: "Mix ranked by throughput per constraint minute"
  minutes_demanded: = sum(kind:product_line, "minutes_demanded") * 1[min]
  minutes_committed: = sum(kind:product_line, "minutes_used") * 1[min]
  minutes_idle: = machining_cell.constraint_minutes - minutes_committed
  overload: = pct(minutes_demanded, machining_cell.constraint_minutes)
  // Named line by line. The two roll-ups above it work because their
  // attributes are one step from a literal; throughput_earned is three
  // (material and outsourced cost make variable cost, price less variable
  // cost makes throughput, build times throughput makes this), and the
  // aggregate runs before a chain that deep resolves — so the selector
  // matches nothing and the total silently comes out zero. A zero total
  // in a throughput-accounting figure is the one number that must never
  // be quietly wrong.
  total_throughput: = hydraulic_manifold.throughput_earned + motorsport_upright.throughput_earned + medical_housing.throughput_earned + aero_bracket.throughput_earned
  net_profit: = total_throughput - machining_cell.operating_expense
  return_per_constraint_hour: = total_throughput / machining_cell.constraint_hours
}

// The same capacity filled highest-margin-first, which is what the quoting
// system recommends today.
mix_result margin_first {
  of: machining_cell
  label: "Mix ranked by throughput per unit"
  manifolds: 40
  uprights: 60
  housings: 150
  brackets: 98
  minutes_committed: = (manifolds * hydraulic_manifold.constraint_time) + (uprights * motorsport_upright.constraint_time) + (housings * medical_housing.constraint_time) + (brackets * aero_bracket.constraint_time)
  total_throughput: = (manifolds * hydraulic_manifold.throughput) + (uprights * motorsport_upright.throughput) + (housings * medical_housing.throughput) + (brackets * aero_bracket.throughput)
  net_profit: = total_throughput - machining_cell.operating_expense
  penalty: = throughput_first.total_throughput - total_throughput
  penalty_share_of_profit: = pct(penalty, throughput_first.net_profit)
}

// An hour recovered at the constraint is worth a whole hour of throughput;
// an hour saved anywhere else is worth nothing at all.
elevation_option setup_reduction {
  of: machining_cell
  label: "Single-minute exchange of die on the turn-mill: 18 setups per month, 22 minutes saved each"
  setups_per_month: 18
  minutes_saved_each: 22 min
  minutes_recovered: = setups_per_month * minutes_saved_each
  extra_manifolds: = floor(minutes_recovered / hydraulic_manifold.constraint_time)
  throughput_gained: = extra_manifolds * hydraulic_manifold.throughput
  project_cost: 6500
  payback_months: = project_cost / throughput_gained
}

// The month's constraint minutes, spent in the order the ranking implies.
// Each step is the previous one less the minutes that product takes, so
// the chart is the mix decision rather than an illustration of it.
funnel_step capacity_available {
  n: = machining_cell.constraint_minutes
  title: "Constraint minutes available — 21 days, 2 shifts, 85% OEE"
}

funnel_step after_brackets {
  n: = capacity_available.n - aero_bracket.minutes_used
  from: capacity_available
  title: "After 220 aerospace brackets at GBP 4.46 per constraint minute"
}

funnel_step after_housings {
  n: = after_brackets.n - medical_housing.minutes_used
  from: after_brackets
  title: "After 150 medical housings at GBP 3.76"
}

funnel_step after_uprights {
  n: = after_housings.n - motorsport_upright.minutes_used
  from: after_housings
  title: "After 60 motorsport uprights at GBP 3.53"
}

funnel_step unsold_capacity {
  n: = after_uprights.n - hydraulic_manifold.minutes_used
  from: after_uprights
  title: "After 9 of the 40 manifolds at GBP 2.79 — the constraint runs out here"
}

cite goldratt {
  key: "goldratt1992"
  title: "The Goal: A Process of Ongoing Improvement, 2nd revised edition"
  authors: "Eliyahu M. Goldratt and Jeff Cox"
  publisher: "North River Press"
  year: 1992
  type: "book"
  of: machining_cell
}

note inventory_measure {
  text: "Throughput accounting has three measures and this document uses two. The third, investment, is what the shop has tied up in material and work in progress; it does not change the mix decision but it does change whether the manifold work is worth quoting at all, because its material cost sits on the floor for eleven days before the machine sees it."
  anchor: machining_cell
}

view capacity: funnel(capacity_available)

caption capacity_caption {
  of: capacity
  title: "Where the constraint's month goes"
  text: "Constraint minutes spent in descending order of throughput per constraint minute. The manifold is the marginal product: 31 of the 40 units demanded do not get made, and that is the correct decision — made in the other order it costs about GBP 4700 of throughput."
  n_statement: "Basis: 16 065 constraint minutes, March 2026."
  style: journal
}