PRD: Stock Reservation & Oversell Guard
| Module | Inventory | PRD ID | PRD-RSV-001 |
| Status | No item | FEAT | RSV |
| Epic | — | Plane | — |
| Date | 2026-07-13 | Version | v1.0 |
| Packages | @nx/inventory | URD | RSV |
| Surface | BackendClient · Owner/Mgr | ||
| Owner | Phát Nguyễn | ||
What a reservation is
A merchant can hold goods in several inventory locations, and each item at each location carries four stock numbers.
A reservation is a promise to set aside N units of an item for a specific holder: a sale order, a stock transfer ticket, or a production order. The goods are still on the shelf, but nobody else can sell them. The moment a cashier taps an item into an order on the POS, the system holds the matching stock.
The four stock numbers
Quoted from Stock Locations & Levels so nobody has to open another PRD. Each item at each location carries four numbers:
| Number | Meaning |
|---|---|
| On-hand | The goods physically in the warehouse |
| Reserved | Already promised to another document, no longer sellable to anyone else |
| Available | The part that can still be sold |
| Incoming | Already left the source location, not yet arrived at the destination |
The invariant, true at every moment:
Available = On-hand - ReservedWhy overselling is the most expensive mistake
Overselling means promising the same item to two customers. The second one pays, then finds out it is gone, and the merchant has to apologise and refund. It happens because the system looks at a single "how much stock is left" number instead of "how much can still be sold".
Reservation closes that gap. Stock is held the instant an item lands on the order, not at payment, so two cashiers on two POS terminals cannot both sell the last can of soda. The same mechanism serves stock transfers (goods already promised to another location) and production orders (materials already promised to a batch).
The second half is traceability. One aggregate number, "3 reserved", says nothing about who is holding the stock or where to unstick it, so the owner sees stock stuck and cannot act on it. That is why every reservation carries the name of its holder.
The last can of soda, from cart to payment
Coke at the bar counter: on-hand 10, nothing reserved. Two POS terminals sell at once.
| What happens | On-hand | Reserved | Available |
|---|---|---|---|
| POS 1 taps 3 cans into order #123 | 10 | 3 | 7 |
| POS 2 taps 8 cans into order #124 | 10 | 3 | 7 |
| POS 2 taps 7 cans into order #124 instead | 10 | 10 | 0 |
| The customer on order #123 drops 1 can | 10 | 9 | 1 |
| Order #123 is paid, 2 cans leave the shelf | 8 | 7 | 1 |
- POS 2's request for 8 cans is refused right in the cart: available was only 7 at that moment. The customer has not paid, and the cashier can still offer something else.
- A reservation does not move on-hand. On-hand only drops when the order completes and the goods really leave the shelf.
- Open Coke's reservation detail at this point: order #124 is holding 7 cans. Wherever stock is stuck, that is where it can be unstuck.
The easiest thing to get wrong
Inventory never releases stock on a timer
There is no countdown and no time-based expiry: an order left open overnight still holds its stock.
Inventory does not know whether that order is alive or dead. An order left open for 2 hours might be a table halfway through a meal. Only whoever created the reservation knows, so that is who commands the release: a sale order when it is voided or completed, a stock transfer when goods leave the source location, a production order when the batch finishes or is cancelled.
The release happens quietly, with no notification. If an abandoned order is holding stock, the owner finds it on the reservation detail screen and voids it.