Answers "which of my 100 tills are alive and healthy", and fixes three things
that were fine on one device and broken on a hundred.
Terminal identity (lib/data/local/terminal_identity.dart)
- Every device mints a UUID on first run, stored in its own database, plus a
short code (T4A9) derived from it. Renaming keeps the device id, so history
keeps pointing at the same physical till.
- Replaces the literal 'TERM-01', which was hardcoded in five places. The whole
fleet reported as one terminal: shift reports merged, MQTT topics collided,
and a second connection with the same client id evicts the first from the
broker — so two tills would have knocked each other offline in a loop.
Invoice numbers now carry the terminal code
- INV-2608-T4A9-00042. The sequence counter lives in each till's own database
and starts at 1, so without this every terminal in the fleet minted
INV-2608-00001 for its first sale of the month. The order UUID kept the data
distinct; the number a customer quotes on a receipt was not.
SQLite pragmas
- WAL, so the product grid refreshing does not block the sale being written,
and the file is never left mid-rewrite by a power cut.
- busy_timeout 5s, so a contended lock waits instead of throwing "database is
locked" — which at checkout is a failed sale with a customer standing there.
- synchronous NORMAL, the right trade under WAL for a till.
Fleet presence (lib/data/sync/presence_reporter.dart)
- Retained status record on connect and once a minute: device id, code, name,
app version, pending bill count, last upload, catalogue revision, sync halt
state. Retained so a dashboard connecting at noon gets all 100 terminals
immediately rather than a blank board.
- The Last Will already said "reachable". A till can be connected and still be
holding 200 unsent bills or running last month's prices; only pending_bills
and catalogue_revision say so.
NATS
- The MQTT gateway maps / to . so the existing transport works unchanged.
SyncConfig.asNatsSubject() exposes the translation, and the contract doc
gives the JetStream subjects (pos.*.*.order, pos.*.*.status) plus the two
server-side requirements: a file-backed stream, and the ack published by the
consumer after commit rather than by the ingest handler.
Tests: 129 -> 140. New coverage for identity minting and stability, per-device
invoice uniqueness, topic and client-id separation, NATS subject mapping, and
the two pragmas. Suite run three times clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>