465 lines
12 KiB
Markdown
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
|