# PMC-001: Módulo de Tenants

**Versión:** 1.0.0
**Fecha:** 2025-12-08
**Estado:** Definición
**Prioridad:** Alta

---

## Descripción General

El módulo de Tenants proporciona la arquitectura multi-tenant que permite aislar datos y configuraciones entre diferentes organizaciones (agencias) que utilicen la plataforma.

---

## Objetivos

1. Aislar completamente los datos entre tenants
2. Permitir configuración personalizada por tenant
3. Soportar branding personalizado (logo, colores)
4. Gestionar límites y cuotas por tenant
5. Preparar arquitectura para comercialización SaaS futura

---

## Entidades del Dominio

### Tenant

```yaml
Entidad: Tenant
Descripción: Organización/agencia que utiliza la plataforma
Atributos:
  - id: UUID (PK)
  - name: string (nombre de la organización)
  - slug: string (identificador URL-friendly, único)
  - status: enum [active, suspended, trial, cancelled]
  - plan_id: UUID (FK a Plan)
  - settings: JSONB (configuración personalizada)
  - branding: JSONB (logo_url, primary_color, secondary_color)
  - limits: JSONB (cuotas y límites)
  - created_at: timestamp
  - updated_at: timestamp
  - deleted_at: timestamp (soft delete)

Relaciones:
  - 1:N con User
  - 1:N con Client (CRM)
  - 1:N con Project
  - 1:N con Asset
  - N:1 con Plan
```

### Plan

```yaml
Entidad: Plan
Descripción: Plan de suscripción con límites definidos
Atributos:
  - id: UUID (PK)
  - name: string (Free, Pro, Enterprise, Internal)
  - code: string (identificador único)
  - features: JSONB (funcionalidades habilitadas)
  - limits: JSONB
    - generations_per_month: number
    - trainings_per_month: number
    - storage_gb: number
    - users_max: number
    - projects_max: number
  - price_monthly: decimal
  - price_yearly: decimal
  - is_active: boolean
  - created_at: timestamp
  - updated_at: timestamp

Relaciones:
  - 1:N con Tenant
```

### TenantSettings

```yaml
Estructura JSONB: settings
Campos:
  - default_language: string (es, en)
  - timezone: string
  - date_format: string
  - currency: string
  - notifications:
      email_enabled: boolean
      slack_webhook: string
  - integrations:
      crm_external_url: string
      n8n_webhook_base: string
  - generation:
      default_model: string
      default_quality: string
      watermark_enabled: boolean
```

---

## Funcionalidades

### F-001.1: Gestión de Tenants

| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-001.1.1 | Crear tenant | Registro de nueva organización | Alta |
| F-001.1.2 | Editar tenant | Modificar datos y configuración | Alta |
| F-001.1.3 | Suspender tenant | Desactivar acceso temporalmente | Media |
| F-001.1.4 | Eliminar tenant | Soft delete con retención de datos | Baja |

### F-001.2: Configuración por Tenant

| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-001.2.1 | Branding | Personalizar logo y colores | Media |
| F-001.2.2 | Límites | Configurar cuotas de uso | Alta |
| F-001.2.3 | Integraciones | URLs de webhooks y APIs externas | Media |
| F-001.2.4 | Preferencias | Idioma, zona horaria, formatos | Baja |

### F-001.3: Aislamiento de Datos

| ID | Funcionalidad | Descripción | Prioridad |
|----|---------------|-------------|-----------|
| F-001.3.1 | RLS PostgreSQL | Row-Level Security por tenant_id | Alta |
| F-001.3.2 | Middleware | Inyección automática de tenant_id | Alta |
| F-001.3.3 | Storage isolation | Prefijos S3 por tenant | Alta |

---

## Reglas de Negocio

```yaml
RN-001.1:
  Descripción: Todo tenant debe tener un plan asignado
  Validación: plan_id NOT NULL
  Default: Plan "Free" o "Internal"

RN-001.2:
  Descripción: El slug del tenant debe ser único y URL-safe
  Validación: Regex ^[a-z0-9-]+$, único en BD

RN-001.3:
  Descripción: Tenant suspendido no permite login de usuarios
  Acción: Validar status en middleware de autenticación

RN-001.4:
  Descripción: Límites del plan se heredan al tenant
  Override: Tenant puede tener límites personalizados que sobreescriban el plan

RN-001.5:
  Descripción: Soft delete conserva datos 90 días
  Acción: deleted_at marca fecha, cron job limpia después
```

