Multiple transactions
Stock transaction service is transactional, so transactions are immutable, and a single transaction updates a single stock type. Some operations therefore result in several transactions — and in a transfer, in two different business units.
They are published as separate events, linked by linkIdRelated, which every transaction produced from the same
operation shares. A consumer that needs to see an operation as a whole should group on that field.
Stock transfer
Sending the goods — one operation, three transactions:
| # | Business unit | Stock type | Change | Why |
|---|---|---|---|---|
| 1 | A (sending) | SalesStock | decrease | no longer available to sell |
| 2 | A (sending) | InTransit | increase | on its way, still A's stock |
| 3 | B (receiving) | InOrder | increase | incoming, visible to B before it arrives |
Receiving the goods — the receiving store performs a stock transfer received operation, producing three more:
| # | Business unit | Stock type | Change | Why |
|---|---|---|---|---|
| 4 | A (sending) | InTransit | decrease | no longer in transit |
| 5 | B (receiving) | InOrder | decrease | no longer incoming |
| 6 | B (receiving) | SalesStock | increase | available to sell |
Note that the goods remain accounted for at every step. Between transactions 1 and 6 they are neither sellable at A
nor sellable at B, but they are never invisible — they sit in InTransit and InOrder.
The receiving store's SalesStock grows by exactly what arrived, whatever it already held — the 4 coffees Uppsala had
before are not touched. A receipt can be corrected by sending it again for the same transfer: the correction
moves SalesStock by the difference from what that transfer received before. Receiving 10, selling 1 and then
correcting the receipt to 15 adds 5, not 6 — what was sold in between does not count.
Store transfer
A store transfer adds a reservation step, so goods can be committed to a transfer while still physically present in the sending store.
Initiating the transfer:
| Business unit | Stock type | Change | Why |
|---|---|---|---|
| A (sending) | Reserved | increase | committed to the transfer, must not be sold |
| B (receiving) | InOrder | increase | incoming |
Completing the transfer — when the goods actually leave A:
| Business unit | Stock type | Change | Why |
|---|---|---|---|
| A (sending) | SalesStock | decrease | has now left the store |
| A (sending) | InTransit | increase | on its way |
| A (sending) | Reserved | released in full | the whole reservation is released, whatever quantity shipped |
| B (receiving) | InOrder | set to the quantity shipped | no longer expects what did not ship |
The receiving store then performs a stock transfer received operation, exactly as above.
Why the extra step matters: the quantity actually shipped may differ from the quantity requested. The completion reconciles both the reservation at A and the incoming quantity at B against what really left, so a partial transfer does not leave a permanent phantom reservation or a permanent phantom incoming quantity.
Sales that touch a reservation
A sale submitted through the Input API (POST /business-units/{businessUnitId}/receipts) can name the reservation it
fulfils in reservationId. It then releases the sold quantity from Reserved (or ReservedInOrder) as well as
reducing SalesStock, so the same grouping applies — one receipt line, two transactions, sharing linkIdRelated.
Sales from the POS arrive as PosLog, which carries no reservation, so they reduce SalesStock only.
Note: The transactions are immutable, so any adjustment of previously created transactions needs to be handled by creating a delta transaction. This is also how movements that arrive out of sequence are corrected — see Delayed transactions.