Adds _push_stats_snapshot() to cloud_sync.py. Every 5 minutes
(piggybacked on the existing _connect_loop push tick alongside the
menu snapshot) it queries the local DB and POSTs a JSON stats blob
to POST /api/remote/snapshot (site API key auth).
Stats collected:
- open_tables: count of open/partially_paid POS orders
- today_revenue: sum of active+paid item prices on orders
closed today
- today_orders: count of paid/closed POS orders today
- online_orders_pending: online orders awaiting acceptance
- online_orders_today: all online orders opened today
- current_shift: active waiter shift info (waiter_id, started_at)
if a business day is open; null otherwise
- as_of: UTC timestamp of the snapshot
The remote manager dashboard reads this via
GET /api/remote/sites/{id}/snapshot (manager JWT).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Phase 4 _mirror_status_to_cloud function had no way to look up the
cloud order's numeric id once it was marked synced (not in the pending
list anymore), so status updates from local staff could silently fail.
Fix:
- models/order.py: online_order_cloud_id INTEGER column added to Order
- main.py: migration for the new column
- schemas/order.py: online_order_cloud_id exposed in OrderOut
- cloud_sync.py: stores cloud_order["id"] as online_order_cloud_id
when creating the local order during the pull
- connect_orders.py: _mirror_status_to_cloud now takes the integer
cloud id directly — no more pending-list lookup; function body
reduced from ~50 lines to ~15
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.1 — cloud_sync.py
- _push_menu_snapshot(): serializes digital-visible products+categories
and POSTs to cloud /api/menu/sync every 5 minutes
- _pull_pending_orders(): polls cloud /api/orders/pending/{site_id}
every CONNECT_SYNC_INTERVAL_SECONDS (default 30s); creates local
Order + OrderItem rows, marks synced on cloud, broadcasts SSE event
- _connect_loop(): second asyncio task running the fast poll loop;
piggybacked menu push fires every 5 min regardless of poll interval
- _sync_once(): captures site_numeric_id from heartbeat response and
stores it in license_state so Connect loops can use it
- start_cloud_sync(): now creates and returns both tasks
4.2 — orders model/schema/migrations
- models/order.py: table_id made nullable (online orders have no
table); 7 new online_* columns added to Order
- schemas/order.py: OrderOut table_id Optional, all 7 online_* fields
added
- main.py: 8 additive ALTER TABLE migrations for orders table
4.3 — routers/connect_orders.py (NEW)
GET /api/connect/orders/incoming — pending online orders (any auth)
POST /api/connect/orders/{id}/accept — accept (manager)
POST /api/connect/orders/{id}/reject — reject with optional reason (manager)
POST /api/connect/orders/{id}/status — progress through lifecycle (manager)
All state changes mirror to cloud via background task and broadcast SSE
4.4 — main.py router registration
connect_orders router registered at /api/connect
config.py
CONNECT_SYNC_INTERVAL_SECONDS setting added (default 30)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Includes all work to date:
- local_backend: FastAPI backend with products, orders, tables, shifts, cloud sync
- manager_dashboard: React manager UI with product/category management, reports, settings
- waiter_pwa: React PWA for waiter devices
- Category reparent endpoint and UI
- Waiter domain: local_ip sent on heartbeat, waiter_domain persisted from cloud response
- QR code modal in AppInfoTab for waiter domain
- Product form: number input spinners removed, category pre-selected on new product
- Category row: count badge moved to far right
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>