Delayed transactions
For some types of transactions, the sequence of the transactions is important to get the calculations done correctly. However, since some transactions might be delayed for a reason, the system needs to handle this when processing the transactions.
The case that matters is a stock count, because a count does not describe a change — it asserts an absolute truth about a point in time. A movement that happened before the count but arrives after it — a sale from a till that was offline, a delivery registered late — is already reflected in the counted quantity. It must not be applied on top of it, or the same goods are removed (or added) twice.
Example:
- 10:00, the system quantity is 12
- 11:00, business unit performs a stock count, counted quantity is 10
- System calculates a loss/gain based on whether the counted quantity deviates from the system quantity, in this case, it's a loss of 2 items
- System calculates new stock quantity = 12 - 2 (loss) = 10
- 11:05, receives a sale performed at 11:05, sales quantity is 1
- System calculates new stock quantity = 10 - 1 (sale) = 9
- 11:10, receives a sale performed at 10:30, sales quantity is 2
- The sales transaction is actually done prior to the stock count, so the transaction should not affect the current stock quantity.
- On the other hand, the stock count performed at 11:00 reported a loss of 2 items, so the system creates a gain of +2 items to adjust the previously reported loss. In addition, the sales transaction is stored, i.e. reducing the stock by -2 items, so the result for both transactions is no change in stock quantity:
- System calculates new stock quantity = 9 + 2 (adjust/nullify loss reported for stock count) - 2 (sale) = 9
Note: The transactions are immutable, so any adjustment of previously created transactions needs to be handled by creating a delta transaction.
What this looks like to a consumer
The correction is published like any other transaction, so a subscriber sees three transactions from the example above: the original count from earlier, then the late sale and the compensating gain.
| Transaction | operationType | quantity | transactionDate |
|---|---|---|---|
| the original count | STOCK_COUNT | −2 | 11:00 |
| the late sale | SALES | −2 | 10:30 |
| the compensating gain | STOCK_COUNT | +2 | 11:00 |
The late movement and its compensation always cancel out: their quantities sum to zero, and so do their costs. These fields identify what is happening:
| Field | Use |
|---|---|
transactionDate | when the movement actually happened — 10:30 for the late sale. The compensating transaction carries the count's date, 11:00 |
linkIdDelayed | set on both the late movement and its compensating transaction, to the transactionId of the stock count transaction they relate to. Empty on every other transaction |
linkIdRelated | the same value on the late movement and its compensation, so the pair can be grouped as one operation |
documentType / documentId | on the compensating transaction, StockCount and the id of the count it corrects |
transactionDate can therefore be older than that of a transaction you already received. A consumer that
assumes the stream is ordered by business date will misread this. Order by timestamp — when STP processed the
movement — and treat transactionDate as data. See
External Events.
Scope
Compensation applies to every movement of sellable stock (SalesStock) that is dated before the item's most
recent stock count — sales, deliveries, stock corrections and transfers alike. It is always made against that most
recent count, because that count is the latest statement of what was there.
It does not apply to the operations that themselves assert an absolute quantity — a stock count or a synchronize stock. Those are never compensated.
A movement with no stock count after its date needs none of this. Ordinary movements are deltas, so a late arrival simply applies its delta, and the total comes out right whatever order the movements were processed in.