Saltar al contenido principal

0004 — Etiquetas de conversación como capacidad de plataforma

Estado: aceptada · Fecha: 2026-09-15

Contexto​

Hacía falta que el bot clasificara conversaciones ("viene por seguro") sin transferirlas, y que la bandeja mostrara esas etiquetas. Ya existían etiquetas de conversación, pero en el CRM:

  • Tablas sin versionar. crm_tags, crm_lead_tags y crm_conversation_tags se crearon a mano en producción; no había migración, y DEV y PROD podían divergir sin que nadie lo viera.
  • Atadas a un módulo opcional. El catálogo solo se gestionaba en /dashboard/crm/tags, detrás del módulo CRM, que el paquete de clínica no activa.
  • Accesos abiertos, según el volcado de PROD del 2026-09-15:
    • GRANT ALL a anon en tablas y vistas;
    • cualquier miembro del tenant editaba el catálogo;
    • el pivote comprobaba que la conversación fuera del tenant, pero no la etiqueta.

Decisión​

Las etiquetas de conversación son plataforma, con las mismas tablas. No se crean tablas nuevas: un tenant con CRM y sin él comparten un único catálogo, y el widget del CRM sigue viendo lo que pone el bot.

  • Migración 20261007000002.
    • Versiona el esquema de forma idempotente (no-op donde ya existe).
    • Revoca anon.
    • Exige core.tags.manage (o crm.tags.manage, por compatibilidad) para gestionar el catálogo y conversation.reply para etiquetar, con etiqueta y conversación del mismo tenant. Leer las etiquetas exige poder ver la conversación.
    • Publica el pivote en Realtime.
    • La prueba de roles está en supabase/tests/conversation_tags/.
  • Migración 20261007000003. Añade core.tags.manage a los dos caminos de alta de tenant (grant_tenant_admin_core_permissions y role_presets.admin de los paquetes de industria) y crea el menú Configuración → Etiquetas sin module_code.
  • Backend.
    • ConversationTagsService (core) resuelve etiquetas por código o nombre, solo del catálogo del tenant, y nunca las crea.
    • La herramienta tag_conversation no declara requiresModule y solo aparece si el tenant tiene etiquetas de conversación.
  • Frontend. Las vistas del catálogo reciben sus rutas (TagRoutes) y se montan en Configuración y en el CRM.

El nombre crm_* de las tablas se conserva a propósito: renombrarlas obligaría a tocar el CRM y sus vistas sin ningún beneficio para quien usa el producto.

Consecuencias​

  • Pierden la gestión del catálogo los miembros sin rol admin y sin crm.tags.manage, que antes podían editarlo. La migración reparte el permiso nuevo a admins y a quien ya tenía el del CRM.
  • Pierde la opción de etiquetar quien no tiene conversation.reply. Es coherente: tampoco puede responder en esa conversación.
  • El motivo del traspaso (conversations.handoff_reason) es otra cosa: un estado temporal que se borra al reactivar el bot. Las etiquetas son clasificación persistente.
  • Pendiente: crm_lead_tags, las vistas crm_*_v del resto del CRM y sus tablas siguen sin versionar. Esta decisión solo cubre lo que usa la bandeja.