> ## Documentation Index
> Fetch the complete documentation index at: https://internal.softcrum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Actividades

> Cuando esto se entregue, "llamar de vuelta a este cliente el jueves" es algo que el sistema recuerda en vez de un papel en el escritorio de alguien.

> Traducción. Autoritativo: [`fs-crm-0002-activities.md`](/modules/crm/features/fs-crm-0002-activities).

## Contexto

Una actividad es la unidad más pequeña útil de seguimiento: algo que hacer, sobre un cliente, para una
fecha, por alguien.

`crm.activity_types` es uno de **solo dos catálogos extensibles por tenant en la plataforma** (DEC-B4),
y la razón es exactamente la regla del estándar de datos: los valores son el vocabulario de negocio del
tenant. Un gimnasio registra "inasistencia a clase", una clínica "recordatorio de hora", un distribuidor
"visita trimestral". Eso no nos toca enumerarlo. Lo que sigue siendo nuestro es la máquina de estados —
una actividad está pendiente, completada o cancelada, y un tenant no puede agregar un cuarto estado,
porque el estado gobierna lógica.

## Alcance *(normativo)*

* `crm.activities` sobre un contacto: tipo, descripción, vencimiento, asignado, estado.
* `crm.activity_types`, paramétrico y **extensible por tenant**, con semillas `is_system`.
* Asignación a un usuario de la misma organización.
* Completado con una nota de resultado.
* Detección y listado de vencidas.

## Fuera de alcance *(normativo)*

* Recordatorios y notificaciones — F1b, por `messaging` y el evento `activity.created`.
* Actividades recurrentes. Un motor de recurrencia es un scheduler, y reusar el de campañas necesita su
  propio diseño.
* SLAs, colas y escalamiento. Eso es ticketing, y es un no-objetivo del módulo.
* Sincronización con calendarios — no planificado.

## Comportamiento *(normativo)*

1. Una actividad pertenece a un contacto, tiene un tipo, y está `pending`, `completed` o `cancelled`. El
   conjunto de estados **no es extensible por tenant**: gobierna la lógica de vencidos y la reportería.
2. `activity_types` **sí** es extensible por tenant. Las filas del tenant son `is_system = false`,
   acotadas a él, y un tenant nunca puede modificar ni desactivar una fila de sistema.
3. El asignado debe ser un **miembro activo de la misma organización**. Asignar a alguien que se fue se
   rechaza, lo que además empuja a mantener las membresías al día.
4. La fecha de vencimiento es opcional. Una actividad sin ella es un recordatorio sin urgencia, que es
   algo que la gente efectivamente anota.
5. El completado registra **quién y cuándo**, más un resultado opcional. Reabrir una actividad completada
   no se permite — lo correcto es una nueva, para que la historia siga siendo honesta.
6. "Vencida" se **deriva**, nunca se guarda. Un flag guardado necesita un job que lo mantenga y estará
   equivocado entre corridas.
7. Desactivar una membresía deja las actividades asignadas a esa persona y las **expone para
   reasignación**, en vez de reasignarlas en silencio o dejarlas huérfanas.
8. Toda mutación escribe en `core.audit_log`.

## Datos *(normativo)*

| Tabla                | Invariantes clave                                                                                                                                                                                                                                          |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `crm.activities`     | `tenant_id`, FK `contact_id`; `type_code` FK; `description`; `due_at` nullable; `assignee_user_id` nullable; `state` pending\|completed\|cancelled; `completed_by`, `completed_at`, `outcome`; índice (`tenant_id`, `assignee_user_id`, `state`, `due_at`) |
| `crm.activity_types` | paramétrica; **extensible por tenant** (DEC-B4); semillas `is_system` `call`, `email`, `meeting`, `task`, `visit`, `follow_up`                                                                                                                             |

## API *(normativo)*

| Endpoint                                | Clase      | Permiso                             | Presupuesto |
| --------------------------------------- | ---------- | ----------------------------------- | ----------- |
| `GET /v1/crm/activities`                | Management | `crm.activities.read`               | p95 \<1 s   |
| `POST /v1/crm/contacts/{id}/activities` | Management | `crm.activities.create`             | p95 \<1 s   |
| `PATCH /v1/crm/activities/{id}`         | Management | `crm.activities.update`             | p95 \<1 s   |
| `POST /v1/crm/activities/{id}/complete` | Management | `crm.activities.complete`           | p95 \<1 s   |
| `GET/POST /v1/crm/activity-types`       | Management | `crm.activity_types.{read\|create}` | p95 \<1 s   |

## Eventos *(normativo)*

`crm.activity.created` y `crm.activity.completed`. El primero es lo que consumirá la feature de
recordatorios de F1b — el evento existe ahora para que agregarla no requiera cambios acá.

## Criterios de aceptación *(normativo)*

1. Un tipo de actividad creado por un tenant es visible solo para ese tenant.
2. Un tenant no puede modificar ni desactivar un tipo `is_system`.
3. Asignar a un usuario fuera de la organización, o a una membresía desactivada, se rechaza.
4. "Vencida" se deriva en lectura y es correcta entre zonas horarias.
5. Una actividad completada no se puede reabrir; la API lo dice con error tipado.
6. Desactivar una membresía expone sus actividades abiertas para reasignación.
7. El completado registra quién, cuándo y el resultado.
8. **Negativo:** ningún endpoint agrega un cuarto estado de actividad.

## Ejecución

Un solo slice, comando síncrono. Nada asíncrono — los recordatorios de F1b consumirán el evento.

## Preguntas abiertas

| # | Pregunta                                                                                                                      | Decide | Para             |
| - | ----------------------------------------------------------------------------------------------------------------------------- | ------ | ---------------- |
| 1 | ¿Una actividad se puede asignar a cualquier usuario, o solo al dueño de la cuenta? (pregunta 2 del PRD)                       | Daniel | antes de aprobar |
| 2 | ¿Una actividad debería poder colgarse de un evento de fidelización, para que "hacer seguimiento a este canje" quede enlazado? | Daniel | antes de aprobar |

## Changelog

| Versión | Fecha      | Cambio           | Por qué | Autor                  |
| ------- | ---------- | ---------------- | ------- | ---------------------- |
| 0.1.0   | 2026-08-17 | Borrador inicial | —       | daniel + claude-opus-5 |

## Registro de entrega

*Aún no implementado.*
