# MODELO DE DOMINIO: Mensajería y Notificaciones **Módulos:** MGN-012 (Reportes), MGN-014 (Mensajería y Notificaciones) **Fecha:** 2025-11-24 **Referencia Odoo:** mail (mail.thread, mail.message, mail.followers, mail.activity) **Referencia Gamilit:** notifications_management schema --- ## Diagrama de Entidades (Texto UML) ``` [Message] - id: UUID (PK) - tenant_id: UUID (FK) - author_id: UUID (FK user) - model: String (ej: "SaleOrder") - res_id: UUID (ID del registro) - message_type: ENUM (note, comment, notification, email) - subject: String - body: Text - parent_id: UUID (FK self) - subtype_id: UUID (FK) 1 <----> * [Attachment] 1 <----> * [Notification] [Follower] - id: UUID (PK) - tenant_id: UUID (FK) - model: String - res_id: UUID - partner_id: UUID (FK) - user_id: UUID (FK) [Notification] - id: UUID (PK) - tenant_id: UUID (FK) - message_id: UUID (FK) - user_id: UUID (FK) - notification_type: ENUM (inbox, email, push) - is_read: Boolean - read_at: Timestamp [Activity] - id: UUID (PK) - tenant_id: UUID (FK) - model: String - res_id: UUID - activity_type_id: UUID (FK) - user_id: UUID (FK) - assigned_by: UUID (FK user) - summary: String - note: Text - date_deadline: Date - status: ENUM (pending, done, overdue, cancelled) - completed_at: Timestamp [ActivityType] - id: UUID (PK) - tenant_id: UUID (FK) - name: String - icon: String - sequence: Integer - default_user_id: UUID (FK) [MessageSubtype] - id: UUID (PK) - name: String - description: Text - internal: Boolean - parent_id: UUID (FK self) [Attachment] - id: UUID (PK) - tenant_id: UUID (FK) - name: String - file_path: String - file_size: Integer - mimetype: String - checksum: String - model: String - res_id: UUID [EmailTemplate] - id: UUID (PK) - tenant_id: UUID (FK) - name: String - model: String - subject: String (Handlebars template) - body_html: Text (Handlebars template) - email_from: String - email_to: String [TrackingValue] - id: UUID (PK) - tenant_id: UUID (FK) - message_id: UUID (FK) - field_name: String - field_type: ENUM (char, integer, float, boolean, many2one, date) - old_value: String - new_value: String ``` ## Entidades Principales ### 1. Message (Mensaje/Chatter) **Descripción:** Mensaje asociado a un registro (patrón mail.thread). **Atributos:** - `id`: UUID - `author_id`: Autor del mensaje (user) - `model`: Modelo del registro (ej: "SaleOrder") - `res_id`: ID del registro asociado - `message_type`: note, comment, notification, email - `subject`: Asunto - `body`: Cuerpo del mensaje (HTML) - `parent_id`: Mensaje padre (replies) - `subtype_id`: Subtipo de mensaje **Relaciones:** - 1 Message → N Attachments - 1 Message → N Notifications - 1 Message → N TrackingValues **Patrón Odoo:** mail.message **Tipos:** - note: Nota interna (solo empleados) - comment: Comentario (visible a followers) - notification: Notificación automática - email: Email enviado/recibido ### 2. Follower (Seguidor) **Descripción:** Usuario que sigue un registro (recibe notificaciones). **Atributos:** - `id`: UUID - `model`: Modelo del registro - `res_id`: ID del registro - `partner_id`: Partner seguidor - `user_id`: Usuario seguidor **Relaciones:** - N Followers → 1 User/Partner **Patrón Odoo:** mail.followers **Auto-follow:** - Usuario que crea registro se vuelve follower automáticamente - Usuario asignado (assigned_to) se vuelve follower - Usuario mencionado (@username) se vuelve follower ### 3. Notification (Notificación) **Descripción:** Notificación enviada a usuario. **Atributos:** - `id`: UUID - `message_id`: Mensaje asociado - `user_id`: Usuario destinatario - `notification_type`: inbox, email, push - `is_read`: Leída/No leída - `read_at`: Timestamp de lectura **Relaciones:** - N Notifications → 1 Message - N Notifications → 1 User **Patrón Odoo:** mail.notification **Tipos:** - inbox: Notificación in-app (campana) - email: Email enviado - push: Push notification (mobile/web) ### 4. Activity (Actividad Programada) **Descripción:** Actividad programada (tarea, llamada, reunión). **Atributos:** - `id`: UUID - `model`: Modelo del registro asociado - `res_id`: ID del registro - `activity_type_id`: Tipo de actividad - `user_id`: Usuario asignado - `assigned_by`: Usuario que asignó - `summary`: Resumen - `note`: Nota adicional - `date_deadline`: Fecha límite - `status`: pending, done, overdue, cancelled - `completed_at`: Fecha de completado **Relaciones:** - N Activities → 1 ActivityType - N Activities → 1 User (asignado) **Patrón Odoo:** mail.activity **Estados:** - pending: Pendiente - done: Completada - overdue: Vencida (date_deadline < HOY) - cancelled: Cancelada ### 5. ActivityType (Tipo de Actividad) **Descripción:** Tipo de actividad programable. **Atributos:** - `id`: UUID - `name`: Nombre (ej: "Llamada", "Reunión") - `icon`: Icono para UI - `sequence`: Orden - `default_user_id`: Usuario por defecto **Relaciones:** - 1 ActivityType → N Activities **Patrón Odoo:** mail.activity.type **Tipos estándar:** - Call (Llamada telefónica) - Meeting (Reunión) - Email (Enviar email) - To Do (Tarea general) ### 6. MessageSubtype (Subtipo de Mensaje) **Descripción:** Subtipo para clasificar mensajes. **Atributos:** - `id`: UUID - `name`: Nombre (ej: "Status Change", "Stage Changed") - `description`: Descripción - `internal`: Solo interno (no visible a followers externos) - `parent_id`: Subtipo padre **Relaciones:** - 1 MessageSubtype → N Messages **Patrón Odoo:** mail.message.subtype **Subtipos estándar:** - Discussions (Comentarios) - Note (Notas internas) - Status Change (Cambio de estado) - Stage Changed (Cambio de etapa) ### 7. Attachment (Adjunto) **Descripción:** Archivo adjunto a mensaje o registro. **Atributos:** - `id`: UUID - `name`: Nombre del archivo - `file_path`: Ruta en storage (S3 o filesystem) - `file_size`: Tamaño en bytes - `mimetype`: Tipo MIME - `checksum`: Hash SHA256 - `model`: Modelo asociado - `res_id`: ID del registro **Relaciones:** - N Attachments → 1 Message - N Attachments → 1 Registro (genérico) **Patrón Odoo:** ir.attachment **Storage:** S3 o filesystem local ### 8. EmailTemplate (Template de Email) **Descripción:** Template configurable para emails. **Atributos:** - `id`: UUID - `name`: Nombre del template - `model`: Modelo asociado - `subject`: Asunto (Handlebars) - `body_html`: Cuerpo HTML (Handlebars) - `email_from`: Email remitente - `email_to`: Email destinatario (template) **Relaciones:** - 1 EmailTemplate → N Emails enviados **Patrón Odoo:** mail.template **Variables disponibles:** ```handlebars {{user.name}} {{company.name}} {{order.name}} {{order.amount_total}} ``` ### 9. TrackingValue (Valor Trackeado) **Descripción:** Cambio de valor en campo trackeado. **Atributos:** - `id`: UUID - `message_id`: Mensaje asociado - `field_name`: Nombre del campo - `field_type`: Tipo de campo - `old_value`: Valor anterior - `new_value`: Valor nuevo **Relaciones:** - N TrackingValues → 1 Message **Patrón Odoo:** mail.tracking.value **Ejemplo:** ``` Field: status Old: draft New: confirmed → Mensaje: "User X changed Status from Draft to Confirmed" ``` ## Reglas de Negocio ### RN-MSG-001: Auto-Follow - Usuario que crea registro → follower automático - Usuario asignado (assigned_to) → follower automático - Usuario mencionado (@username) → follower automático ### RN-MSG-002: Notificaciones a Followers - Al crear mensaje tipo 'comment', notificar a followers - Al crear mensaje tipo 'note' (internal), no notificar a followers externos ### RN-MSG-003: Tracking Automático - Decorador `@TrackChanges(['status', 'amount'])` en modelo - Al actualizar campo, generar mensaje automático con TrackingValues - Mensaje subtipo: "Status Change" o similar ### RN-MSG-004: Actividades Vencidas - Sistema marca activities con status='overdue' si date_deadline < HOY - Enviar notificación diaria de actividades vencidas ### RN-MSG-005: Email desde Templates - Al enviar email, reemplazar variables Handlebars - Variables disponibles: registro, user, company - Guardar email enviado como Message tipo='email' ### RN-MSG-006: Notificaciones Multi-Canal - Usuario puede configurar preferencias: - inbox: Siempre - email: Solo para menciones - push: Nunca - Sistema respeta preferencias al crear Notification ### RN-MSG-007: WebSocket Real-Time - Notificaciones inbox se envían por WebSocket (Socket.IO) - Cliente recibe notificación instantánea - Badge count actualizado en tiempo real ### RN-MSG-008: Chatter Genérico - TODO modelo puede tener chatter (mixin) - Patrón: model + res_id + messages - UI consistente en todos los módulos ## Casos de Uso Principales 1. **UC-MSG-001:** Usuario comenta en orden de venta (chatter) 2. **UC-MSG-002:** Sistema registra cambio automático (tracking) 3. **UC-MSG-003:** Usuario recibe notificación in-app 4. **UC-MSG-004:** Usuario recibe notificación por email 5. **UC-MSG-005:** Usuario menciona a otro (@username) en comentario 6. **UC-MSG-006:** Usuario programa actividad (llamada) 7. **UC-MSG-007:** Sistema marca actividad como overdue 8. **UC-MSG-008:** Usuario sigue orden de compra (follow) 9. **UC-MSG-009:** Sistema envía email desde template 10. **UC-MSG-010:** Usuario adjunta archivo en mensaje ## Validaciones y Constraints ```sql -- Message tipo válido CHECK (message_type IN ('note', 'comment', 'notification', 'email')) -- Activity status válido CHECK (status IN ('pending', 'done', 'overdue', 'cancelled')) -- Notification type válido CHECK (notification_type IN ('inbox', 'email', 'push')) -- Attachment file_size > 0 CHECK (file_size > 0) -- Activity deadline no puede ser pasado al crear -- (validación en backend) -- Follower único por model + res_id + user_id UNIQUE (tenant_id, model, res_id, user_id) ``` ## Índices Requeridos ```sql CREATE INDEX idx_messages_model_res_id ON system.messages(model, res_id); CREATE INDEX idx_messages_author_id ON system.messages(author_id); CREATE INDEX idx_messages_message_type ON system.messages(message_type); CREATE INDEX idx_followers_model_res_id ON system.followers(model, res_id); CREATE INDEX idx_followers_user_id ON system.followers(user_id); CREATE INDEX idx_notifications_user_id ON system.notifications(user_id); CREATE INDEX idx_notifications_is_read ON system.notifications(is_read); CREATE INDEX idx_activities_user_id ON system.activities(user_id); CREATE INDEX idx_activities_status ON system.activities(status); CREATE INDEX idx_activities_date_deadline ON system.activities(date_deadline); CREATE INDEX idx_attachments_model_res_id ON system.attachments(model, res_id); CREATE INDEX idx_tracking_values_message_id ON system.tracking_values(message_id); ``` ## Integración con Otros Módulos ### Con TODOS los Módulos - Chatter es transversal: todos los modelos pueden tener mensajes - Tracking automático en modelos transaccionales - Actividades programables en cualquier registro ### Con MGN-001 (Fundamentos) - Messages de User - Notifications a User - Followers son Users/Partners ### Con MGN-007 (Ventas) - Cotizaciones con chatter - Tracking de cambios de status - Actividades de seguimiento a cliente ### Con MGN-011 (Proyectos) - Tareas con chatter - Actividades por tarea - Followers de proyecto ### Con MGN-013 (Portal) - Cliente ve mensajes en portal - Cliente puede comentar (tipo='comment') - Cliente NO ve mensajes internos (type='note') ## Implementación Técnica ### Mixin de Chatter (Backend) ```typescript // Decorador para modelos con chatter @HasChatter() export class SaleOrder { // ...campos } // Decorador para tracking @TrackChanges(['status', 'amount_total', 'partner_id']) export class SaleOrder { status: string; amount_total: number; partner_id: string; } ``` ### WebSocket (Real-Time) ```typescript // Enviar notificación en tiempo real socket.to(`user:${userId}`).emit('notification', { id: notification.id, message: notification.message, type: notification.type }); ``` ## Referencias - [ALCANCE-POR-MODULO.md - MGN-014](../../01-definicion-modulos/ALCANCE-POR-MODULO.md#mgn-014) - [ADR-007: Database Design](../../adr/ADR-007-database-design.md) - [odoo-mail-analysis.md](../../00-analisis-referencias/odoo/odoo-mail-analysis.md) --- **Importancia:** ⭐⭐⭐⭐⭐ ESENCIAL para auditoría y colaboración