- services/license.py: verifies the cloud's Ed25519-signed license token
(public key built in) and decides purely: valid → licensed until expiry,
then a 5-day grace, then blocked - never mid-service (deferred while a
workday is open, applied at close). Works offline for as long as the
license lasts: the "unlicensed after 72h without heartbeat" rule is gone.
- Tamper resistance: an edited token fails the signature ("unverified");
a clock earlier than the latest provable time (token issued_at, newest
order in the DB, stored high-water mark; 1 day tolerance) → "clock".
- apply_license() re-evaluates from the stored token at startup, after
every heartbeat attempt and when a workday closes. Cloud lock/unlock from
the token keeps the workday-deferred behaviour. Transition: a cloud
without tokens is trusted 72h per successful heartbeat.
- FIX: the promised 5-day grace after expiry never happened - the cloud's
licensed=false was applied immediately (402 on everything).
- FIX: license_state.json lived inside the container and was lost on every
re-creation; it now lives in the data volume (old path read once).
- /api/system/status: offline_days, license_verified, license_problem,
grace_over; lock_reason "clock"/"unverified"; grace days from the license
module (rounded up).
Tests: 18 unit checks (signature, tamper, other site/key, 364 days
offline, grace ±workday, inactive, clock rollback, transition) + 17 E2E
checks with a real cloud + site process (400 days offline, tampered file →
402, clock behind newest order, expiry deferred until workday close,
renewal, remote lock/unlock).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Snapshot of in-progress work across local_backend, manager_dashboard,
and waiter_pwa (pricing, chat, fiscal, prep zones, recovery codes, CRM,
inventory, permissions), plus the nginx/docker-compose deploy fixes for
the Unraid + NPM reverse-proxy setup.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Backend — /api/business-day/summary now returns full financial summary:
- COGS: trackable cost (sum of unit_cost snapshots on paid items), uncosted item count
and revenue, gross_profit, cogs_has_gap flag
- Labor: total_labor_cost (sum of shift_pay for the day), labor_untracked_shifts count
- Waste: waste_item_count + waste_estimated_cost from waste_log for this business day
- Expenses: expenses_total, expenses_paid_today, expenses_due for expenses logged today
- Tabbed revenue: revenue from items with status='tabbed' (deferred, not yet collected)
- Net estimate: revenue - COGS - labor - waste - expenses_paid; net_has_unknowns flag
when any component has gaps (uncosted items or untracked shifts)
- compute_shift_pay import hoisted out of loop (bug fix)
Frontend — WorkdaySummaryModal OverviewTab fully expanded:
- New FinancialRow helper for consistent two-column P&L rows with accent/warn/indent
- REVENUE section: total sales, tabbed deduction, net collected
- COGS section: trackable cost, uncosted items warning, gross profit with gap indicator
- LABOR section: tracked labor cost, untracked shifts warning
- WASTE section: item count + estimated cost (hidden when zero)
- EXPENSES section: total, paid today, still due (hidden when zero)
- NET ESTIMATE: prominent bottom line, amber warning when unknowns exist
- Payment breakdown bar (existing)
- Cash reconciliation summary when store cash was counted
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New reservations module: model, schema, router (CRUD + status updates + upcoming alerts)
and background task for auto-expiring stale reservations
- Reports: print_products, print_categories, print_tables analytics endpoints
plus meta_products and business_day_summary for workday close/view flow
- printer_service: configurable font sizes/weights, donut/bar chart print layout helpers,
analytics print blocks per printer
- tables/schemas: surfaced color, zone, and other new fields on Table, Product, User, Printer
- demo_seed.py for quick dev DB population; wipe_database.py utility
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>