# Sprint 1.1 · Schema de Sistema (auth + RBAC + audit)

> **Para vos, Ricci.** Este documento explica las 8 tablas en castellano. Después de que Claude Code las cree, leelo y validalo. No tenés que entender SQL avanzado — basta con entender qué guarda cada tabla y por qué.

---

## ¿Qué vamos a crear en este sprint?

Las **8 tablas mínimas** que cualquier sistema multi-usuario necesita: usuarios, roles, permisos y registro de actividad. Sin esto no podemos hacer login.

Estas 8 tablas viven en **`innovium_demo`** (la BD del tenant `demo`). Más adelante en Sprint 1.2 las replicamos en `innovium_infinia`. Las tablas de `innovium_master` (gestión de tenants) son distintas y van en Sprint 1.2.

---

## Las 8 tablas

### 1. `users`
> Los usuarios que pueden entrar al sistema (vendedores, gerentes, contadores, etc).

| Columna | Tipo | Para qué |
|---|---|---|
| `id` | BIGINT auto | identificador único |
| `nombre` | VARCHAR(150) | nombre completo (ej: "María González") |
| `email` | VARCHAR(180) UNIQUE | usado para login |
| `password_hash` | VARCHAR(255) | password encriptado con Argon2id |
| `rut` | VARCHAR(12) | RUT chileno (ej: "12.345.678-9") |
| `telefono` | VARCHAR(20) NULL | celular |
| `avatar_url` | VARCHAR(500) NULL | foto de perfil (path R2) |
| `activo` | TINYINT(1) | 1=activo, 0=desactivado |
| `ultimo_login_en` | DATETIME NULL | para auditoría |
| `ultimo_login_ip` | VARCHAR(45) NULL | IPv4 o IPv6 |
| `creado_en` | DATETIME | timestamp creación |
| `actualizado_en` | DATETIME | timestamp última edición |
| `eliminado_en` | DATETIME NULL | soft delete |

**Decisión clave:** los passwords nunca se guardan en texto plano. Se guarda solo el hash Argon2id. Si alguien roba la BD, no puede recuperar passwords.

---

### 2. `roles`
> Los 6 roles fijos del sistema.

| Columna | Tipo | Para qué |
|---|---|---|
| `id` | BIGINT auto | id |
| `slug` | VARCHAR(50) UNIQUE | código (ej: "vendedor") |
| `nombre` | VARCHAR(100) | nombre legible (ej: "Vendedor") |
| `descripcion` | TEXT NULL | qué puede hacer este rol |
| `nivel` | TINYINT | jerarquía 1-100 (mayor = más poder) |
| `activo` | TINYINT(1) | |
| `creado_en` | DATETIME | |
| `actualizado_en` | DATETIME | convención global (CLAUDE.md) |

**Los 6 roles que vamos a sembrar:**

| Slug | Nombre | Nivel | Qué puede hacer |
|---|---|---|---|
| `superadmin` | Super Admin | 100 | Cross-tenant, solo Crono Systems |
| `tenant_admin` | Admin Funeraria | 90 | Configura el tenant, crea usuarios |
| `gerente` | Gerente | 70 | Ve todo, dashboards, reportes |
| `vendedor` | Vendedor | 40 | Crea contratos, sus propios + asignados |
| `operativo` | Operativo | 35 | Agenda velatorios, capillas, vehículos |
| `contador` | Contador | 50 | Cobranzas, registra pagos, reportes financieros |

---

### 3. `permissions`
> Permisos granulares del sistema (acciones específicas).

| Columna | Tipo | Para qué |
|---|---|---|
| `id` | BIGINT auto | |
| `slug` | VARCHAR(100) UNIQUE | código (ej: "contratos.crear") |
| `nombre` | VARCHAR(150) | nombre legible |
| `categoria` | VARCHAR(50) | agrupación (ej: "contratos", "cobranzas") |
| `descripcion` | TEXT NULL | |
| `creado_en` | DATETIME | |
| `actualizado_en` | DATETIME | convención global (CLAUDE.md) |

**Ejemplos de permisos** (estos los sembramos en Sprint 1.1):

- `contratos.crear`, `contratos.ver_propios`, `contratos.ver_todos`, `contratos.editar`, `contratos.anular`
- `cobranzas.ver`, `cobranzas.registrar_pago`, `cobranzas.exportar`
- `usuarios.crear`, `usuarios.editar`, `usuarios.desactivar`
- `reportes.ver_basicos`, `reportes.ver_financieros`, `reportes.exportar`
- `productos.gestionar`, `planes.crear`, `planes.editar`
- `tenant.configurar`

Más adelante agregamos más a medida que crecen los módulos.

---

### 4. `role_permissions`
> Tabla pivot — qué permisos tiene cada rol.

| Columna | Tipo | Para qué |
|---|---|---|
| `id` | BIGINT auto | |
| `role_id` | BIGINT FK | |
| `permission_id` | BIGINT FK | |
| `creado_en` | DATETIME | |

