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
-
Notificaciones → n8n como middleware. Rails no habla con WhatsApp/SMS directamente.
NotificationServicepublica 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). -
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.
-
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. -
Multi-tenant → NO en este MVP. Un solo dealer, sin
dealer_id. Si llega un segundo concesionario, la migración es acotada. -
Fuentes de leads ciclo 1 → manual + webhook. Formulario interno para BDC y endpoint JSON protegido por token. Ambos alimentan el mismo
CreateLeadService. -
Roles → enum nativo, fuera Rolify. El brief instalaba Devise + Rolify y a la vez
role:integer— redundante. Un soloenumen User, 6 roles. -
DB → Postgres también en dev (Docker), no SQLite.
jsonbdel brief requiere Postgres; igualar dev/prod elimina sorpresas.
Correcciones al brief
t.jsonb→ solo Postgres (ver punto 7).- Enum de
leadsincluye 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
rails newen este directorio (Postgres + propshaft, sin JS pipeline).- Domain layer +
spec/domain/lead_assignment_spec.rb(primer test). - Devise + enum de roles.
- 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).
LeadAssignmentquedó como clase top-level enapp/domain/: en Rails los subdirectorios deapp/NO aportan su basename al namespace (igual queapp/services); el brief asumíaDomain::LeadAssignmentpero eso requeriríaapp/domain/domain/. La espec actual usaLeadAssignment.- BUG del brief corregido:
rotate_queue!actualizaba la posición antes de reordenar, con lo que elwhere("position > ...")nunca matcheaba. Ahora primero corre el shift y luego mueve la entrada al final. - Enum
statuscon valornewcolisiona conAR::new(ArgumentError). Solución:prefix: :statusen Lead (Lead.status_new,lead.status_contacted!) yprefix: :from/:toen LeadEvent. lead_events.user_idpasó a nullable: los leads de webhook no tienen actor.- Webhook en
/webhooks/leadsconX-Webhook-Token(envSICOP_WEBHOOK_TOKEN, secure_compare) yskip verify_authenticity_token(CSRF no aplica: el token propio autentica; sin el skip, Rails devolvía 422). - Dashboard BDC en
/(Turbo Streams en canalbdc_dashboard): banda de leads, form manual, score SLA por tarjeta, escalación porLeadSlaMonitorJob. 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-pglo 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.