Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.laportenard.com/llms.txt

Use this file to discover all available pages before exploring further.

Think of a busy restaurant with tablets at the front door, waiters walking around with handhelds, a cashier terminal, and a kitchen screen. Every device needs to see the same orders in real time. When a waiter adds a burger to Table 5, the kitchen screen needs to know immediately — and so does the cashier. That’s the problem the sync protocol solves: keeping every device in sync, without letting two people step on each other’s changes.

The three pieces

Terminals (the students)

Tablets and computers running the POS app. Each is a “terminal.” They take orders, update tables, and process payments.

Hub (the teacher)

A Node.js server running on-site (Raspberry Pi). Every terminal connects via WebSocket. The hub decides who edits what, stores every order, and broadcasts updates.

Odoo (the principal's office)

The cloud business system. The hub batches order updates to Odoo every 5 seconds. Odoo holds the permanent record; the hub handles real-time traffic locally.

Step by step

1

Logging in (AUTH)

A terminal starts up and sends AUTH { token, deviceId }. The hub validates the token against Odoo and replies with AUTH_OK or AUTH_FAIL. On success, the hub immediately sends a SYNC_INIT message — a full dump of all active orders so the terminal starts with accurate data.
2

Locking a table (TABLE_LOCK)

Before a waiter can work a table, their terminal asks: TABLE_LOCK_ACQUIRE { tableId }. The hub checks:
  • FreeTABLE_LOCK_GRANTED
  • Held by another terminalTABLE_LOCK_DENIED { lockedBy }
The terminal sends TABLE_LOCK_HEARTBEAT every 15 seconds to keep the lock alive. If a terminal crashes and stops heartbeating, the lock auto-expires after 45 seconds. A manager can force-take any lock with TABLE_LOCK_FORCE_ACQUIRE.
3

Editing an order (LEASE)

Once on the order page, the terminal needs a lease — like borrowing a library book. It sends LEASE_ACQUIRE { posReference, baseVersion }. The hub grants it with a version number and expiry time. Only the lease holder can send snapshots for that order.The terminal heartbeats every 20 seconds (LEASE_HEARTBEAT). The lease expires after 60 seconds without one.
4

Saving changes (ORDER_SNAPSHOT)

Every edit — adding an item, changing quantity, applying a discount — sends the complete current order as ORDER_SNAPSHOT. The hub:
  1. Checks the version number matches (no concurrent edits slipped through)
  2. Saves the snapshot and bumps the version
  3. Broadcasts ORDER_UPDATED to all other connected terminals
If the version doesn’t match (two terminals somehow edited the same order), the hub returns a CONFLICT message with its authoritative snapshot.
5

Releasing the lease

When the waiter navigates away or the order is paid, the terminal sends LEASE_RELEASE { posReference, finalSnapshot? }. The hub saves the final snapshot (if provided) and frees the order for other terminals.
6

Forwarding to Odoo

Every 5 seconds, the hub drains up to 20 recent order events and forwards them to Odoo’s /pos-api/v1/orders/{create,update} endpoints. Order creation is idempotent by pos_reference — re-forwarding is always safe. If Odoo is temporarily down, the hub keeps the event log and retries.

What’s in an order snapshot

An ORDER_SNAPSHOT is a complete photo of an order at a point in time:
FieldContent
IdentityposReference, id, odooId
AssignmenttableId, partnerId
Statusopen / sent / changed / paid
LinesEach item: product, qty, price, discount, taxes, void info, kitchen sent time
PaymentsPayment method, amount, change
TotalsSubtotal, tax, total
MetaupdatedAt, terminalId, notes

Table status

Tables sync separately from orders. A TABLE_UPDATE message sets a table’s status (available, occupied, or reserved) and the hub broadcasts TABLE_UPDATED to all terminals instantly.

Why this design

Without the protocol:
  • Two waiters could edit the same order — one would silently overwrite the other
  • A crashed terminal could hold a lock forever, blocking the whole table
  • The kitchen would miss order updates
  • The cashier would see stale totals
With the protocol, each of these is covered:
ProblemSolution
Concurrent editsOrder leases — only one writer at a time
Table conflictsTable locks — only one terminal per table
Stale dataBroadcast on every change — all terminals update instantly
Crashed terminalsHeartbeat expiry — locks and leases auto-release
Odoo downtimeHub event log — changes queue and forward when Odoo recovers

Key files

FilePurpose
nu_pos_sync_protocol/src/messages.tsAll message types (Terminal→Hub and Hub→Terminal)
nu_pos_sync_protocol/src/snapshots.tsOrderSnapshot, TableStatus schemas
nu_pos_sync_protocol/src/constants.tsTTLs, heartbeat intervals
nu_pos_sync_protocol/src/validators.tsZod schemas for runtime validation
nu_pos_hub/Hub server: WebSocket broker, lease manager, table lock manager, Odoo forwarder
nu_pos_react/src/realtime/Frontend transport and sync bridge