2026-07-16 — Twenty CRM para concesionario VW Camiones

Entregable: ~/nef/twenty-vw-crm/ — proyecto completo, tests pasando (7/7).

Decisiones clave

  • Alcance = CRM de unidades, no DMS. Refacciones/taller/facturación quedan en el DMS externo. Frontera: DmsAdapter (leer inventario, empujar venta).
  • Dinero en centavos enteros; Twenty CURRENCY usa micros (×10 000 conversión).
  • IVA fijo 16% en precio-buildup.ts (IVA_TASA).
  • M2M Modelo↔Aplicación requiere objeto puente (Twenty no tiene M2M nativo).
  • Adapter intercambiable vía env DMS_ADAPTER; mock incluido, idempotente por folio de cotización.
  • Web-to-lead: token compartido X-Lead-Token + honeypot, no OAuth.
  • Recordatorios = objeto custom + cron 5 min que crea Tarea nativa de Twenty (aprovecha notificaciones nativas en vez de reinventarlas).
  • Manifiesto de App (app/package.json → clave twenty) marcado como “verificar contra versión instalada”: el framework de Apps aún cambia.

Runner externo (para PikaPods)

PikaPods no permite habilitar el framework de Apps → los handlers corren FUERA del pod: app/src/server.ts (Express + node-cron). Rutas: /salud, /web-to-lead (token + CORS), /funciones/* (X-Api-Token opcional via RUNNER_TOKEN), /tablero (dashboard estático en app/public/index.html). Crons: recordatorios 5 min, sync DMS 15 min, sync inicial al arrancar. Config en .env (ver .env.example). Correr: npm run server (o server:dev). El pod queda 100% vanilla; solo se usa npm run metadata contra él una vez.

Pendiente al desplegar

  1. Twenty self-host arriba (docs/self-host-docker.md).
  2. npm run metadata con API key real.
  3. Crear vistas en UI (docs/vistas.md — receta exacta).
  4. Adapter real cuando se sepa qué DMS usa el concesionario.