LA BIBLIA DE LA PLATAFORMA UNIFICADA — ADDENDUM 06
Migración × Contactos × WebaBox — Gestor Multi-Contacto
Versión: 1.0
Fecha: 6 de junio de 2026
Autor: Alain Bessette, CFO/Product Owner, ABI Conglomerate
Dominio: GoWeBa.com
Estado: Addendum Estratégico Oficial — Especificación Vinculante
Restricciones: CERO pérdida de datos · CERO restablecimiento forzado · CERO interrupción en el envío/recepción de correos
⚠️ AVISO CRÍTICO
Este addendum tiene la MISMA autoridad que la Parte 1, la Parte 2 y todos los Addendums anteriores de la Biblia.
Todos los cambios descritos aquí están desplegados en producción y registrados como puntos de control.
No se realizaron cambios que rompan el esquema. Todas las modificaciones son compatibles con versiones anteriores.
📋 TABLA DE CONTENIDOS
- Fase A — Corrección de Categoría WebaBox
- Fase B.1 — Botón Eliminar en Migración
- Fase B.2 — Desconexión de Cuenta Google
- Fase B.3 — Enriquecimiento Automático Durante la Migración
- Fase B.4 — Motor de Clasificación WebaCategory + Archivado a 90 Días
- Fase C.1 — Pestaña Línea de Tiempo del Contacto
- Fase C.2 — Pestaña Archivos Adjuntos (Código de Color + Vista Previa + Pantalla Completa)
- Fase C.3 — Pestaña Registro de Fusiones
- Fase C.4 — Pestaña Análisis de IA
- Fase D — Enriquecimiento por Lotes Post-Migración y Verificación
- Fase E — Gestor Multi-Contacto (Enriquecimiento Relacional + Seguridad en Fusiones)
- Correcciones de Errores
- Archivos Modificados — Inventario Completo
- Cronología de Puntos de Control
<a id="phase-a"></a>
SECCIÓN 1: FASE A — CORRECCIÓN DE CATEGORÍA WEBABOX
1.1 Descripción del Problema
La API PATCH de WebaBox (/api/inbox/threads/[id]) estaba asignando webaCategory: 'CONVERSATIONS' cuando un usuario respondía a un hilo URGENT. Sin embargo, CONVERSATIONS no es un valor válido en el enum WebaCategory.
Valores válidos del enum: URGENT, IMPORTANT, JOURNAL, OFFICE, NONE.
1.2 Causa Raíz
En webabox-client.tsx, el manejador de respuestas tenía codificado de forma fija webaCategory: 'CONVERSATIONS' en lugar del valor correcto webaCategory: 'NONE'.
1.3 Corrección Aplicada
Archivo: app/(app)/webabox/webabox-client.tsx
Cambio: webaCategory: 'CONVERSATIONS' → webaCategory: 'NONE'
1.4 Comportamiento Después de la Corrección
| Acción | Antes | Después |
|---|---|---|
| Responder a un hilo URGENT | El hilo se mueve a "Conversations" con categoría inválida | El hilo se mueve al filtro Conversations (NONE + messageCount > 1) |
| Contadores de tarjetas WebaBox | Podían mostrar conteos fantasma | Conteos precisos en las 12 tarjetas |
1.5 Aclaración Clave — Arquitectura de 12 Tarjetas WebaBox
Las 12 tarjetas WebaBox utilizan 5 valores de BD + 7 filtros inteligentes:
| Tarjeta | Fuente |
|---|---|
| Urgent | webaCategory = URGENT |
| Important | webaCategory = IMPORTANT |
| Journal | webaCategory = JOURNAL |
| Office | webaCategory = OFFICE |
| None | webaCategory = NONE Y messageCount = 1 |
| Conversations | webaCategory = NONE Y messageCount > 1 |
| Reply Later | isReplyLater = true |
| Set Aside | isSetAside = true |
| Starred | isStarred = true |
| Spam | isSpam = true |
| Trash | deletedAt IS NOT NULL |
| All Emails | Todos los hilos no eliminados |
<a id="phase-b1"></a>
SECCIÓN 2: FASE B.1 — BOTÓN ELIMINAR EN MIGRACIÓN
2.1 Descripción del Problema
El botón Eliminar en las tarjetas de proyectos de migración solo era visible para los estados CREATED, COMPLETED, FAILED y CANCELLED. Los proyectos con estado IMPORTING, SCANNING o PLANNING no mostraban el botón Eliminar, dejando a los usuarios sin posibilidad de cancelar una migración bloqueada.
2.2 Cambios Aplicados
Frontend — app/(app)/migration/migration-client.tsx
// Antes
const canDelete = ['CREATED', 'COMPLETED', 'FAILED', 'CANCELLED'].includes(project.status);
// Después
const canDelete = ['CREATED', 'COMPLETED', 'FAILED', 'CANCELLED', 'IMPORTING', 'SCANNING', 'PLANNING'].includes(project.status);
Backend — app/api/migration/projects/[id]/route.ts
// Antes
const deletableStatuses = ['CREATED', 'COMPLETED', 'FAILED', 'CANCELLED'];
// Después
const deletableStatuses = ['CREATED', 'COMPLETED', 'FAILED', 'CANCELLED', 'IMPORTING', 'SCANNING', 'PLANNING'];
2.3 Resultado
Los usuarios ahora pueden eliminar proyectos de migración en cualquier estado, incluyendo importaciones activas.
<a id="phase-b2"></a>
SECCIÓN 3: FASE B.2 — DESCONEXIÓN DE CUENTA GOOGLE
Ya existía en la plataforma. El botón Desconectar era funcional en la página de Migración. No se requirieron cambios.
<a id="phase-b3"></a>
SECCIÓN 4: FASE B.3 — ENRIQUECIMIENTO AUTOMÁTICO DURANTE LA MIGRACIÓN
4.1 Descripción General
Cuando los contactos se importan durante una migración de Google Workspace, el sistema ahora los enriquece automáticamente mediante:
- Creación/vinculación de registros
CompanyContactbasados en el nombre de la empresa o el dominio del correo electrónico - Creación de vínculos
ContactCompanyRoleentre Contact y CompanyContact - Análisis de firmas de correo electrónico mediante LLM para extraer datos adicionales
4.2 Nuevas Funciones
Archivo: lib/migration/migration-actions.ts
enrichContactPostCreation(contactId, normalizedContact, organizationId, userId)
- Disparador: Se llama de forma asíncrona (fire-and-forget) después de
createContactEntity()en el flujo de ejecución de la migración - Lógica:
- Si
companyNameestá presente → buscar o crearCompanyContact(coincidencia por nombre sin distinción de mayúsculas/minúsculas O dominio de correo) - Extraer dominio del correo, omitir proveedores genéricos (gmail, yahoo, hotmail, etc.)
- Crear vínculo
ContactCompanyRolecon jobTitle como rol
- Si
- Manejo de errores: Fire-and-forget — los fallos se registran pero nunca interrumpen la migración
analyzeSignatureWithLLM(bodyHtml, bodyText)
- Propósito: Extraer datos estructurados de firmas de correo electrónico
- Modelo:
primary(gpt-4.1) mediante el wrapper LLM de WEBA - Campos extraídos:
jobTitlecompanyNamephonewebsiteaddressbookingUrl← NUEVO (Calendly, Cal.com, HubSpot meetings, Acuity, etc.)
- Estrategia: Toma los últimos 600 caracteres del cuerpo del correo como bloque de firma
- Salida: Respuesta JSON analizada, devuelve
nullsi no se encuentran datos útiles
4.3 Punto de Integración
Archivo: app/api/migration/projects/[id]/execute/route.ts
// Después de createContactEntity()
enrichContactPostCreation(contact.id, normalized, organizationId, userId).catch(() => {});
<a id="phase-b4"></a>
SECCIÓN 5: FASE B.4 — MOTOR DE CLASIFICACIÓN WEBACATEGORY + ARCHIVADO A 90 DÍAS
5.1 Motor de Clasificación
Archivo: lib/migration/weba-classify.ts
Clasificación basada en reglas con respaldo opcional de LLM para hilos ambiguos.
Motor de Reglas (Sin costo de LLM)
| Categoría | Patrones Regex/Regla |
|---|---|
| JOURNAL | Reportes DMARC, noreply@, no-reply@, recibos, notificaciones de envío, confirmaciones de entrega, alertas del sistema, respuestas automáticas, mensajes de rebote |
| OFFICE | Boletines informativos, notificaciones de redes sociales, correos promocionales, "unsubscribe", etiquetas de categoría de Gmail (CATEGORY_PROMOTIONS, CATEGORY_SOCIAL, CATEGORY_UPDATES, CATEGORY_FORUMS) |
| URGENT | Mensajes destacados de Gmail, etiqueta IMPORTANT |
| IMPORTANT | Contactos conocidos en la organización (correo en la tabla ContactEmail), hilos con 3+ mensajes (conversaciones activas) |
| NONE | Valor predeterminado de respaldo |
Respaldo LLM (Opcional)
classifyAmbiguousWithLLM() — Clasifica en lote los hilos que obtuvieron NONE con baja confianza. Disponible para una pasada post-migración, pero no se llama durante la importación inicial para mantener los costos bajos.
5.2 Punto de Integración
Archivo: app/api/migration/projects/[id]/execute/route.ts
// Antes de la creación del hilo
const webaCategory = classifyWebaCategory(threadData, gmailLabels, orgContactEmails);
// webaCategory se pasa a EmailThread.create()
5.3 API de Archivado a 90 Días
Archivo: app/api/migration/archive-old-threads/route.ts
- Endpoint:
POST /api/migration/archive-old-threads - Cuerpo:
{ days?: number }(predeterminado: 90) - Acción: Establece
isArchived: trueen hilos con más de N días de antigüedad - Auditoría: Registrado como
migration.archive_old_threads
<a id="phase-c1"></a>
SECCIÓN 6: FASE C.1 — PESTAÑA LÍNEA DE TIEMPO DEL CONTACTO
6.1 Nueva Pestaña
Archivos:
app/api/contacts/[id]/timeline/route.ts— Endpoint de APIapp/(app)/contacts/[id]/timeline-tab.tsx— Componente de interfaz
6.2 Funcionalidades
- Lista cronológica de todos los intercambios de correo con el contacto
- Agrega datos de TODAS las direcciones de correo del contacto (principal + registros ContactEmail)
- Código de color por dirección:
- ENTRANTE ↓ — Indicadores verdes
- SALIENTE ↑ — Indicadores azules
- Se muestra asunto, fragmento, fecha y direcciones de/para
- Capacidad de exportación a PDF
<a id="phase-c2"></a>
SECCIÓN 7: FASE C.2 — PESTAÑA ARCHIVOS ADJUNTOS (CÓDIGO DE COLOR + VISTA PREVIA + PANTALLA COMPLETA)
7.1 Reescritura Completa
Archivo: app/(app)/contacts/[id]/attachments-tab.tsx
7.2 Funcionalidades
- Lista todos los archivos adjuntos de correo de los hilos del contacto
- Código de color por dirección: Insignias verdes para ENTRANTE, azules para SALIENTE
- Botón de vista previa (👁): Abre un diálogo con vista previa en línea para:
- Imágenes (todos los formatos)
- PDFs (mediante iframe)
- Video (reproductor HTML5)
- Audio (reproductor HTML5)
- Archivos de texto (visualización en fuente monoespaciada)
- Modo pantalla completa (⛶): Botón maximizar en el diálogo de vista previa → superposición negra inmersiva
- Botón de descarga: Disponible tanto en la lista como en el modal de vista previa
- API de vista previa: Utiliza el endpoint existente
/api/inbox/attachments/[id]?action=previewpara URLs en línea prefirmadas
<a id="phase-c3"></a>
SECCIÓN 8: FASE C.3 — PESTAÑA REGISTRO DE FUSIONES
8.1 Nueva Pestaña
Archivos:
app/api/contacts/[id]/merge-log/route.ts— Endpoint de APIapp/(app)/contacts/[id]/merge-log-tab.tsx— Componente de interfaz
8.2 Funcionalidades
- Muestra el historial de todas las operaciones de fusión que involucran a este contacto
- Muestra: fecha de fusión, contactos de origen, actor (quién realizó la fusión)
- Datos obtenidos de registros AuditLog con la acción
contact.merge
<a id="phase-c4"></a>
SECCIÓN 9: FASE C.4 — PESTAÑA ANÁLISIS DE IA
9.1 Arquitectura
API: app/api/contacts/[id]/ai-insights/route.ts
Interfaz: app/(app)/contacts/[id]/ai-insights-tab.tsx
9.2 Análisis LLM
- Disparador: Bajo demanda — el usuario hace clic en el botón "Lancer l'analyse"
- Datos recopilados: Detalles del contacto, todas las direcciones de correo, teléfonos, roles en empresas, interacciones recientes (asunto + cuerpo), notas
- Modelo:
primary(gpt-4.1) mediante el wrapper LLM de WEBA - Prompt: Prompt estructurado en francés que solicita:
- Résumé de la relation
- Points clés de communication
- Statistiques d'interaction
- Recommandations
- Signaux de risque ou d'opportunité
9.3 Funcionalidades de la Interfaz
- CTA "Lancer l'analyse" con estilo degradado
- Estado de carga con spinner animado
- Secciones de markdown analizadas (encabezados, viñetas)
- Barra de estadísticas: conteo de correos, tiempo de respuesta, tokens utilizados
- Botón "Réanalyser" para actualizar
- Color de la pestaña: Acento violeta (coherente con las funcionalidades de IA)
<a id="phase-d"></a>
SECCIÓN 10: FASE D — ENRIQUECIMIENTO POR LOTES POST-MIGRACIÓN Y VERIFICACIÓN
10.1 API de Enriquecimiento por Lotes
Archivo: app/api/migration/batch-enrich/route.ts
- Endpoint:
POST /api/migration/batch-enrich - Propósito: Enriquecer contactos sin
jobTitleNIcompanyNameanalizando sus firmas de correo electrónico - Flujo:
- Encontrar contactos sin jobTitle/companyName (límite: 50-200 por lote)
- Para cada contacto, encontrar el correo ENTRANTE más reciente (mediante vínculo de hilo o participantEmails)
- Llamar a
analyzeSignatureWithLLM()sobre el cuerpo del correo - Actualizar campos del contacto (jobTitle, companyName, website, calendarBookingUrl)
- Crear registro ContactPhone si se extrajo un teléfono
- Crear CompanyContact + ContactCompanyRole si se encontró una empresa
- Deduplicación: Verifica registros existentes antes de crear
- Auditoría: Registrado como
migration.batch_enrich
10.2 API de Verificación Post-Migración
Archivo: app/api/migration/post-verify/route.ts
- Endpoint:
POST /api/migration/post-verify - Propósito: Ejecutar una verificación integral de salud post-migración
- Devuelve:
- Análisis de duplicados: Utiliza el
detectDuplicates()existente (coincidencia por correo, nombre, teléfono) - Estadísticas de completitud: Conteos de contactos sin jobTitle, companyName, teléfono, correo
- Vínculos de empresa: Conteo de contactos con asociaciones CompanyContact
- Estadísticas de hilos: Total, archivados, distribución de WebaCategory
- Estadísticas del proyecto: Si se proporciona
projectId, devuelve el estado del proyecto de migración
- Análisis de duplicados: Utiliza el
- Auditoría: Registrado como
migration.post_verify
<a id="phase-e"></a>
SECCIÓN 11: FASE E — GESTOR MULTI-CONTACTO
11.1 Visión
Un solo contacto puede tener:
- 4+ empresas (mediante ContactCompanyRole → CompanyContact)
- 4+ números de teléfono (mediante registros ContactPhone)
- 5-6 correos electrónicos (mediante registros ContactEmail)
- Múltiples direcciones (mediante registros ContactAddress — oficinas, propiedades, internacionales)
- Múltiples perfiles sociales (mediante registros ContactSocialMedia)
- URL de reserva (campo calendarBookingUrl)
11.2 Problema Identificado
La Inteligencia de Firma extraía los datos correctamente pero los guardaba solo en campos planos:
| Dato | Guardado en (❌ Antes) | La pestaña lee de |
|---|---|---|
| Teléfono | Contact.primaryPhone (texto) | ContactPhone (tabla relacional) |
| Empresa | Contact.companyName (texto) | ContactCompanyRole → CompanyContact (relaciones) |
| Facebook/Instagram | Contact.socialLinks (JSON) | ContactSocialMedia (tabla relacional) |
Resultado: Las pestañas mostraban (0) a pesar de que los datos habían sido extraídos.
11.3 Corrección — E.1: Enriquecimiento Relacional
Archivo: app/api/email/extract-signature/route.ts
La API de Inteligencia de Firma ahora crea registros relacionales además de los campos planos:
Creación de ContactPhone
- Crea
ContactPhonepara elphoneextraído (etiqueta: work) ymobilePhone(etiqueta: mobile) - Establece
isPrimary: truepara el primer teléfono si el contacto no tiene teléfonos existentes - Deduplicación: verifica registros ContactPhone existentes antes de crear
CompanyContact + ContactCompanyRole
- Busca CompanyContact existente por nombre (sin distinción de mayúsculas/minúsculas) o dominio de correo
- Crea nuevo CompanyContact si no se encuentra (con dominio, sitio web)
- Crea vínculo ContactCompanyRole con jobTitle como rol/título
- Establece
isPrimary: truepara el primer rol de empresa
ContactSocialMedia
- Crea registros individuales para cada plataforma social encontrada:
- LinkedIn →
LINKEDIN - Twitter/X →
TWITTER_X - Facebook →
FACEBOOK - Instagram →
INSTAGRAM - GitHub →
GITHUB - YouTube →
YOUTUBE - TikTok →
TIKTOK
- LinkedIn →
- El identificador se extrae de la URL (elimina protocolo/www)
- Deduplicación: verifica la combinación
plataforma:identificador
11.4 Corrección — E.2: Deduplicación en Fusiones (ContactCompanyRole)
Archivo: lib/contacts/contact-merge.ts
Antes: updateMany movía TODOS los registros ContactCompanyRole → FALLO por restricción de unicidad contactId_companyContactId si el destino y el origen comparten la misma empresa.
Después: Verificación por registro:
// Para cada rol de origen:
if (targetRoleSet.has(role.companyContactId)) {
// Misma empresa → eliminar el duplicado del origen
await prisma.contactCompanyRole.delete({ where: { id: role.id } });
} else {
// Empresa diferente → mover al destino
await prisma.contactCompanyRole.update({ where: { id: role.id }, data: { contactId: targetId } });
}
11.5 Corrección — E.3: Deduplicación en Fusiones (ContactSocialMedia)
Archivo: lib/contacts/contact-merge.ts
Mismo patrón — verifica la combinación plataforma:identificador antes de mover:
if (targetSocialSet.has(`${s.platform}:${s.handle.toLowerCase()}`)) {
await prisma.contactSocialMedia.delete({ where: { id: s.id } });
} else {
await prisma.contactSocialMedia.update({ where: { id: s.id }, data: { contactId: targetId } });
}
11.6 Corrección — E.4: El Destino Hereda los Mejores Campos Después de la Fusión
Archivo: lib/contacts/contact-merge.ts
Después de la fusión, el contacto destino ahora hereda los mejores datos disponibles de los orígenes fusionados:
for (const source of sources) {
if (!target.jobTitle && source.jobTitle) updates.jobTitle = source.jobTitle;
if (!target.companyName && source.companyName) updates.companyName = source.companyName;
if (!target.website && source.website) updates.website = source.website;
if (!target.calendarBookingUrl && source.calendarBookingUrl) updates.calendarBookingUrl = source.calendarBookingUrl;
if (!target.socialLinks && source.socialLinks) updates.socialLinks = source.socialLinks;
}
11.7 Flujo Completo de Fusión (16 Pasos)
Al fusionar N contactos en 1 destino:
| # | Relación | Estrategia |
|---|---|---|
| 1 | ContactEmail | Mover no duplicados, eliminar duplicados |
| 2 | ContactPhone | Mover no duplicados, eliminar duplicados |
| 3 | ContactInteraction | Mover todos |
| 4 | ContactNote | Mover todos |
| 5 | ContactTag | Mover no duplicados, eliminar duplicados |
| 6 | ContactAddress | Mover todos |
| 7 | ContactSocialMedia | Mover no duplicados, eliminar duplicados (E.3) |
| 8 | ContactCompanyRole | Mover no duplicados, eliminar duplicados (E.2) |
| 9 | ContactGroupMember | Mover no duplicados, eliminar duplicados |
| 10 | ContactObjectLink | Mover (omitir duplicados mediante restricción de unicidad) |
| 11 | FileAsset | Revincular todos |
| 12 | ClientInvoice | Revincular todos |
| 13 | EmailThread | Revincular todos |
| 14 | StudioFormSubmission | Revincular todos |
| 15 | ContractSignatory | Revincular todos |
| 16 | CalendarEventAttendee | Revincular todos |
Luego: establecer mergedIntoId en los orígenes, heredar los mejores campos en el destino.
<a id="bugfixes"></a>
SECCIÓN 12: CORRECCIONES DE ERRORES
12.1 Pestaña Línea de Tiempo — Bucle de Carga Infinita
Causa raíz: La función de traducción t de useT() estaba incluida en las dependencias de useCallback. La función t cambia de referencia en cada renderizado, causando bucles de recarga infinita.
Corrección: Se eliminó t del arreglo de dependencias de useCallback en timeline-tab.tsx.
12.2 Pestaña Registro de Fusiones — Bucle de Carga Infinita
Misma causa raíz y corrección que la Pestaña Línea de Tiempo, en merge-log-tab.tsx.
12.3 API de Eliminación — Estado IMPORTING Bloqueado
Problema: La API de backend rechazaba la eliminación para proyectos en estado IMPORTING/SCANNING/PLANNING.
Corrección: Se agregaron estos estados al arreglo deletableStatuses en app/api/migration/projects/[id]/route.ts.
12.4 BookingUrl No Extraída
Problema: analyzeSignatureWithLLM() no extraía URLs de reserva de calendario.
Corrección: Se agregó bookingUrl al prompt del LLM, la firma de tipo, la verificación hasData y applyEnrichment(). Se mapea a Contact.calendarBookingUrl.
<a id="files"></a>
SECCIÓN 13: ARCHIVOS MODIFICADOS — INVENTARIO COMPLETO
Nuevos Archivos Creados
| Archivo | Propósito |
|---|---|
lib/migration/weba-classify.ts | Motor de clasificación WebaCategory (reglas + LLM) |
app/api/migration/archive-old-threads/route.ts | API de archivado de hilos a 90 días |
app/api/migration/batch-enrich/route.ts | API de enriquecimiento por lotes post-migración |
app/api/migration/post-verify/route.ts | API de verificación post-migración |
app/api/contacts/[id]/ai-insights/route.ts | API de análisis de Análisis de IA |
app/(app)/contacts/[id]/ai-insights-tab.tsx | Componente de interfaz de Análisis de IA |
Archivos Modificados
| Archivo | Cambios |
|---|---|
app/(app)/webabox/webabox-client.tsx | Corrección CONVERSATIONS → NONE |
app/(app)/migration/migration-client.tsx | Se agregó IMPORTING/SCANNING/PLANNING a canDelete |
app/api/migration/projects/[id]/route.ts | Se agregó IMPORTING/SCANNING/PLANNING a deletableStatuses |
app/api/migration/projects/[id]/execute/route.ts | Se agregaron llamadas a enrichContactPostCreation + classifyWebaCategory |
lib/migration/migration-actions.ts | Se agregaron enrichContactPostCreation() + analyzeSignatureWithLLM() + bookingUrl |
app/(app)/contacts/[id]/timeline-tab.tsx | Corrección de bucle infinito (se eliminó t de las dependencias de useCallback) |
app/(app)/contacts/[id]/merge-log-tab.tsx | Corrección de bucle infinito (se eliminó t de las dependencias de useCallback) |
app/(app)/contacts/[id]/attachments-tab.tsx | Reescritura completa: modal de vista previa + pantalla completa |
app/(app)/contacts/[id]/contact-detail-client.tsx | Se agregó la pestaña Análisis de IA |
app/api/email/extract-signature/route.ts | Se agregó enriquecimiento relacional (ContactPhone, CompanyContact, ContactSocialMedia) |
lib/weba/signature-extraction.ts | Referencia — buildContactEnrichmentData (sin cambios) |
lib/contacts/contact-merge.ts | E.2/E.3: deduplicación de ContactCompanyRole + ContactSocialMedia; E.4: herencia de los mejores campos |
<a id="checkpoints"></a>
SECCIÓN 14: CRONOLOGÍA DE PUNTOS DE CONTROL
| # | Punto de Control | Descripción |
|---|---|---|
| 1 | Corrección Fase A | URGENT → NONE (no CONVERSATIONS) en webabox-client.tsx |
| 2 | Fase B.1-B.2 | Botón Eliminar visible + Desconexión de Google (preexistente) |
| 3 | Fase C.1-C.3 | Pestañas Línea de Tiempo, Archivos Adjuntos (código de color), Registro de Fusiones |
| 4 | Corrección eliminar línea de tiempo registro de fusiones vista previa | Eliminación IMPORTING + correcciones de bucle infinito + vista previa/pantalla completa de Archivos Adjuntos |
| 5 | Pestaña C.4 Análisis de IA en contactos | Pestaña de análisis LLM bajo demanda |
| 6 | B.3 auto-enriquecimiento CompanyContact migración | Enriquecimiento automático durante la ejecución de la migración |
| 7 | B.4 clasificación WebaCategory archivado 90d | Motor de clasificación + API de archivado |
| 8 | APIs D batch-enrich post-verify | Endpoints de enriquecimiento por lotes + verificación |
| 9 | Corrección API eliminar permitir estado IMPORTING | Corrección de API de backend para eliminación en estado IMPORTING |
| 10 | Agregar extracción de bookingUrl desde firmas | BookingUrl agregada a la extracción de firmas LLM |
| 11 | E enriquecimiento relacional multi-contacto fusión | Enriquecimiento relacional + deduplicación en fusiones |
SECCIÓN 15: IMPACTO EN EL ESQUEMA
Cero Cambios en el Esquema
Todas las modificaciones utilizan modelos y campos existentes de Prisma:
Contact— jobTitle, companyName, website, calendarBookingUrl, socialLinks, primaryPhone, primaryEmailContactPhone— phone, label, isPrimaryContactEmail— email, isPrimaryContactCompanyRole— contactId, companyContactId, role, title, isPrimaryCompanyContact— name, domain, website, organizationIdContactSocialMedia— platform (enum SocialPlatform), handle, url, isPrimaryContactAddress— campos completos de direcciónEmailThread— webaCategory (enum WebaCategory), isArchivedContactInteraction— subject, bodyContactNote— content
No se requiere prisma db push. No se requiere --force-reset. No se requiere --accept-data-loss.
SECCIÓN 16: ENDPOINTS DE API CREADOS
| Método | Endpoint | Propósito |
|---|---|---|
| POST | /api/migration/batch-enrich | Enriquecimiento por lotes de contactos sin jobTitle/companyName mediante análisis LLM de firmas de correo |
| POST | /api/migration/post-verify | Verificación post-migración (duplicados + estadísticas de completitud) |
| POST | /api/migration/archive-old-threads | Archivar hilos con más de N días de antigüedad (predeterminado: 90) |
| POST | /api/contacts/[id]/ai-insights | Análisis LLM bajo demanda de la relación con el contacto |
SECCIÓN 17: PESTAÑAS DE DETALLE DEL CONTACTO (LISTA COMPLETA)
Fila 1
| Pestaña | Fuente de Datos |
|---|---|
| Overview | ContactForm (campos editables) |
| Companies | ContactCompanyRole → CompanyContact |
| Registros ContactEmail | |
| Phone | Registros ContactPhone |
| Addresses | Registros ContactAddress |
| Social Media | Registros ContactSocialMedia |
Fila 2
| Pestaña | Fuente de Datos |
|---|---|
| Activity | Registros ContactInteraction |
| Notes | Registros ContactNote |
| Tags | ContactTag → Tag |
| Calendar | CalendarEvent (mediante asistentes) |
| Signature Intel | Extracción LLM de correos entrantes |
| Timeline | Agregación de EmailMessage (NUEVA) |
| Merge Log | AuditLog con action=contact.merge (NUEVA) |
| Attachments | EmailAttachment con vista previa/pantalla completa (REESCRITA) |
| AI Insights | Análisis LLM bajo demanda (NUEVA) |
| Contracts | Registros ContractSignatory |
Fin del Addendum 06
"Un solo WEBA. Cada canal. En tiempo real, siempre."
Documento preparado por: Agente de Desarrollo WEBA
Validado por: Alain Bessette
Fecha: 6 de junio de 2026
Clasificación: Interno — Especificación Técnica