erp-core/docs/04-modelado/domain-models/messaging-domain.md

465 lines
12 KiB
Markdown

# 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