# F-144 — Implementer evidence ## What F-144 build evidence: an additive schema migration `048_*` adds the store/VAT/cost/shipping snapshot columns to `orders_orders` / `orders_items`, plus DB integration test `reporting-snapshots.itest.ts`. Backend-only, **no API or user-facing behavior change** (reporting routes are unchanged from F-143). ## Design recap (architect-approved — see architect.md) - `orders_orders.store_id` `uuid NOT NULL DEFAULT ` with guarded FK → `pos_stores(id)` + index `(store_id, created_at)`. Column DEFAULT backfills existing rows and future inserts without a table rewrite and without app-layer changes to the order INSERT path. FK resolución por request se posterga a F-146. - `orders_orders.shipping_cents` `integer NOT NULL DEFAULT 0` separa envío del total (sin sobreescritura: históricos → 0, `total_cents` intacto). - `orders_items.cost_at_sale_cents` `bigint` y `orders_items.vat_rate` `text`, **nullable** (snapshots → NULL hasta poblados; margen/IVA-por-tipo permanecen `unavailable`, nunca 0). - `up()` idempotente: `ADD COLUMN IF NOT EXISTS` (PG>=9.6) + `DO $$ IF NOT EXISTS ... $$` guard para el FK (PG16 no admite `ADD CONSTRAINT IF NOT EXISTS`). `down()` reversible (índice/constraint/columnas). ## Files - `project/migrations/048_reporting_store_shipping_snapshots.js` (created) — `up`/`down` idempotent/reversible. El FK usa `DO $$` guard (convención 047/046) tras confirmar via psql que el bloque DO es SQL válido en PG16.15. - `project/src/app/tests/reporting-snapshots.itest.ts` (created) — recrea la DB (drop+recreate), migra `001→048`, verifica schema vía `information_schema`/`pg_constraint`/`pg_indexes`, e inserta revertindo el CHECK de 047. ## Tests - NEW `reporting-snapshots.itest.ts` (3, real PostgreSQL): AC1 (store_id col NOT NULL + DEFAULT + FK contype='f' + índice + INSERT backfill store_id=DEFAULT_STORE_ID), AC2 (shipping_cents NOT NULL DEFAULT 0 + backfill=0), AC3 (cost_at_sale_cents bigint nullable / vat_rate text nullable). - Adaptación: el `INSERT` usa `INSERT INTO orders_orders (source) VALUES ('pos')` en lugar de `DEFAULT VALUES` porque el CHECK de 047 (`source='pos' OR user_id IS NOT NULL`) rechaza una fila 100% NULL; `store_id`/`shipping_cents` siguen backfilliándose por el column DEFAULT → el intento de AC1/AC2 se verifica. ## Verification - Migration 048 applies clean (dev + test DB): `node-pg-migrate up --verbose` → "Migrations complete!"; `048_reporting_store_shipping_snapshots (UP)` log; columns/FK/index presentes en `mercadodevida` dev. (Raw psql confirmó el DO-block FK válido en PG16.15.) - `TEST_DATABASE_URL=... npx vitest run src/app/tests/reporting-snapshots.itest.ts` → **3 passed**, 0 skipped, exit 0 (crea DB, migra 048, verifica todo). - `npx tsc --noEmit` → **0 errors** (incluye el itest .ts; estricto, noUncheckedIndexedAccess). - `node scripts/check-module-boundaries.mjs src` → **0 NEW violations** (única infracción R1 preexistente `security.routes.ts → log-broadcaster`, no introducida por F-144, fuera del diff). - `./scripts/verify.sh` → green (F-144 in_progress, runtime-consistent; 270 features válidos). - Re-run up() idempotente → no-op (DDL `IF NOT EXISTS` + DO guard); `down()` revierte todo. ## AC traceability | AC | Estado | Evidencia | |----|--------|-----------| | AC1 store_id NOT NULL + DEFAULT + FK + idx + backfill | ✅ | itest col/is_nullable=NO/default+FK contype='f'+índice+INSERT backfill | | AC2 shipping_cents NOT NULL DEFAULT 0 | ✅ | itest col + INSERT backfill=0 | | AC3 cost_at_sale/vat_rate nullable | ✅ | itest is_nullable=YES en orders_items | | AC4 idempotent/reversible | ✅ | IF NOT EXISTS + DO guard; down() drops all (PG16 raw check válida DO-block) | | AC5 itest DB | ✅ | 3/3 (dev + test DB fresh) | | AC6 gates | ✅ | tsc 0; itest 3/3; 0 boundaries nuevas | | AC7 no regressión | ✅ | orders_orders INSERT sin store_id explícito sigue válido (column DEFAULT) |