Soft holds in an ERP are a lie. We fixed that structurally — with a first-class reservation record the engine enforces.
The problem with soft holds
Every ERP we looked at stores a "reserved" quantity as a column on the stock row. This is a scalar — it carries no timestamp, no priority, no expiry, and no owner. Two orders can both read 10 units available, both reserve 8, and the engine happily hands out 16 units it only has 10 of.
Reservations vs. allocations
We distinguish: a reservation is a promise at order confirmation (binding but not yet physically staged); an allocation is a physical pick ticket. The reservation record carries an expiry and a priority rank. When the reservation expires or the order is cancelled, the engine releases the hold automatically.
What the data model looks like
The Reservation table has a composite primary key of (stock_location_id, part_id, reservation_id). The quantity column is signed — positive for holds, negative for releases — so the ledger is append-only and fully auditable. ATP queries sum the ledger in a single aggregation.
Impact on MRP
MRP now consumes reservations directly. The projected available balance curve accounts for expiring holds, giving planners a more accurate picture of when to trigger replenishment. In our pilot, overcommits dropped by 94%.