Technical Lab · Synthetic demonstration

Constrained procurement

The forecast says what is short. The order is whatever remains feasible after the pack, the minimum and the budget.

All technical examples

Synthetic demonstration · Independent educational implementation. Not an employer purchasing rule.

Choose an order quantity for each SKU when packs, minimum orders and a shared budget cannot all be ignored.

A forecast gap of a few units can still force a much larger purchase, or none, once the supplier’s minimum and the remaining budget are applied.

Spend the budget

  • SKU-01 order 36 · covered
  • SKU-02 order 0 · short 14
  • SKU-03 order 0 · short 26
  • SKU-04 order 15 · covered
  • SKU-05 order 0 · covered
  • On hand
  • Order, white ticks are packs
  • Unmet forecast
  • Forecast
  • Minimum order
Each row compares the forecast with stock plus the chosen order. A positive order is whole packs and at least the minimum. Rows share one scale in units.
The budget is allocated by SKU. Pack and minimum rules leave a remainder that cannot buy another valid order.
Spend
234 of 240
Unmet units × 9
40
Surplus units × 1.5
0
Objective
360.0

Budget 240. The search orders SKU-01 × 36, SKU-04 × 15 and spends 234. Unmet forecast demand is 40 units and surplus is 0. The objective, 9 × unmet + 1.5 × surplus, equals 360.0. SKU-02, SKU-03, SKU-05 are not ordered. A positive quantity is a multiple of the pack size and at least the minimum order. This is a synthetic single-period plan, not a purchasing result.

Solver: exact enumeration for this small teaching instance

Across the three budgets

More budget buys down unmet demand: 64, then 40, then 13 units. Surplus stays at 0 on this instance. Each point is the exact best plan for that budget.

The model

Objective Minimise 9 × unmet units + 1.5 × surplus units. Constraints Quantity is 0, or at least the pack-rounded minimum. Quantity is a multiple of pack size. Total cost stays within budget.

A CP-SAT model could encode these constraints. This page does not run OR-Tools or CP-SAT. It enumerates the feasible quantities and checks the same rules.

src/lib/lab/procurement.ts View source on GitHub: src/lib/lab/procurement.ts

Feasibility is the budget and the allowed quantities. The objective is scored only on plans that pass.src/lib/lab/procurement.ts
export function planIsFeasible(problem: ProcurementProblem, plan: ProcurementPlan): boolean {
  if (plan.spend > problem.budget + 1e-9) return false;
  return plan.items.every((planned, index) => {
    const item = problem.items[index];
    const options = orderOptions(item);
    return options.includes(planned.quantity);
  });
}