Offline-first POS sync for multi-branch retail

← Blog
  • erp
  • postgresql
  • distributed-systems
  • typescript

Retail branches in the Philippines rarely fail neatly. A fiber line drops mid-shift while three cashiers have queues at the counter, or power flickers during end-of-day Z-reading generation. If a point-of-sale terminal blocks on a cloud round-trip to print a receipt, store operations halt immediately.

When I architected Cirquolus ERP, I treated network partitions as the normal operating state rather than an exception.

Append-only register journals

Instead of mutating stock counts and drawer balances directly over REST calls, each branch terminal writes every sale, void, and cash movement as an immutable, deterministic event in a local journal keyed by terminal ID, shift sequence number, and hardware clock watermark.

Receipts and BIR-compliant invoice series are allocated from pre-leased terminal ranges. A register can operate offline for an entire weekend without colliding with adjacent lanes or another branch.

Reconciling stock and shifts in PostgreSQL

When connectivity returns, the terminal flushes its journal batches to the central Node.js and PostgreSQL cluster:

  1. Idempotent ingestion: Each batch is committed inside a serializable transaction using (terminal_id, sequence_id) uniqueness constraints, making retries safe across flaky connections.
  2. Ledger-derived inventory: Central stock levels are computed from reconciled journal entries rather than last-write-wins row overwrites, surfacing physical shrinkage separately from in-flight offline sales.
  3. Automated shift audit: End-of-day cashier declarations are matched against journal totals automatically, flagging only variance exceptions for finance review.

What changed in production

Moving from manual spreadsheet tallying and fragile live-only forms to an append-only sync pipeline cut monthly financial reconciliation time by 60% while sustaining 99.98% production uptime across retail branches.