**Constraint:** UNIQUE en `(role_id, permission_id)` para evitar duplicados.

---

### 5. `user_roles`
> Tabla pivot — qué rol tiene cada usuario.

| Columna | Tipo | Para qué |
|---|---|---|
| `id` | BIGINT auto | |
| `user_id` | BIGINT FK | |
| `role_id` | BIGINT FK | |
| `asignado_por` | BIGINT FK NULL | quién asignó este rol (audit) |
| `asignado_en` | DATETIME | cuándo |

**Decisión:** un usuario puede tener **un solo rol** en Innovium (por simplicidad). Si en el futuro queremos multi-rol, la tabla ya está preparada (UNIQUE en `user_id` solo, no en el par).

---

### 6. `sessions`
> Sesiones activas (cuando un usuario hace login).

| Columna | Tipo | Para qué |
|---|---|---|
| `id` | VARCHAR(64) PRIMARY | session token aleatorio |
| `user_id` | BIGINT FK | |
| `ip` | VARCHAR(45) | IP desde donde se loguearon |
| `user_agent` | VARCHAR(500) | navegador/dispositivo |
| `iniciada_en` | DATETIME | |
| `ultima_actividad_en` | DATETIME | para timeout |
| `expira_en` | DATETIME | hasta cuándo dura |
| `recordarme` | TINYINT(1) | si marcó "Recordarme" |

**Decisión:** sesiones en BD (no archivos). Permite invalidar todas las sesiones de un usuario fácilmente, ver desde dónde está logueado, hacer "cerrar sesión en otros dispositivos".

---

### 7. `password_resets`
> Tokens de recuperación de contraseña (para cuando agreguemos esa feature en Sprint 2.x).

| Columna | Tipo | Para qué |
|---|---|---|
| `id` | BIGINT auto | |
| `user_id` | BIGINT FK | |
| `token_hash` | VARCHAR(255) UNIQUE | hash del token (no se guarda el token plano) |
| `creado_en` | DATETIME | |
| `expira_en` | DATETIME | normalmente 1 hora |
| `usado_en` | DATETIME NULL | si ya se usó |

**Importante:** la tabla se crea ahora pero **la feature de "olvidé mi password" NO se implementa en Sprint 1.1**. Solo dejamos la mesa servida para Sprint 2.x.

---

### 8. `audit_log`
> Registro inmutable de todo lo que pasa en el sistema. **No se borra nunca.**

| Columna | Tipo | Para qué |
|---|---|---|
| `id` | BIGINT auto | |
| `user_id` | BIGINT FK NULL | quién hizo la acción (NULL si es sistema) |
| `accion` | VARCHAR(100) | ej: "user.login", "contrato.creado" |
| `entidad_tipo` | VARCHAR(50) NULL | ej: "Contrato", "Cuota" |
| `entidad_id` | BIGINT NULL | id del registro afectado |
| `datos_antes` | JSON NULL | estado previo (en updates) |
| `datos_despues` | JSON NULL | estado nuevo |
| `ip` | VARCHAR(45) | |
| `user_agent` | VARCHAR(500) NULL | |
| `creado_en` | DATETIME | timestamp exacto |

**Acciones que vamos a registrar en Sprint 1.1:**
- `user.login` — usuario hace login OK
- `user.login_fallido` — intento fallido (sin user_id si el email no existe)
- `user.logout`
- `user.password_cambiado`

Más acciones se registran en sprints siguientes (creación de contratos, pagos, etc).

---

## Índices que Claude Code va a crear

Para que las queries sean rápidas:

- `users(email)` — para login
- `users(rut)` — para búsquedas
- `sessions(user_id)` — listar sesiones de un user
- `sessions(expira_en)` — limpiar sesiones expiradas
- `audit_log(user_id, creado_en)` — ver historial de un user
- `audit_log(entidad_tipo, entidad_id)` — ver historial de un contrato/cuota específico

---

## Foreign Keys importantes

Todas con `ON DELETE RESTRICT` por defecto. Razón: NUNCA queremos perder data por borrado en cascada. Si querés borrar un usuario, primero hay que reasignar/anular sus contratos.

Excepciones (`ON DELETE CASCADE`):
- `sessions.user_id` → si se borra un usuario, sus sesiones también
- `password_resets.user_id` → ídem
- `user_roles.user_id` → ídem

---

## Lo que NO está en este sprint (para que no esperes encontrarlo)

- 🚫 `tenants` y resto de master DB (Sprint 1.2)
- 🚫 `productos`, `planes_lineas`, `categorias` (Sprint 1.3)
- 🚫 `clientes`, `contratos`, `cuotas`, `firmas` (Sprint 1.5)
- 🚫 Lockout por intentos fallidos (Sprint 2.x)
- 🚫 Recuperación de password por email (Sprint 2.x)
- 🚫 2FA (Sprint 3.x o 4.x si nos da tiempo)

---

## Cómo validar este sprint cuando Claude Code termine

Ver `CRITERIOS_ACEPTACION_1_1.md` (archivo aparte). Esa es la checklist.
