-- 015_order_status_check.sql — Kanonisk orderstatus (ADR-0002, C3 2026-09-29) -- -- Guardar `orders.status` (fri text sedan sql/001_init.sql line ~92) mot den -- kanoniska unionen i lib/order-flow.ts (ORDER_STATUSES). Denna fil KÖRS EJ -- automatiskt av deploy — Simon kör den manuellt mot prod efter harvest. -- -- KÖRORDNING (DoD §17 backup-städ, §33 migreringsordning, §35 restore-drill): -- 1. BACKUP FÖRST: pg_dump "$DATABASE_URL" -t orders -Fc -f orders-pre-015.dump -- (och verifiera att restore-drillen har körts nyligen — §35). -- 2. HARVEST-PREFLIGHT (läs-only, körs före fas A): -- SELECT status, COUNT(*) FROM orders GROUP BY status ORDER BY 2 DESC; -- Alla rader MÅSTE ligga i listan nedan. Finns okända statusar: -- AVBRYT — mappa dem först (UPDATE ... WHERE status = '') i en -- egen, granskad migrering; kör aldrig fas A med okända rader kvar. -- 3. FAS A ( TRANSACTIONEN NEDAN — ADD CONSTRAINT ... NOT VALID ): -- tar bara ett kort ACCESS EXCLUSIVE-lås vid själva katalogändringen, -- validerar INTE befintliga rader, blockerar inte skrivningar längre tid. -- 4. FAS B (SEPARAT session/transaktion — VALIDATE CONSTRAINT): -- BEGIN; SET lock_timeout = '5s'; -- ALTER TABLE orders VALIDATE CONSTRAINT order_status_check; -- COMMIT; -- VALIDATE tar SHARE UPDATE EXCLUSIVE (skrivningar tillåtna); med -- lock_timeout stänger vi ute långa låsköer. Misslyckas fas B: retry. -- -- Listan är kod-SSOT: håll synkroniserad med ORDER_STATUSES i lib/order-flow.ts. BEGIN; -- Preflight-guard: fas A nekar om okända statusar finns (fail-closed). -- (SELECT ... FOR UPDATE-stil låsning behövs ej — NOT VALID-constrainten -- läser inga rader; vi vill bara neka körning vid dirt-data.) DO $preflight$ DECLARE unknown_rows bigint; BEGIN SELECT COUNT(*) INTO unknown_rows FROM orders WHERE status NOT IN ('draft', 'quoted', 'in_production', 'review', 'accepted', 'delivered', 'cancelled'); IF unknown_rows > 0 THEN RAISE EXCEPTION 'order_status_check preflight: % rader med status utanför den kanoniska unionen — kör harvest (SELECT status, COUNT(*) FROM orders GROUP BY status), mappa okända statusar, KÖR EJ fas A', unknown_rows; END IF; END $preflight$; -- Rapportera (loggar) okända statusar om preflighten ändå passerar tom: SELECT status, COUNT(*) AS n FROM orders WHERE status NOT IN ('draft', 'quoted', 'in_production', 'review', 'accepted', 'delivered', 'cancelled') GROUP BY status; -- (0 rader = grönt; >0 rader = ovanstående DO-block har redan avbrutit.) ALTER TABLE orders ADD CONSTRAINT order_status_check CHECK (status IN ('draft', 'quoted', 'in_production', 'review', 'accepted', 'delivered', 'cancelled')) NOT VALID; COMMIT; -- FAS B körs i EGEN session (se körordning steg 4) — kommenterad här för att -- den ALDRIG ska ligga i samma transaktion som fas A: -- BEGIN; SET lock_timeout = '5s'; -- ALTER TABLE orders VALIDATE CONSTRAINT order_status_check; -- COMMIT;