# 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.
