# F-144 — Diseño técnico ## Enfoque Una única migrations `048_reporting_store_shipping_snapshots.js` (node-pg-migrate, estilo `043_pos_basics`/`047_*`) idempotente y reversible. **No hay migración de datos costosa**: `store_id` usa column `DEFAULT` del default store (sembrado por 043), por lo que filas existentes e inserts sin store_id heredan el default sin table rewrite ni toque en el app-layer. ## Columnas nuevas | Tabla | Columna | Tipo | Nullable | Default | Restricción | |---|---|---|---|---|---| | orders_orders | store_id | uuid | NOT NULL | `'00000000-0000-0000-0000-000000000001'::uuid` | FK → pos_stores(id) | | orders_orders | shipping_cents | integer | NOT NULL | 0 | CHECK (>=0) | | orders_items | cost_at_sale_cents | bigint | YES (snapshot) | — | — | | orders_items | vat_rate | text | YES (snapshot) | — | — | Índice: `orders_orders_store_id_created_at_idx ON orders_orders(store_id, created_at)` (por el patrón de reporting §7: consultas por tienda + rango de fechas). ## Idempotencia + reversibilidad - Toda la DDL: `ADD COLUMN IF NOT EXISTS` / `DO $$ IF NOT EXISTS` sobre `pg_constraint`. Re-ejecutar es no-op. - `down()`: `DROP INDEX`, `DROP CONSTRAINT`, `DROP COLUMN` por cada nueva columna (orden inverso de dependencias). ## Por qué DEFAULT (no NOT NULL sin default + UPDATE) - `store_id` es una columna nueva: no hay histórico que "reescribir". Un `DEFAULT` constante backfilla silenciosamente y evita un `UPDATE` table-scan sobre tabla potencialmente grande — alineado con §11 "no añadir índices duplicados indiscriminadamente" y con no-rewrite-history. - El app **inserta pedidos** vía `PgOrderRepository` (raw SQL `INSERT INTO orders_orders (...)`). Con column `DEFAULT`, ese INSERT sigue funcionando (la column no aparece en la lista) → cero regresión en checkout/orders.itest. La resolución explícita de tienda (writes app-layered) se deja a F-146. ## Test - itest `reporting-snapshots.itest.ts` (mirror `inventory.itest.ts`): `recreateDatabase(url)` + `runMigrations(url,'up')` (aplica 048 contra `mercadodevida_test`) + `createPool`, con `describe.skipIf(!hasDb)`. - Asocia columnas/nullabilidad/default/FK/índice vía `information_schema` / `pg_constraint` / `pg_indexes`, e inserta `INSERT INTO orders_orders DEFAULT VALUES RETURNING store_id, shipping_cents` → `store_id = DEFAULT_STORE_ID`, `shipping_cents = 0`. - Harness: `TEST_DATABASE_URL=postgres://mdv:mdv_dev_only@localhost:5432/mercadodevida_test` (verificado: `orders.itest.ts` recrea+migra+tests en ~450ms con el `mdv` superuser). ## Verificación esperada - `npx tsc --noEmit` → 0 errores. - `TEST_DATABASE_URL=... npx vitest run src/app/tests/reporting-snapshots.itest.ts` → 3/3. - `TEST_DATABASE_URL=... npx vitest run` → 1 archivo itest nuevo + regresión (orders/catalog/checkout/sales itests) verde, 0 fallos. - `node scripts/check-module-boundaries.mjs src` → 0 violaciones nuevas (F-144 añade 1 migration .js + 1 .ts en tests; sin imports inter-módulo).