SICOP — Decisiones de arranque (MVP CRM automotor)

Fecha: 2026-08-21 · Contexto: brief v0.1 + sesión de kickoff Estado: pre-rails new · Siguiente acción: scaffolding + primer test de dominio

Decisiones tomadas

  1. Notificaciones → n8n como middleware. Rails no habla con WhatsApp/SMS directamente. NotificationService publica a webhook de n8n (URL por ENV); n8n enruta a Twilio/Meta BAPI/email. In-app sigue siendo Turbo Streams. Fallback: si no hay webhook configurado, log + cola (outbox pattern).

  2. Inventario → write-through. La venta cerrada en CRM decrementa stock en el sistema externo (poner -1 en CRM → el inventario real se actualiza). El CRM NO es fuente de verdad de stock; refleja lo que la API/CSV entrega y emite el evento de decremento vía DMS adapter.

  3. API de inventario → StubAdapter primero. No hay docs/credenciales del DMS. Se define el contrato del adaptador (push_deal, pull_inventory) y se intercambia cuando exista el DMS real. El resto del sistema no cambia.

  4. Multi-tenant → NO en este MVP. Un solo dealer, sin dealer_id. Si llega un segundo concesionario, la migración es acotada.

  5. Fuentes de leads ciclo 1 → manual + webhook. Formulario interno para BDC y endpoint JSON protegido por token. Ambos alimentan el mismo CreateLeadService.

  6. Roles → enum nativo, fuera Rolify. El brief instalaba Devise + Rolify y a la vez role:integer — redundante. Un solo enum en User, 6 roles.

  7. DB → Postgres también en dev (Docker), no SQLite. jsonb del brief requiere Postgres; igualar dev/prod elimina sorpresas.

Correcciones al brief

  • t.jsonb → solo Postgres (ver punto 7).
  • Enum de leads incluye etapas de cotización inalcanzables en ciclo 1: aceptable, pero el dashboard no muestra etapas vacías.

Pendientes para definir

  • ¿DMS real del cliente? (BusinessPro, Oracle DM, otro) — decide adaptador real.
  • ¿Webhook de la fuente digital ya existe o se crea el contrato primero?
  • Presupuesto para canales de n8n (Twilio vs Meta BAPI vs email).

Próximos pasos

  1. rails new en este directorio (Postgres + propshaft, sin JS pipeline).
  2. Domain layer + spec/domain/lead_assignment_spec.rb (primer test).
  3. Devise + enum de roles.
  4. Leads CRUD + webhook + BDC dashboard con SLA timer.

IMPLEMENTADO — 2026-08-21 (cambios vs brief)

  • App generada como MvpCrm (el directorio es mvp-crm; SICOP queda como producto).
  • Postgres 17 en Docker (sicop-pg, restart unless-stopped, credenciales postgres/postgres).
  • Devise 5 + enum de rol en User; sin Rolify (redundante con el enum).
  • LeadAssignment quedó como clase top-level en app/domain/: en Rails los subdirectorios de app/ NO aportan su basename al namespace (igual que app/services); el brief asumía Domain::LeadAssignment pero eso requeriría app/domain/domain/. La espec actual usa LeadAssignment.
  • BUG del brief corregido: rotate_queue! actualizaba la posición antes de reordenar, con lo que el where("position > ...") nunca matcheaba. Ahora primero corre el shift y luego mueve la entrada al final.
  • Enum status con valor new colisiona con AR::new (ArgumentError). Solución: prefix: :status en Lead (Lead.status_new, lead.status_contacted!) y prefix: :from/:to en LeadEvent.
  • lead_events.user_id pasó a nullable: los leads de webhook no tienen actor.
  • Webhook en /webhooks/leads con X-Webhook-Token (env SICOP_WEBHOOK_TOKEN, secure_compare) y skip verify_authenticity_token (CSRF no aplica: el token propio autentica; sin el skip, Rails devolvía 422).
  • Dashboard BDC en / (Turbo Streams en canal bdc_dashboard): banda de leads, form manual, score SLA por tarjeta, escalación por LeadSlaMonitorJob. Vendedor solo ve sus leads asignados.
  • Seeds: 7 usuarios (pass sicop123) + 4 leads demo + cola de asignación.
  • 21 specs verdes + rubocop limpio. Webhook verificado manualmente: 401/201/422.
  • El contenedor Docker se había detenido una vez; docker update --restart unless-stopped sicop-pg lo protege.

Falta (siguiente sesión)

  • Flujo de contacto: lead.status_contacted! + first_contact_at.
  • Asignación por reloj al vendedor (AssignToSellerService + UI).
  • Citas (crear/confirmar/reagendar) + reminder.
  • Inventario: modelo Unit + DMS::AdapterBase + StubAdapter + sync CSV.
  • n8n: NotificationService → webhook outbound.