2026-05-13 on Supabase prod. First of two bundles toward multi-register per shop + UUID-decoupling from Odoo's bigint. Bundle 1 is migration + backfill ONLY, zero code change, zero behavior change. No code on prod reads the new pos_config_uuid column yet — legacy int pos_config_id stays authoritative until Bundle 2 ships.
node migrate.js against Supabase. New columns + backfill committed. Idempotent — safe to re-run.cafe.pos_configs's 4 new columns + partial index, pos_config_uuid on 4 referencing tables, 100% backfill coverage on every legitimate row (orphan history rows correctly excluded).642a07a (backend — migration + migrate.js entry) + dba0bc6 (cafe — Drizzle dual-column schema).- cafe.pos_configs Pro shop_id enriched: 0 rows - cafe.pos_configs 'Register 1' seeded for shops without a row: 7 rows - cafe.sessions.pos_config_uuid backfilled: 440 rows - cafe.orders.pos_config_uuid backfilled: 181 rows - cafe.shop_pos_configs.pos_config_uuid backfilled: 0 (Pro) + 4 (fallback) - cafe.pos_sequences.pos_config_uuid backfilled: 10 (Pro) + 368 (Starter) Migrations complete.
Loading…
get-coffee has 6 cafe.pos_configs but only 1 commerce.shops row:
- 5 Pro rows (Bakery Shop, Get Coffee CM/TK/TSP, Fresh Clean Shop — Odoo ids 4-8)
all with shop_id=NULL (no shop_pos_configs entry ever linked them).
- 1 'Register 1' auto-seeded by THIS migration — redundant for get-coffee.
The migration's "auto-seed if no pos_configs row exists FOR this shop" check
treated the shop as unlinked (correct — no shop_id was set yet) and inserted.
Implication for Bundle 2:
- Single-shop tenants (get-coffee + demo) need a one-shot enrichment:
UPDATE cafe.pos_configs SET shop_id=(...)
WHERE shop_id IS NULL
AND tenant has exactly 1 commerce.shops row
- Then delete the redundant auto-seeded 'Register 1' on get-coffee.
- Bundle 2's /cafe/settings/registers will show this cleanly: 5 Pro registers
under the one get-coffee shop, named after the Odoo pos.configs.
Why this happens: R6.5 created cafe.shop_pos_configs as an optional scoping
link table — Pro tenants can use it to restrict which shop sees which Odoo
register. Narong never populated it for get-coffee (only lumiere has 4 rows).
So the auto-enrichment-from-JOIN path returned 0 rows for get-coffee.
Probe still passes — every pos_config_uuid resolves correctly, every session +
order has the right UUID. The issue is purely cosmetic until Bundle 2's admin
UI tries to list "registers per shop". Fix lands as the first step of
Bundle 2's migration step.
| Suite | Result |
|---|---|
| test-multi-register-bundle1-prod.mjs (NEW) | 15/15 |
| test-phase1-prod.mjs | 11/11 |
| test-phase2-sso-outdoor-prod.mjs | 6/6 |
| test-phase2-cafe-multishop-prod.mjs | 6/6 |
| test-m1-prod.mjs | 10/10 |
| test-r7-prod.mjs | 14/14 |
| test-r8-prod.mjs | 4/4 |
| Total | 66/66 |
Migration step 1 (Bundle 2 starts here):
Enrich shop_id on pos_configs for tenants with exactly 1 shop +
delete redundant auto-seeded Register 1 rows where Pro rows exist.
Then code cutover (~30-40 files):
- Read pos_config_uuid in every DAO/route/action/test
- Set NOT NULL + FK on pos_config_uuid → cafe.pos_configs.id
- /cafe/settings/registers admin (per-shop dropdown, CRUD, sequence_prefix)
- Starter POS landing renders N cards per shop (currently 1:1)
- StarterTopBar register picker when shop has > 1 register
- formatPosOrderNumber extended with sequence_prefix
- settings-nav.tsx adds "Registers" entry
Out of scope (separate future task):
- Odoo connector to push NIX-created Pro registers via pos.config.create.
Bundle 2 sets sync_state='pending_create' on those rows; the connector
drain mirrors R11.5's session-close-move shape.