---

## API Endpoints

```yaml
Base: /api/v1/tenants

Endpoints:
  POST /tenants:
    Descripción: Crear nuevo tenant (Super Admin)
    Body: { name, slug, plan_id, settings? }
    Response: 201 Created

  GET /tenants:
    Descripción: Listar tenants (Super Admin)
    Query: ?status=active&page=1&limit=20
    Response: 200 OK (paginado)

  GET /tenants/:id:
    Descripción: Obtener tenant por ID
    Response: 200 OK

  PUT /tenants/:id:
    Descripción: Actualizar tenant
    Body: { name?, settings?, branding?, limits? }
    Response: 200 OK

  PATCH /tenants/:id/status:
    Descripción: Cambiar estado del tenant
    Body: { status: "suspended" | "active" }
    Response: 200 OK

  DELETE /tenants/:id:
    Descripción: Soft delete tenant
    Response: 204 No Content

  GET /tenants/current:
    Descripción: Obtener tenant del usuario actual
    Response: 200 OK
```

---

## Dependencias

```yaml
Dependencias del Catálogo:
  - @CATALOG_TENANT:
      path: core/catalog/multi-tenancy/
      uso: Patron base multi-tenancy con RLS
      adaptar:
        - Extender modelo Tenant con campos branding
        - Agregar sistema de quotas por plan
        - Integrar con limites de generacion
      docs: core/catalog/multi-tenancy/README.md

  - @CATALOG_PAYMENTS:
      path: core/catalog/payments/
      uso: Suscripciones y billing (Fase 4)
      adaptar:
        - Crear productos/precios en Stripe
        - Definir planes PMC (Starter, Pro, Business, Enterprise)
        - Integrar creditos de generacion
      docs: core/catalog/payments/README.md

Dependencias de Módulos:
  - PMC-007 Admin: Gestión de planes y configuración global

Servicios Externos:
  - Stripe (Fase 4 - SaaS comercial)

Referencia de Implementacion:
  - Proyecto origen: projects/gamilit/apps/backend/src/modules/auth/entities/tenant.entity.ts
  - RLS policies: projects/gamilit/apps/database/ddl/policies/
```

---

## Consideraciones Técnicas

### Estrategia Multi-Tenant

```yaml
Tipo: Single Database, Shared Schema
Aislamiento: Row-Level Security (RLS) en PostgreSQL

Implementación:
  1. Columna tenant_id en todas las tablas principales
  2. Políticas RLS que filtran por tenant_id
  3. Middleware que extrae tenant del JWT/sesión
  4. SET app.current_tenant antes de cada query
```

### Ejemplo RLS PostgreSQL

```sql
-- Política para tabla clients
CREATE POLICY tenant_isolation_policy ON clients
  USING (tenant_id = current_setting('app.current_tenant')::uuid);

-- Habilitar RLS
ALTER TABLE clients ENABLE ROW LEVEL SECURITY;
```

### Storage Isolation

```yaml
Estructura S3:
  bucket/
    ├── {tenant_slug}/
    │   ├── assets/
    │   ├── generated/
    │   ├── models/
    │   └── temp/
```

---

## Criterios de Aceptación

- [ ] Tenant puede ser creado con datos mínimos (name, slug, plan)
- [ ] RLS impide acceso a datos de otros tenants
- [ ] Branding se refleja en UI del tenant
- [ ] Límites del plan se validan antes de operaciones
- [ ] Soft delete funciona correctamente
- [ ] API responde correctamente a todos los endpoints

---

## Referencias

- [ARQUITECTURA-TECNICA.md](../00-vision-general/ARQUITECTURA-TECNICA.md)
- [@CATALOG_TENANT](../../../core/catalog/modules/multi-tenancy/)

---

**Documento generado por:** Requirements-Analyst
**Fecha:** 2025-12-08
