# Implementer evidence — CLUB-001 ## Resumen Implementé la **Fase 1 — Core backend** del nuevo módulo **Club de Clientes**. ## Qué se creó ### 1) Nuevo módulo backend `club` Archivos nuevos en `project/src/modules/club/`: - `api/club.routes.ts` - `application/club-service.ts` - `domain/club.ts` - `domain/errors.ts` - `domain/ports.ts` - `infrastructure/device-token.ts` - `infrastructure/member-code.ts` - `infrastructure/pg-club-repository.ts` - `index.ts` - `tests/token-and-code.test.ts` - `tests/boundary.test.ts` ### 2) Migración core del Club - Nueva migración: `project/migrations/066_club_core.js` - Crea tablas: - `club_members` - `club_devices` - `club_transactions` - `club_recovery_codes` - `club_rewards` - `club_campaigns` - Añade seeds en `store_settings` para: - `club_enabled` - `club_cashback_bps` - `club_allow_anonymous_members` - `club_allow_recovery_codes` - `club_minimum_redeem_cents` ### 3) Endpoints backend de Fase 1 - `GET /club/config` - `POST /club/join` - `GET /club/me` - `GET /club/movements` - `GET /admin/club/settings` - `PATCH /admin/club/settings` ### 4) Comportamiento implementado - Alta anónima de socio Club. - Generación de `deviceToken` opaco. - Persistencia solo del `device_token_hash`. - Reutilización del socio actual si el dispositivo ya tenía token válido. - `memberCode` corto formato `MDV-XXXXXXXX`. - Ledger `club_transactions` como fuente de verdad. - `current_balance_cents` como cache transaccional. - Registro de transacciones idempotentes mediante `idempotencyKey`. - Configuración Club reutilizando `store_settings`. ### 5) Wiring en la app - Registré el módulo en `project/src/app/build-app.ts`. ## Tests añadidos - `project/src/modules/club/tests/token-and-code.test.ts` - `project/src/modules/club/tests/boundary.test.ts` - `project/src/app/tests/club.itest.ts` ## Fixes necesarios para poder ejecutar itest reales Las itest reales del proyecto estaban bloqueadas por migraciones previas mal definidas con `pgm.addColumn(...)`. Corregí: - `project/migrations/057_product_variant_weight_and_expiry.js` - `project/migrations/058_identity_email_confirmation.js` Esto no cambia la intención funcional de esas migraciones; corrige únicamente su forma para que node-pg-migrate pueda aplicarlas. ## Validación ejecutada - `./scripts/verify.sh` ✅ - `cd project && npm run typecheck` ✅ - `cd project && npm run build` ✅ - `cd project && npx vitest run src/modules/club/tests/token-and-code.test.ts src/modules/club/tests/boundary.test.ts` ✅ - `cd project && TEST_DATABASE_URL=postgres://mdv:mdv_dev_only@localhost:5432/mercadodevida_test npx vitest run src/app/tests/club.itest.ts --no-file-parallelism` ✅ - `git diff --check` ✅ ## Decisiones técnicas relevantes - Reutilicé `store_settings` para configuración del Club en vez de crear otro subsistema. - El token de dispositivo sigue el patrón de sesiones existente: token opaco en cliente, hash SHA-256 en BD. - El módulo ya queda preparado para fases posteriores (PWA, TPV, recovery, linking, admin UI) sin introducirlas todavía. ## Deuda / siguiente paso - Fase 2 debería construir la PWA `/club/*` consumiendo estos endpoints y mostrando la tarjeta digital. - `npm run lint:boundaries` sigue fallando por violaciones **preexistentes y ajenas** en módulos `pos` y `security`; no introducidas por CLUB-001.