← All tests

Loyalty catalog mirror + sync status STAGING

Narong (highlighted directive): “products must MIRROR NIX POS” + show a sync status. The loyalty /sync/catalog was a plain upsert, so a product deleted in cafe lingered in the loyalty catalog as active and kept showing in the app. Board item f3.

The fix: /sync/catalog is now a mirror (replace-set) in one transaction — upsert the pushed menu, then soft-deactivate any catalog product absent from the push, drop it from Popular Menu, and null its banner link so it disappears from the app. Soft (not hard-delete) so an incomplete push is recoverable by the next full sync. Last-sync status (time · count · removed) is recorded in tenant.settings and shown by the Sync button. No migration.

Mirror correctness — local backend integration test (Fastify + Postgres)

StepResult
Push [A, B, C]upserted 3, deactivated 0
Re-push [A, B] (C deleted in cafe)upserted 2, deactivated 1
Catalog afterA active · B active · C active=false
C dropped from Popular Menuyes (0 rows)
C's banner deep-linknulled
Sync status recordedsyncedAt set · count 2 · removed 1
Idempotent re-push [A, B]deactivated 0
Reversible — re-add [A, B, C]C active=true again

Deployed round-trip — get-coffee.staging

Real SSO login → Loyalty settings → Sync catalog & stores → the deployed staging backend mirrors the menu and records status; the “Last synced” indicator renders and persists across reload. 5/5 (+ figma-layout regression 16/16, staging smoke 5/5).

Sync result
Sync round-trips: “Synced 10 products, 1 stores.”
Sync status persists
“Last synced just now · 10 products” — persists after reload

nix-loyalty-backend 84b9f65 (staging Fly) · nix-cafe b7ade8c (nix-cafe-staging Worker). Parked on staging for the scheduled prod promote.