Answer the catalogue as a delta when the terminal sends a revision
Every pull was a full snapshot, so a shop with a thousand products re-sent all of them to correct one price. The response now carries a revision the terminal stores and hands back, and a pull that supplies one gets only what moved: the product row, its row at that outlet, or its stock ledger. Stock is included because a shop's count drifts from a till's on every sale rung at another counter, and a delta that ignored it would let that drift persist until someone forced a full pull. The dangerous part is the flag, not the filter. A response marked is_delta:false tells the terminal to withdraw every product it does not mention — so a filtered result carrying that label empties the shelf. Both are now derived from one value, and there is no path through the function that filters without also setting the flag. Everything ambiguous resolves toward the snapshot. A revision that is malformed, empty, or issued to another outlet yields a zero cutoff and a complete response; the opposite would leave a terminal permanently missing changes with nothing to show for it. The revision advances only on the final page, so a terminal that abandons a paginated pull cannot end up holding one that claims it saw pages it never received. And the stamp is taken a second in the past, because a product written during the same second the query ran would otherwise fall on the wrong side of the next cutoff and be skipped for good. A delta still cannot withdraw a deleted product — removing a row from productlocations leaves no tombstone — so a periodic pull without a revision is what collects those. Verified against the live outlet: a full pull of 12, a delta returning only the one product whose price had changed, and pagination that stays exact now that productid <= 0 is excluded in SQL rather than after the LIMIT. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -192,13 +192,45 @@ Worth knowing before the first bill lands.
|
||||
Registrations are **insert-if-absent** — never an update, so a profile
|
||||
corrected at head office is not reverted by a terminal replaying an old
|
||||
capture.
|
||||
- **Catalogue** answers `is_delta: false` and is therefore a full snapshot. The
|
||||
terminal withdraws every product a snapshot omits, so this must stay true
|
||||
while the query returns everything stocked at the outlet.
|
||||
- **Catalogue** answers a snapshot or a change set, decided by the `since`
|
||||
revision — see below.
|
||||
- **Barcodes** come from `products.productsku` — there is no barcode column.
|
||||
Scanning at the till matches on it, so SKUs must be the scannable code for
|
||||
barcode scanning to work.
|
||||
|
||||
## Catalogue: snapshots and deltas
|
||||
|
||||
`GET /catalogue?store_id=1135` with no `since` returns a **full snapshot**. The
|
||||
response carries a `revision`; the terminal stores it and sends it back next
|
||||
time as `since=`, and then gets only what changed.
|
||||
|
||||
A product is included in a change set when any of three things moved: the
|
||||
product row (name, tax, brand), its row at this outlet (price, availability), or
|
||||
its stock ledger. Stock counts because a shop's figure drifts from a till's on
|
||||
every sale rung at another counter, and a delta that ignored it would let that
|
||||
drift persist until someone forced a full pull.
|
||||
|
||||
**The one rule that matters.** A response marked `is_delta: false` is treated as
|
||||
a snapshot, and the terminal **withdraws every product it does not mention**. A
|
||||
filtered result labelled `false` therefore empties the shop's shelf. The filter
|
||||
and the flag are computed from a single value in `Catalogue()` — there is no
|
||||
path that filters without also setting the flag, and that is deliberate.
|
||||
|
||||
**A revision that cannot be read falls back to a full snapshot.** Malformed,
|
||||
empty, or issued to a different outlet — all yield a zero cutoff and a complete
|
||||
response. The other direction would leave a terminal permanently missing every
|
||||
change it had not already seen, with nothing to indicate it.
|
||||
|
||||
**The revision only advances on the final page.** A terminal that abandons a
|
||||
paginated pull half way gets back the revision it already had — or an empty one,
|
||||
meaning the next pull is a snapshot. Both are recoverable; a prematurely
|
||||
advanced revision is not.
|
||||
|
||||
**A delta cannot withdraw a deleted product.** A row removed from
|
||||
`productlocations` leaves no tombstone, so nothing tells the change set to
|
||||
retire it. Only a snapshot collects those, which is why a terminal should pull
|
||||
without a revision periodically — the morning import is the natural moment.
|
||||
|
||||
## Terminal health
|
||||
|
||||
Every till publishes a heartbeat to `nearle/pos/{loc}/{terminal}/health` every
|
||||
@@ -255,8 +287,6 @@ state.
|
||||
|
||||
## Not built
|
||||
|
||||
- **Catalogue deltas.** Every pull is a full snapshot. Fine for a few hundred
|
||||
products, worth revisiting at a few thousand.
|
||||
- **Loyalty coming back down.** The uplink deliberately carries no points or
|
||||
spend — those belong to the bill stream, which is idempotent and sees every
|
||||
counter. Nothing yet computes them centrally and sends them to the tills, so
|
||||
|
||||
Reference in New Issue
Block a user