# 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
