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.
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
- 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
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
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);
});
}