# Plan — Refactor de Módulos, Submódulos, Privilegios y Perfiles

---

## Estado de implementación (actualizado 2026-06-03)

### ✅ Fase 0 — Esquema Prisma (additive, sin TRUNCATE)
- Migración `prisma/migrations/20260529_seguridad_jerarquia_fase0/migration.sql` aplicada.
- Tablas nuevas: `submodulos`, `suscripcion_submodulos`, `plan_submodulos_addon`.
- Columnas agregadas: `modulos.icono`, `modulos.orden`, `privilegios.submodulo_id` (nullable de momento), `privilegios.recurso`, `privilegios.accion`, `privilegios.is_sistema` (default true).
- `schema.prisma` actualizado con los 3 modelos nuevos y relaciones en `modulos`, `privilegios`, `perfiles`, `planes`, `suscripciones`.
- Migración adicional `20260529_perfiles_is_sistema` agrega `perfiles.is_sistema` + índice.
- **No se truncó nada** — `perfiles_privilegios` legacy intacto, convive con el nuevo modelo.

### ✅ Fase 1 — Seeds idempotentes
- `src/seguridad/seeds/seguridad.seed-data.ts` con catálogo maestro: **10 módulos raíz** (VENTAS, COBRANZAS, COMPRAS, INVENTARIO, CONTABILIDAD, TESORERIA, RRHH, IMPORTACIONES, ADMINISTRACION, REPORTES), **62 submódulos** (con `es_addon=true` en Rutas, Logística, Lotes, Liquidaciones IA, Dashboard IA, Ayuda IA), **227 privilegios** estandarizados `<MODULO>_<RECURSO>_<ACCION>`.
- `PERFILES_SISTEMA_SEED` con 8 perfiles: Administrador, Cajero, Vendedor, Cobrador, Contador, Encargado Stock, Encargado Compras, Solo Lectura. Usan selectores `*`, `MOD.*`, `*.ACCION`, `SUBMOD.*`.
- `src/seguridad/seeds/seed-orchestrator.service.ts` — upsert por código (idempotente). `runPerfilesSistemaParaEmpresa` crea perfiles con `is_sistema: true` resolviendo selectores.
- CLI `src/seguridad/seeds/seed-cli.ts` con modos `catalogo | perfiles | all` (requiere `--empresa=<UUID>` para perfiles).
- Ejecutado en dev — catálogo seed OK.

### ✅ Fase 2 — Backend NestJS `/seguridad/*`
- `SeguridadModule` registrado en `AppModule`.
- DTO `upsert-perfil.dto.ts` (perfil, desc_perfil, privilegio_ids).
- `SeguridadService`: `getArbol`, `listarPerfiles`, `obtenerPerfil`, `crearPerfil`, `actualizarPerfil` (bloquea si `is_sistema`), `clonarPerfil` ("Copia de X"), `eliminarPerfil` (bloquea si `is_sistema` o tiene usuarios), `cargarPerfilesSugeridos`, `validarPrivilegios`.
- Auditoría en todas las mutaciones con `entity_type='perfil'`.
- Controller endpoints:
  - `GET /seguridad/modulos/arbol`
  - `GET /seguridad/perfiles`
  - `GET /seguridad/perfiles/:id`
  - `POST /seguridad/perfiles`
  - `PATCH /seguridad/perfiles/:id`
  - `DELETE /seguridad/perfiles/:id`
  - `POST /seguridad/perfiles/:id/clonar`
  - `POST /seguridad/perfiles/cargar-sugeridos`
  - `POST /seguridad/seed-maestro`

### ✅ Fase 3 — Frontend (editor de perfiles 2 columnas)
- `src/api/seguridad.service.js` — wrappers axios para todos los endpoints.
- `src/tanstack/SeguridadStack.jsx` — hooks TanStack con invalidaciones (`useArbolModulosQuery`, `usePerfilesV2Query`, `usePerfilV2Query`, mutaciones crear/actualizar/clonar/eliminar/cargar-sugeridos).
- `src/components/organismos/Seguridad/PerfilEditorDialog.jsx` — dialog 2 columnas: sidebar módulos raíz con contador `sel/total`, panel submódulos con "Seleccionar todo" + checkboxes, búsqueda global, chips Addon, **modo read-only cuando `is_sistema=true`** con CTA "Cloná para editar".
- `src/pages/SeguridadPerfiles.jsx` — list page con `ScreenGuia`, `EmptyState`, tabla (Perfil/Descripción/Privilegios/Usuarios/Acciones), botón "Cargar perfiles sugeridos" visible solo si no hay perfiles del sistema.

### ✅ Wire-up en Configuración
- En `src/pages/ConfiguracionNew.jsx`, el ítem **"Usuarios y Permisos → Perfiles y Roles"** ahora renderiza `<SeguridadPerfiles />` (reemplaza al legacy `PerfilesListConfig`).
- Acceso desde la sidebar de Configuración existente — sin ruta dedicada por ahora.

---

## Pendiente

### ✅ Fase 4 — Editor de planes (suscripción con addons) — COMPLETA
- ✅ Schema: `plan_modulos`, `plan_submodulos`, `plan_submodulos_addon` con sync delta (`syncModulosRaiz`, `syncSubmodulosIncluidos`, `syncAddons`).
- ✅ `privilegios.submodulo_id` NOT NULL aplicado.
- ✅ Endpoint `/planes/jerarquia` + UI `PlanModal` con módulo raíz toggle + submódulos checkables + addons con precio_override.
- ✅ UI admin para `suscripcion_submodulos` — `GestionModulosDialog` muestra módulos contratados + adicionales + chips de submódulos no-addon (excluibles por empresa) y addons (contratables por empresa).
- ✅ Endpoints holding: `GET/PATCH /suscripciones/holding/:suscripcionId/submodulos-incluidos` y `/addons`.
- ✅ `getSubmodulosActivos` aplica exclusiones por empresa (`suscripcion_submodulos.active=false` para no-addon).
- ✅ `ChangePlan` admite override editable de `fecha_inicio`, `fecha_fin`, `costo_mensual`, `dia_cobro`.
- ✅ Guard backend a nivel submódulo: `@RequireSubmodulo` + `SubmoduloGuard` (`src/auth/guards/submodulo.guard.ts`) ya existían; se reforzó la lógica para que también rechace submódulos no-addon excluidos per-empresa (`suscripcion_submodulos.active=false`).

### ✅ Fase 5 — Transversal (descubrimiento y navegación) — COMPLETA
- ✅ Sidebar refleja jerarquía Módulo → Submódulo: items gated por `modulo` + `submodulo` (ej. Dashboard normal → `REPORTES/REP_DASHBOARD`, Dashboard IA → `REPORTES/REP_DASHBOARD_IA`).
- ✅ Nuevo submódulo `REP_DASHBOARD` en seed para gating del dashboard normal (permite que una empresa contrate solo Dashboard IA, solo Dashboard normal o ambos).
- ✅ `ProtectedRoute` admite prop `submodulo` con fallback (ej. `/dashboard` → fallback `/ai-dashboard`).
- ✅ `screenKeyMap` ruta → `{ modulo, submodulo, titulo }` en `pos-ventas/src/utils/screenSubmoduloMap.js` + hook `usePantallaActual` (`src/hooks/usePantallaActual.js`). Cubre ~70 rutas del ERP usando `matchPath`. Códigos alineados al seed (`VENTAS_*`, `COB_*`, `COMP_*`, `INV_*`, `TES_*`, `CONT_*`, `ADM_*`, `REP_*`, `IMP_*`, `SUS_*`).
- ✅ `screen_submodulo` enganchado en Ayuda IA: `AyudaIaDrawer` resuelve módulo/submódulo de la ruta y los reenvía por `preguntar.dto` (`screen_modulo`, `screen_submodulo`). El backend los inyecta en el prompt (`El usuario está en X (screen_key) [submódulo: TES_CHEQUES / módulo: TESORERIA]`) y los registra en auditoría para analytics. El chip de contexto del chat muestra ambos.
- ✅ Breadcrumbs jerárquicos (`src/components/_standards/Breadcrumbs.jsx`) derivados de `buildBreadcrumb(pathname)`. Diccionarios `MODULO_LABELS` / `SUBMODULO_LABELS` en `screenSubmoduloMap.js`. Montado en `Layout.jsx` arriba del contenido, se muestra automáticamente cuando la ruta está mapeada. Permite inyectar pasos extra (`<Breadcrumbs extra={[{label:'Detalle #123'}]} />`).
- ✅ Decorador `@RequirePrivilegio('CODIGO')` (`src/auth/decorators/require-privilegio.decorator.ts`) + `PrivilegioGuard` (`src/auth/guards/privilegio.guard.ts`): resuelve módulo + submódulo desde el privilegio, valida plan / addon / exclusión per-empresa y privilegios del usuario en un solo decorador.
- ✅ Hook frontend `usePrivilegio('CODIGO')` + `useTienePrivilegio` (`src/hooks/usePrivilegio.js`): chequeo por código de privilegio sin necesitar nombre de módulo. Reemplaza progresivamente a `usePermission(modulo)`. Falta migrar los ~80 call sites legacy (Fase 6).

### 🔜 Fase 6 — Migración progresiva del código legacy
- ~80 archivos en `pos-ventas/` usan `usePermission('XXX')` con códigos legacy. Mapear cada uno al nuevo código `<MODULO>_<RECURSO>_<ACCION>` y refactorizar.
- ~42 controllers en `smartfactvoice-backend/` usan guards/decoradores legacy.
- Limpiar usos del modelo viejo `perfiles_privilegios` en favor del flujo nuevo.

### 🔜 Fase 7 — Corte definitivo (destructivo)
- TRUNCATE de `perfiles_privilegios` legacy + reseed completo.
- Eliminar columnas/relaciones legacy ya inutilizadas.
- Drop de tablas legacy si quedaron huérfanas.
- Pre-requisito: Fases 5 y 6 completas para no romper UI/endpoints.

### Tareas sueltas / nice-to-have
- Confirmar `privilegios.submodulo_id NOT NULL` después de Fase 7 (hoy nullable por compatibilidad).
- Tests e2e del flujo crear/clonar/editar/eliminar perfil.
- Iconos por módulo/submódulo en sidebar (`icono` ya existe en schema, falta poblar).
- Probar `useArbolModulosQuery` con `staleTime: 5 * 60 * 1000` en producción — ajustar si invalidaciones son frecuentes.

---

## Notas para continuar

- **Convención `is_sistema`**: los perfiles seeded son inmutables; la única forma de personalizar es clonar y editar la copia.
- **No tocar** `perfiles_privilegios` legacy hasta Fase 7 — sigue siendo usado por código no migrado.
- **Comando de seed**: `npx ts-node -r tsconfig-paths/register --transpile-only src/seguridad/seeds/seed-cli.ts all --empresa=<UUID>`.
- **Idempotencia**: re-correr el seed es seguro (upsert por código).
- **Auditoría**: revisar `entity_type='perfil'` en logs para trazar cambios.

---

## Contexto

El sistema actual de seguridad tiene tres problemas estructurales que impiden escalar:

1. **`privilegios` es una tabla global sin relación con `modulos`**. La relación módulo↔privilegio nace recién en `perfiles_privilegios`. Por eso el UI de edición de perfil muestra el producto cartesiano (5760 = ~120 privilegios × ~48 módulos): todos los privilegios aparecen bajo todos los módulos, sin importar si tienen sentido juntos.
2. **`modulos` no tiene jerarquía**. "Ajustes de inventario" está al mismo nivel que "Inventario", "Contabilidad", "POS", etc. Lo que en realidad son submódulos de un dominio mayor figuran como módulos sueltos.
3. **Privilegios mezclan dos estilos**: genéricos sueltos (`LEER`, `CREAR`, `EDITAR`, `ELIMINAR`, `PROCESAR`, `EXPORTAR`, `IMPRIMIR`, `ANULAR`) sin contexto, y prefijados por dominio (`POS_VENTA_CREDITO`, `CONT_REVERTIR_ASIENTO`, `TES_CONCILIAR`, `IMP_CREAR_EMBARQUE`). La inconsistencia hace imposible saber "qué privilegios aplican a qué pantalla".

El refactor introduce jerarquía de 2 niveles (Módulo raíz → Submódulo), liga cada privilegio a un único submódulo, estandariza los códigos genéricos al patrón `<MODULO>_<RECURSO>_<ACCION>`, y rediseña la UI de edición de perfil en layout 2-columnas.

---

## Decisiones tomadas

| # | Decisión |
|---|----------|
| 1 | Jerarquía de **2 niveles fijos**: Módulo raíz → Submódulo |
| 2 | Privilegio pertenece a **un único submódulo** (FK `privilegios.submodulo_id NOT NULL`) |
| 3 | Suscripción **híbrida**: módulo raíz por defecto + submódulos marcados como "addon" cobrables aparte |
| 4.1 | Mantener nombre `perfiles` (no renombrar a `roles`) |
| 4.2 | Botón **"Cargar perfiles sugeridos"** que seedea perfiles base por empresa (`is_sistema = true`) |
| 4.3 | **Sin overrides** de privilegios por usuario. Si necesita variante, se **clona** el perfil |
| 5 | **Códigos prefijados se mantienen** (`POS_*`, `CONT_*`, etc.); **genéricos sueltos migran** al estándar `<MODULO>_<RECURSO>_<ACCION>` |
| 6 | **Cambio en frío**: truncar `perfiles_privilegios` + `privilegios`, reseed. Sin deprecation window |
| 7 | UI de edición de perfil en **layout 2-columnas** (sidebar de módulos raíz + panel de submódulos/privilegios) |

---

## Fase 0 — Esquema Prisma nuevo

### Cambios a tablas existentes

**`modulos`** (sigue como módulo raíz):
```prisma
model modulos {
  // existentes
  codigo       String   @unique @db.VarChar(20)
  descripcion  String   @db.VarChar(60)
  active       Boolean? @default(true)
  // nuevos
  icono        String?  @db.VarChar(50)   // mdi:package, mdi:cash-register…
  orden        Int      @default(0)
  submodulos   submodulos[]
}
```

**`privilegios`** — agregar FK NOT NULL a submódulo, recurso, acción:
```prisma
model privilegios {
  // existentes mantienen
  codigo          String   @unique @db.VarChar(80)
  desc_privilegio String   @db.VarChar(120)
  active          Boolean? @default(true)
  // nuevos
  submodulo_id    String   @db.Uuid                  // NOT NULL después del seed
  recurso         String   @db.VarChar(40)           // "AJUSTE", "STOCK", "ASIENTO"
  accion          String   @db.VarChar(20)           // "VER", "CREAR", "ANULAR"…
  is_sistema      Boolean  @default(true)            // privilegios del seed
  submodulo       submodulos @relation(fields: [submodulo_id], references: [id])
}
```

### Tablas nuevas

```prisma
model submodulos {
  id            String   @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  modulo_id     String   @db.Uuid
  codigo        String   @unique @db.VarChar(40)     // "INV_AJUSTES", "CONT_ASIENTOS"
  descripcion   String   @db.VarChar(80)
  icono         String?  @db.VarChar(50)
  orden         Int      @default(0)
  es_addon      Boolean  @default(false)             // Decisión 3
  precio_addon  Decimal? @db.Decimal(12,2)
  active        Boolean  @default(true)
  created_at    DateTime @default(now())
  updated_at    DateTime @updatedAt
  modulo        modulos  @relation(fields: [modulo_id], references: [id])
  privilegios   privilegios[]
  suscripcion_submodulos suscripcion_submodulos[]
  @@index([modulo_id])
}

model suscripcion_submodulos {
  id             String   @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  suscripcion_id String   @db.Uuid
  submodulo_id   String   @db.Uuid
  precio         Decimal  @db.Decimal(12,2)
  active         Boolean  @default(true)
  created_at     DateTime @default(now())
  @@unique([suscripcion_id, submodulo_id])
}
```

`suscripcion_modulos` se mantiene como **acceso base al módulo raíz**; `suscripcion_submodulos` solo se llena para addons.

### Pasos de la migración SQL

1. `CREATE TABLE submodulos` + `suscripcion_submodulos`.
2. `ALTER TABLE privilegios ADD COLUMN submodulo_id UUID, recurso VARCHAR(40), accion VARCHAR(20), is_sistema BOOLEAN DEFAULT true`.
3. `ALTER TABLE modulos ADD COLUMN icono VARCHAR(50), orden INT DEFAULT 0`.
4. `TRUNCATE perfiles_privilegios CASCADE; DELETE FROM privilegios;` — reset legacy.
5. Después del seed: `ALTER TABLE privilegios ALTER COLUMN submodulo_id SET NOT NULL`.

---

## Fase 1 — Seed maestro

Path: `smartfactvoice-backend/src/seguridad/seeds/`

### Archivos

```
modulos.seed.ts              — módulos raíz
submodulos.seed.ts           — submódulos por módulo
privilegios.seed.ts          — privilegios por submódulo (estructura curada a mano)
perfiles-sistema.seed.ts     — perfiles sugeridos
seed-orchestrator.service.ts — corre los 4 en orden
```

### Estructura propuesta de módulos raíz

```ts
[
  { codigo: "VENTAS",        descripcion: "Ventas",         icono: "mdi:cash-register" },
  { codigo: "COBRANZAS",     descripcion: "Cobranzas",      icono: "mdi:hand-coin" },
  { codigo: "COMPRAS",       descripcion: "Compras",        icono: "mdi:cart-arrow-down" },
  { codigo: "INVENTARIO",    descripcion: "Inventario",     icono: "mdi:package-variant" },
  { codigo: "CONTABILIDAD",  descripcion: "Contabilidad",   icono: "mdi:book-account" },
  { codigo: "TESORERIA",     descripcion: "Tesorería",      icono: "mdi:bank" },
  { codigo: "RRHH",          descripcion: "RRHH",           icono: "mdi:account-group" },
  { codigo: "IMPORTACIONES", descripcion: "Importaciones",  icono: "mdi:ferry" },
  { codigo: "ADMINISTRACION",descripcion: "Administración", icono: "mdi:cog" },
  { codigo: "REPORTES",      descripcion: "Reportes",       icono: "mdi:chart-box" },
]
```

### Submódulos (extracto representativo)

```ts
INVENTARIO    → AJUSTES, STOCK, CONTEO_FISICO, TRANSFERENCIAS, LOGISTICA(addon)
VENTAS        → POS, FACTURACION, NOTAS_CREDITO, NOTAS_DEBITO, PRESUPUESTOS, PEDIDOS
COBRANZAS     → RECIBOS, CUENTAS_COBRAR, COBRADORES, RUTAS, RENDICIONES
COMPRAS       → ORDENES_COMPRA, RECEPCIONES, PAGOS_PROVEEDOR, REQUISICIONES
CONTABILIDAD  → ASIENTOS, PLAN_CUENTAS, EJERCICIOS, MAPEO, CENTROS_COSTO, REPORTES_CONT, RECIBOS_CONFIG
TESORERIA     → MOVIMIENTOS, CHEQUES, CONCILIACION, CATEGORIAS, CUENTAS
RRHH          → EMPLEADOS, CONCEPTOS, LIQUIDACIONES, IPS, REPORTES_RRHH, VENCIMIENTOS
IMPORTACIONES → EMBARQUES, DESPACHOS, COSTOS_IMP, REPORTES_IMP, CONFIG_IMP
ADMINISTRACION→ EMPRESAS, SUCURSALES, USUARIOS, PERFILES, NUMERACIONES, CONFIG_GENERAL, AUDITORIA
REPORTES      → DASHBOARD_IA, FACTURACION_REP, CONTABILIDAD_REP, INVENTARIO_REP
```

### Privilegios — estándar `<MODULO>_<RECURSO>_<ACCION>`

Ejemplo para submódulo `INV_AJUSTES`:
```ts
[
  { codigo: "INV_AJUSTE_VER",      recurso: "AJUSTE", accion: "VER" },
  { codigo: "INV_AJUSTE_CREAR",    recurso: "AJUSTE", accion: "CREAR" },
  { codigo: "INV_AJUSTE_EDITAR",   recurso: "AJUSTE", accion: "EDITAR" },
  { codigo: "INV_AJUSTE_ANULAR",   recurso: "AJUSTE", accion: "ANULAR" },
  { codigo: "INV_AJUSTE_APROBAR",  recurso: "AJUSTE", accion: "APROBAR" },
]
```

**Acciones estandarizadas**: `VER, CREAR, EDITAR, ELIMINAR, ANULAR, APROBAR, CONFIRMAR, REVERTIR, EXPORTAR, IMPRIMIR, CONFIGURAR, ASIGNAR, PROCESAR`.

Los códigos prefijados existentes (`POS_VENTA_CREDITO`, `CONT_REVERTIR_ASIENTO`, `TES_CONCILIAR`, etc.) **se mantienen** y solo se les asigna el `submodulo_id` correcto en el seed. Solo los **genéricos sueltos** (`LEER`, `CREAR`, `EDITAR`, `ELIMINAR`, `PROCESAR`, `EXPORTAR`, `IMPRIMIR`, `ANULAR`, `ASIGNAR` sin prefijo) se reescriben al nuevo estándar.

### Perfiles del sistema (seed opcional — botón "Cargar sugeridos")

```ts
[
  { perfil: "Administrador",     privilegios: "TODOS" },
  { perfil: "Cajero",            privilegios: ["VENTAS.POS.*", "COBRANZAS.RECIBOS.VER"] },
  { perfil: "Vendedor",          privilegios: ["VENTAS.FACTURACION.*", "VENTAS.PRESUPUESTOS.*"] },
  { perfil: "Cobrador",          privilegios: ["COBRANZAS.*"] },
  { perfil: "Contador",          privilegios: ["CONTABILIDAD.*", "REPORTES.*", "TESORERIA.MOVIMIENTOS.VER"] },
  { perfil: "Encargado Stock",   privilegios: ["INVENTARIO.*"] },
  { perfil: "Encargado Compras", privilegios: ["COMPRAS.*"] },
  { perfil: "Solo Lectura",      privilegios: ["*.VER"] },
]
```

Quedan con `is_sistema = true`. Pueden **clonarse** pero no editarse ni eliminarse directamente.

---

## Fase 2 — Backend (NestJS)

### Estructura del módulo

```
src/seguridad/
  seguridad.module.ts
  modulos/
    modulos.controller.ts          GET /seguridad/modulos
    modulos.service.ts
  submodulos/
    submodulos.controller.ts
    submodulos.service.ts
  privilegios/
    privilegios.controller.ts      GET /seguridad/privilegios?submodulo_id=
    privilegios.service.ts
  perfiles/
    perfiles.controller.ts         CRUD + clonar + cargar-sugeridos
    perfiles.service.ts
  seeds/
    modulos.seed.ts
    submodulos.seed.ts
    privilegios.seed.ts
    perfiles-sistema.seed.ts
    seed-orchestrator.service.ts
  guards/
    permission.guard.ts            (refactor mínimo — la API del decorator no cambia)
```

### Endpoints clave

- `GET  /seguridad/modulos/arbol` — módulos raíz con submódulos y privilegios anidados (cacheable, sin datos del tenant)
- `GET  /seguridad/perfiles` — listar perfiles de la empresa
- `POST /seguridad/perfiles` — crear
- `PATCH /seguridad/perfiles/:id` — editar (recibe array de `privilegio_ids`)
- `POST /seguridad/perfiles/:id/clonar` — clona privilegios + nombre `"Copia de X"`
- `DELETE /seguridad/perfiles/:id` — bloquea si `is_sistema = true`
- `POST /seguridad/perfiles/cargar-sugeridos` — corre seed de perfiles para la empresa actual
- `POST /seguridad/seed-maestro` — solo super-admin, reseedea módulos/submódulos/privilegios

### Guard de permisos

`@RequirePermission('VENTAS', 'POS_VENTA_CREAR')` — la firma se mantiene; cambia solo la query interna (que ahora pasa por `submodulo` para validar coherencia).

### Validación de suscripción (decisión 3)

Helper `hasAccessToSubmodulo(empresaId, submoduloCodigo)`:
1. Si submódulo tiene `es_addon = false` → verificar `suscripcion_modulos` del módulo padre.
2. Si `es_addon = true` → verificar `suscripcion_submodulos` activa.

---

## Fase 3 — Frontend (pos-ventas)

### Service + tanstack

```
src/api/seguridad.service.js
src/tanstack/SeguridadStack.jsx
  useModulosArbolQuery()                  — árbol módulo→submódulo→privilegios
  usePerfilesQuery()
  useCrearPerfilMutation()
  useEditarPerfilMutation()
  useClonarPerfilMutation()
  useEliminarPerfilMutation()
  useCargarPerfilesSugeridosMutation()
```

### Editor de perfil — layout 2-columnas

```
┌──────────────────────────────────────────────────────────────────┐
│ Editar Perfil "Cajero"                                    [X]    │
│ ─────────────────────────────────────────────────────────────── │
│ Nombre: [Cajero          ]  Descripción: [...]                  │
│                                                                  │
│  PERMISOS POR MÓDULO                          🔍 Buscar...      │
│  ┌─────────────────┬──────────────────────────────────────────┐│
│  │ ▢ Ventas     12 │  POS                                     ││
│  │ ▣ Inventario  3 │  ☑ Ver ventas    ☑ Crear venta          ││
│  │ ▢ Compras       │  ☑ Anular ítem   ☐ Anular venta         ││
│  │ ▢ Contabilidad  │  ☑ Descuento     ☐ Reimpresión          ││
│  │ ▢ Tesorería     │                                          ││
│  │ ▢ Cobranzas     │  FACTURACIÓN  [Seleccionar todo]         ││
│  │ ▢ RRHH          │  ☐ Ver  ☐ Crear  ☐ Editar  ☐ Anular     ││
│  │ ▢ Importaciones │                                          ││
│  │ ▢ Administración│  NOTAS DE CRÉDITO                       ││
│  │ ▢ Reportes      │  ☐ Ver  ☐ Crear  ☐ Anular               ││
│  └─────────────────┴──────────────────────────────────────────┘│
│                                                                  │
│            [CANCELAR]   [CLONAR]   [GUARDAR PERFIL]             │
└──────────────────────────────────────────────────────────────────┘
```

- **Izquierda (sidebar)**: módulos raíz con contador "seleccionados / total". Click cambia el módulo activo.
- **Derecha (panel)**: scroll vertical con secciones por submódulo. Cada submódulo tiene "Seleccionar todo" y checkboxes por privilegio.
- **Búsqueda global** filtra el árbol completo (módulos y submódulos), expande lo que coincide.
- Si el submódulo es **addon no contratado** → checkboxes deshabilitados + badge "Addon — Contratar".

### Pantalla de listado de perfiles

`src/pages/SeguridadPerfiles.jsx` con tabla y acciones:
- Botón principal: **+ Nuevo perfil**
- Botón secundario: **Cargar perfiles sugeridos** (solo aparece si la empresa no tiene perfiles del sistema)
- Acciones por fila: editar, clonar, eliminar (eliminar deshabilitado si `is_sistema = true`)

---

## Fase 4 — Administración de planes (Gestión de Planes)

La pantalla actual de **Gestión de Planes** (super-admin) lista los módulos planos asignados a cada plan (ej. el plan "Trial" muestra "23 módulos" sin distinguir entre módulos raíz, submódulos ni addons). Con la nueva jerarquía hay que rediseñar tanto el modelo de relación plan↔módulo como la UI de edición.

### Modelo de relación plan ↔ jerarquía

**`plan_modulos`** (tabla existente) — mantiene su significado: **"el plan incluye este módulo raíz"**. Al incluir un módulo raíz, el plan automáticamente da acceso a todos sus submódulos **no-addon**. Esto reemplaza la lógica actual que asignaba submódulos individualmente.

**`plan_submodulos_addon`** (tabla nueva) — solo para submódulos marcados `es_addon = true` que el plan incluye explícitamente:

```prisma
model plan_submodulos_addon {
  plan_id      String   @db.Uuid
  submodulo_id String   @db.Uuid
  precio       Decimal? @db.Decimal(12,2)   // override del precio default del addon
  active       Boolean  @default(true)
  created_at   DateTime @default(now())
  updated_at   DateTime @updatedAt
  planes       planes     @relation(fields: [plan_id], references: [id])
  submodulos   submodulos @relation(fields: [submodulo_id], references: [id])
  @@id([plan_id, submodulo_id])
}
```

Y en `planes` se agrega relación inversa: `plan_submodulos_addon plan_submodulos_addon[]`.

### Lógica de "qué tiene acceso una suscripción"

Cuando un cliente contrata el plan X:
1. Se copian las filas de `plan_modulos(X)` → `suscripcion_modulos`.
2. Se copian las filas de `plan_submodulos_addon(X)` → `suscripcion_submodulos`.
3. El helper `hasAccessToSubmodulo(empresaId, submoduloCodigo)`:
   - Si `submodulo.es_addon = false` → busca el módulo padre en `suscripcion_modulos`.
   - Si `submodulo.es_addon = true` → busca en `suscripcion_submodulos`.

### UI nueva — Editor de plan

Reemplaza el listado plano actual ("23 módulos" como chips sueltos) por una vista **jerárquica con toggles**:

```
┌────────────────────────────────────────────────────────────────────┐
│ Editar Plan "Profesional"                                    [X]   │
│ ───────────────────────────────────────────────────────────────── │
│ Código: [PRO_001 ]   Descripción: [Plan Profesional      ]        │
│ Precio base: [ Gs. 250.000 / mensual ]                            │
│                                                                    │
│ Límites: usuarios [50]  productos [5000]  docs/ciclo [1000]       │
│          clientes [500]  empresas [3]  sucursales [10]            │
│                                                                    │
│ ─────────────────── MÓDULOS Y ADDONS ─────────────────────────── │
│                                                                    │
│  📦 Ventas                                              [●─ ON]   │
│     ├─ POS, Facturación, NC, ND, Presupuestos, Pedidos (incluidos)│
│     └─ Addons:                                                    │
│        ☑ POS Avanzado          + Gs. 50.000   [override: _____]  │
│        ☐ Vendedores móviles    + Gs. 30.000                       │
│                                                                    │
│  📦 Inventario                                          [●─ ON]   │
│     ├─ Ajustes, Stock, Conteo, Transferencias (incluidos)         │
│     └─ Addons:                                                    │
│        ☐ Logística             + Gs. 80.000                       │
│        ☐ Lotes y vencimientos  + Gs. 40.000                       │
│                                                                    │
│  📦 Contabilidad                                        [○─ OFF]  │
│     (deshabilitado — submódulos no se muestran)                   │
│                                                                    │
│  📦 RRHH                                                [●─ ON]   │
│     ├─ Empleados, Conceptos, Liquidaciones, IPS (incluidos)       │
│     └─ Addons:                                                    │
│        ☑ Liquidaciones IA      + Gs. 70.000                       │
│                                                                    │
│  📦 Importaciones                                       [○─ OFF]  │
│                                                                    │
│ ───────────────────────────────────────────────────────────────── │
│ Precio total estimado: Gs. 370.000 / mensual                      │
│ (base 250.000 + 3 addons 120.000)                                 │
│                                                                    │
│              [CANCELAR]                  [GUARDAR PLAN]           │
└────────────────────────────────────────────────────────────────────┘
```

**Comportamiento**:
- Cada módulo raíz tiene un **toggle ON/OFF** que lo agrega/remueve de `plan_modulos`.
- Cuando el toggle está ON, debajo se listan:
  - Submódulos **incluidos** (no-addon): solo descriptivos, no se pueden desmarcar (van automáticos).
  - Submódulos **addon**: checkboxes individuales que se persisten en `plan_submodulos_addon`, con campo opcional para override del precio default.
- Cuando un módulo raíz está OFF, sus addons se ocultan automáticamente (no tiene sentido contratar el addon "Logística" sin el módulo "Inventario").
- **Precio total estimado** se recalcula en vivo: precio base del plan + suma de precios de addons activos.
- Búsqueda arriba para filtrar por nombre de módulo o submódulo.

### Endpoints

- `GET  /planes` — lista de planes (super-admin) con count de módulos y addons.
- `GET  /planes/:id` — detalle con módulos raíz incluidos + addons + límites.
- `POST /planes` — crear.
- `PATCH /planes/:id` — editar plan + sincroniza `plan_modulos` y `plan_submodulos_addon` desde el body:
  ```ts
  body: {
    codigo, descripcion, precio, ...limites,
    modulos_raiz_ids: string[],           // IDs de módulos raíz incluidos
    addons: Array<{ submodulo_id, precio_override?: number }>,
  }
  ```
- `POST /planes/:id/clonar` — útil para crear variantes de plan.
- `DELETE /planes/:id` — bloquea si tiene suscripciones activas.

### Migración de planes existentes

Los planes actuales tienen filas en `plan_modulos` que apuntan a módulos planos (mezcla de raíces y submódulos viejos). Pasos:

1. Para cada `plan_modulos.modulo_id`, buscar en el **mapeo de migración** (definido en el seed):
   - Si el módulo viejo es ahora un **módulo raíz** → mantener la fila apuntando al nuevo `modulo_id`.
   - Si el módulo viejo es ahora un **submódulo no-addon** → reemplazar por su módulo raíz (deduplicar).
   - Si el módulo viejo es ahora un **submódulo addon** → mover a `plan_submodulos_addon` con su precio default.
2. El script de migración del seed-orchestrator genera estas filas automáticamente leyendo el mapeo.
3. Validar: cada plan debe quedar con al menos 1 módulo raíz; si no, marcar para revisión manual del super-admin.

### Archivos a tocar

**Backend nuevos**:
```
src/planes/planes-v2.controller.ts        (nuevo endpoint con jerarquía)
src/planes/planes-v2.service.ts
src/planes/dto/upsert-plan-v2.dto.ts
src/seguridad/seeds/mapeo-planes-legacy.seed.ts
```

**Backend modificar**:
```
prisma/schema.prisma                      (model plan_submodulos_addon)
src/suscripciones/suscripciones.service.ts (lógica de copiar plan → suscripción incluye addons)
src/planes/planes.service.ts              (deprecar lógica vieja; nueva entrada va por v2)
```

**Frontend nuevos**:
```
src/components/admin/PlanEditorDialog.jsx           (reemplaza el editor actual)
src/components/admin/PlanEditorModuloCard.jsx       (card por módulo raíz con toggle + addons)
src/components/admin/PlanPrecioCalculator.jsx       (cálculo en vivo del total)
```

**Frontend modificar**:
```
src/pages/admin/PlanesPage.jsx                      (las cards de la lista cambian: muestran "X módulos · Y addons" en vez de chips sueltos)
src/api/planes.service.js
src/tanstack/PlanesStack.jsx
```

### UX adicional para la pantalla de listado (la del screenshot)

Cada card de plan en la grilla muestra hoy "23 módulos" con chips planos. Cambia a:

```
┌──────────────────────────────────┐
│ Activo            [edit] [pause] │
│ PLAN_PROFESIONAL                 │
│ Plan Profesional                 │
│                                  │
│ Gs. 250.000 / mensual            │
│                                  │
│ 👥 50 usuarios    📦 5000 prods │
│ 📄 1000 docs/ciclo 👤 500 clts  │
│                                  │
│ ─────────────────────            │
│ 🧩 6 módulos · 3 addons          │
│  • Ventas, Inventario, RRHH…    │
│  + POS Avanzado (addon)          │
│  + Liquidaciones IA (addon)      │
│                                  │
│ 🔗 12 suscripciones              │
└──────────────────────────────────┘
```

Muestra módulos raíz (no submódulos) y addons separados, dando información significativa al super-admin sin saturar la card.

---

## Fase 5 — Impactos transversales en la UI

Más allá de los dos editores (perfiles y planes), la nueva jerarquía Módulo raíz → Submódulo afecta varias pantallas y componentes del sistema. Esta fase los enumera para que se actualicen como parte del refactor y no queden inconsistentes.

### 5.1 Sidebar / Menú principal

**Hoy**: el menú lateral lista módulos planos (POS, Ajustes de inventario, Contabilidad, Tesorería, RRHH, etc.) al mismo nivel. Cuando hay muchos módulos contratados, se vuelve una lista vertical larga sin organización.

**Cambio**:
- Items de primer nivel = **módulos raíz** (con su `icono` del seed).
- Al expandir un módulo raíz → se muestran sus submódulos contratados (no-addon + addons activos).
- Submódulos no contratados / sin permiso → no aparecen.
- Módulos raíz sin ningún submódulo accesible → no aparecen.
- Si el usuario solo tiene un submódulo de un módulo raíz, se puede colapsar a un solo nivel (UX detail).

**Archivos**: `src/components/organismos/Sidebar/*` (o donde esté el menú principal). Probablemente `src/components/Sidebar.jsx` o similar — confirmar al implementar.

### 5.2 Breadcrumbs / Header de pantalla

**Hoy**: cada pantalla suele mostrar solo el título del módulo (ej. "Ajustes de inventario").

**Cambio**: breadcrumb refleja la jerarquía → `Inventario › Ajustes`. El componente `ScreenGuia` (de `src/components/_standards/`) ya recibe título; agregar prop opcional `modulo_padre` para renderizar el breadcrumb consistente.

### 5.3 Pantalla "Mi suscripción" / Configuración de empresa

**Hoy**: probablemente lista los módulos contratados como chips planos.

**Cambio**: misma vista jerárquica que el editor de plan, pero **read-only para el cliente**, con CTA "Contratar addon" en los submódulos addon disponibles pero no contratados. Esto convierte la pantalla en un canal de upsell natural.

**Archivos sugeridos**: `src/pages/MiSuscripcion.jsx` o equivalente en el área de Configuración.

### 5.4 Onboarding / Selección de plan al registrarse

**Hoy**: al registrar una empresa nueva, la selección de plan muestra los módulos planos.

**Cambio**: mostrar la jerarquía. En el wizard de registro:
1. Paso "Elegí tu plan" → cards de planes con resumen de módulos raíz incluidos.
2. Paso "Personalizá" → checkboxes de addons disponibles del plan elegido (opcional).

### 5.5 Catálogo de pantallas de AyudaIA (`screenKeyMap.js`)

**Hoy**: `src/components/ayuda-ia/screenKeyMap.js` mapea pathnames → screen_key (ej. `/inventario/ajustes` → `inventario/ajustes`). Los `screen_key` están alineados con los **módulos planos** actuales.

**Cambio**:
- Estandarizar `screen_key` a `<modulo_raiz>/<submodulo>` (ej. `inventario/ajustes`, `contabilidad/asientos`, `ventas/pos`).
- El `screen_key` debe coincidir con el `codigo` del submódulo en lowercase + slash, para que el chatbot pueda referenciarlos directamente.
- El seed de privilegios y submódulos puede generar este mapeo automáticamente para mantener consistencia.

**Archivos**:
```
src/components/ayuda-ia/screenKeyMap.js                        (actualizar)
src/ayuda-ia/ingesta/seed-catalogo.ts (backend, plan ayuda-ia) (alinear codigos)
```

### 5.6 Decorators `@RequireModule` en backend

**Hoy**: varios controllers usan `@RequireModule('NOMBRE_MODULO')` (ej. en `ayuda-ia.controller.ts` el decorator espera el módulo `AYUDA_IA`). Hoy esos códigos son los de la tabla `modulos` plana.

**Cambio**:
- `@RequireModule('VENTAS')` → valida acceso al módulo raíz vía `suscripcion_modulos`.
- Nuevo `@RequireSubmodulo('VENTAS_POS')` → valida acceso al submódulo específico (cubre el caso de addons).
- Compatibilidad: el guard reconoce tanto códigos viejos (durante la migración) como nuevos.

**Archivos**: `src/auth/guards/module.guard.ts`, `src/common/decorators/require-module.decorator.ts`.

### 5.7 Selectores de módulo / filtros en pantallas existentes

Hay varias pantallas que tienen un `Select` o `Autocomplete` para filtrar por módulo (ej. auditoría: "ver logs del módulo X"; reportes; algunas configuraciones).

**Cambio**: en vez de un solo dropdown plano, usar un **Autocomplete agrupado por módulo raíz** (`groupBy={(o) => o.modulo_raiz}` de MUI Autocomplete). Mucho más legible cuando hay 60 submódulos.

**Pantallas a revisar**:
- Auditoría (`src/pages/Auditoria*.jsx`)
- Filtros del Dashboard IA (`src/components/organismos/AIDashboard/*`)
- Cualquier pantalla de reportes que filtre por módulo

### 5.8 Dashboard IA

El Dashboard IA tiene `modules-context.service.ts` que provee contexto de módulos al LLM. Hay que actualizarlo para que entienda la nueva jerarquía y dé respuestas más precisas ("¿en qué módulo está X?" → "Inventario › Ajustes" en vez de "Ajustes de inventario").

**Archivos**: `src/ai-dashboard/shared/modules-context.service.ts`.

### 5.9 Búsqueda global (si existe) / Command palette

Si hay alguna búsqueda tipo command palette / spotlight para saltar a pantallas, debe indexar por módulo raíz y submódulo para mejorar resultados (ej. buscar "ajuste" debería mostrar "Inventario › Ajustes" como primer resultado).

### 5.10 Estándares visuales reusables

Para mantener consistencia en todas estas pantallas, agregar a `src/components/_standards/`:

- **`ModuloChip.jsx`** — chip con ícono + nombre del módulo, opcionalmente con submódulo (`Inventario › Ajustes`). Usado en sidebar, listado de planes, auditoría, mi suscripción.
- **`ModuloJerarquiaSelector.jsx`** — Autocomplete agrupado por módulo raíz, reusable para filtros y forms.
- **`AddonBadge.jsx`** — badge "Addon" para submódulos no incluidos en plan base.

Estos componentes se reusan en las pantallas listadas arriba; sin ellos terminamos con 3-4 implementaciones distintas del mismo patrón.

### Checklist de pantallas a revisar

| Pantalla / Componente | Acción |
|---|---|
| Sidebar / menú principal | Reorganizar a 2 niveles con expandir/colapsar |
| Breadcrumbs / `ScreenGuia` | Agregar prop `modulo_padre` |
| Mi Suscripción | Vista jerárquica read-only + CTA upsell de addons |
| Onboarding / wizard de registro | Mostrar jerarquía al elegir plan |
| `screenKeyMap.js` (AyudaIA) | Estandarizar a `<modulo_raiz>/<submodulo>` |
| `@RequireModule` / `@RequireSubmodulo` | Refactor del guard |
| Filtros en Auditoría | Autocomplete agrupado |
| Filtros del Dashboard IA | Autocomplete agrupado |
| `modules-context.service.ts` | Actualizar prompt con jerarquía |
| Búsqueda global (si existe) | Indexar por jerarquía |
| `_standards/ModuloChip`, `ModuloJerarquiaSelector`, `AddonBadge` | Crear componentes reusables |

---

## Fase 6 — Adaptación de pantallas y controllers existentes (el trabajo pesado)

Hoy hay **80 archivos en el frontend** usando `usePermission(modulo)` + `can(privilegio)` y **42 controllers en el backend** usando `@RequirePermission(modulo, privilegio)`, todos con códigos hardcoded. Cada pantalla y cada endpoint del ERP referencia privilegios viejos que cambian con el refactor. Sin un plan sistemático, esto se convierte en el cuello de botella del proyecto.

### Estrategia general

El principio: **no hacer big-bang**. Hacer el refactor módulo por módulo, validando que cada pantalla siga funcionando antes de pasar a la siguiente. Para que eso sea posible sin freezar el sistema completo, introducimos un **mapa de alias temporal en el guard**.

### 6.0 Inventario de pantallas — mapeo sidebar/pestañas → módulo/submódulo

Antes del inventario de **códigos** (6.1), hay que hacer el inventario de **pantallas**. Cada item del sidebar tiene sus pestañas internas, y dos pantallas (Configuración y Reportes) son **transversales** porque agregan screens de varios módulos. Sin este mapeo, el refactor queda inconsistente: una pantalla puede mover su código de privilegio pero seguir viviendo bajo el módulo viejo del sidebar, o aparecer en "Configuración" de un módulo al que ya no pertenece.

#### Items actuales del sidebar (menú principal)

| Sidebar item actual    | Módulo raíz nuevo  | Submódulo principal      | Notas |
|---|---|---|---|
| Dashboard              | (panel transversal)| —                        | Sin permisos especiales; muestra widgets según permisos del usuario |
| IA Dashboard           | REPORTES           | DASHBOARD_IA             | Addon en planes premium |
| POS                    | VENTAS             | POS                      | |
| Facturación            | VENTAS             | FACTURACION              | |
| Presupuestos           | VENTAS             | PRESUPUESTOS             | |
| Mayoristas             | VENTAS             | MAYORISTAS               | Addon (mercado mayorista) |
| Créditos               | COBRANZAS          | CUENTAS_COBRAR           | Aunque hoy es item separado, es vista de cuentas a cobrar |
| Cobros                 | COBRANZAS          | RECIBOS                  | |
| Panel Cobrador         | COBRANZAS          | COBRADORES               | Panel operativo del cobrador |
| Productos              | INVENTARIO         | PRODUCTOS                | Catálogo. Distinto a STOCK que es la consulta de existencias |
| Contactos              | ADMINISTRACION     | CLIENTES / PROVEEDORES   | Tiene pestañas internas (clientes, proveedores, transportistas, choferes, vehículos) |
| Compras                | COMPRAS            | ORDENES_COMPRA           | Tiene pestañas: órdenes, requisiciones, gastos, etc. |
| Importaciones          | IMPORTACIONES      | EMBARQUES                | |
| Suscripciones          | ADMINISTRACION     | SUSCRIPCIONES            | Solo super-admin / reseller |
| Finanzas               | TESORERIA          | (vista consolidada)      | Item legacy — revisar si se mantiene como dashboard de tesorería |
| Tesorería              | TESORERIA          | MOVIMIENTOS              | |
| Contabilidad           | CONTABILIDAD       | ASIENTOS                 | Tiene pestañas: asientos, plan, ejercicios, mapeo, CC, reportes |
| Recursos Humanos       | RRHH               | EMPLEADOS                | Tiene muchísimas pestañas internas |
| Reportes               | REPORTES           | (vista transversal)      | Ver "Pantalla Reportes" abajo |
| Configuración          | (vista transversal)| —                        | Ver "Pantalla Configuración" abajo |
| Mi perfil              | ADMINISTRACION     | USUARIOS                 | Self-service del usuario |

**Acciones**:
- Algunos items que hoy están al primer nivel (Mayoristas, Créditos, Panel Cobrador) son en realidad submódulos. En la **Fase 5.1 (sidebar)** quedan colapsados bajo su módulo raíz (`Ventas › Mayoristas`, `Cobranzas › Créditos`, `Cobranzas › Panel Cobrador`).
- "Finanzas" hoy es legacy/duplicado de Tesorería — confirmar si se mantiene como dashboard consolidado o se elimina.

#### Pestañas internas — inventario por pantalla

Cada pantalla principal con tabs/pestañas necesita su mapeo. Lo importante es que **cada pestaña** debe mapearse a un submódulo con su propio set de privilegios.

**Contabilidad** (`src/components/contabilidad/`):
| Pestaña            | Submódulo            |
|---|---|
| Asientos           | CONT_ASIENTOS        |
| Reportes           | CONT_REPORTES        |
| Plan de Cuentas    | CONT_PLAN_CUENTAS    |
| Ejercicios         | CONT_EJERCICIOS      |
| Mapeo de Cuentas   | CONT_MAPEO           |
| Centros de Costo   | CONT_CENTROS_COSTO   |
| Tipo de Cambio     | CONT_TIPO_CAMBIO     |
| Config. Recibos    | CONT_RECIBOS_CONFIG  |

**Inventario** (`src/components/inventario/`):
| Pestaña            | Submódulo            |
|---|---|
| Productos          | INV_PRODUCTOS        |
| Stock              | INV_STOCK            |
| Ajuste de Stock    | INV_AJUSTES          |
| Movimientos        | INV_MOVIMIENTOS      |
| Transferencias     | INV_TRANSFERENCIAS   |
| Categorías         | INV_CATEGORIAS       |
| Marcas             | INV_MARCAS           |
| Atributos          | INV_ATRIBUTOS        |
| Ofertas            | INV_OFERTAS          |
| Inventario Físico  | INV_CONTEO_FISICO    |

**Ventas / POS Admin** (`src/components/ventas/`):
| Pestaña                  | Submódulo              |
|---|---|
| Facturas                 | VEN_FACTURACION        |
| Notas de Crédito         | VEN_NOTAS_CREDITO      |
| Notas de Débito          | VEN_NOTAS_DEBITO       |
| Pedidos                  | VEN_PEDIDOS            |
| Órdenes de Venta         | VEN_ORDENES_VENTA      |
| Presupuestos             | VEN_PRESUPUESTOS       |
| Solicitudes de Crédito   | COB_SOLICITUDES_CREDITO|

**Cobros / Recibos** (`src/components/organismos/RecibosMulti/`, etc.):
| Pestaña                  | Submódulo              |
|---|---|
| Recibos                  | COB_RECIBOS            |
| Recibos Multi            | COB_RECIBOS            |
| Libro de Retenciones     | COB_RETENCIONES        |
| Rendiciones              | COB_RENDICIONES        |
| Asignación de Cobranza   | COB_ASIGNACION         |
| Rutas de Cobranza        | COB_RUTAS              |

**Compras** (`src/components/organismos/ComprasDesign/` + `src/pages/`):
| Pestaña                  | Submódulo              |
|---|---|
| Órdenes de Compra        | COM_ORDENES_COMPRA     |
| Requisiciones            | COM_REQUISICIONES      |
| Recepciones              | COM_RECEPCIONES        |
| Pagos a Proveedor        | COM_PAGOS_PROVEEDOR    |
| Cuentas por Pagar        | COM_CUENTAS_PAGAR      |
| Gastos                   | COM_GASTOS             |

**Tesorería** (`src/components/tesoreria/`):
| Pestaña                  | Submódulo              |
|---|---|
| Movimientos              | TES_MOVIMIENTOS        |
| Cheques                  | TES_CHEQUES            |
| Conciliación             | TES_CONCILIACION       |
| Reportes                 | TES_REPORTES           |
| Categorías               | TES_CATEGORIAS         |
| Cuentas de Tesorería     | TES_CUENTAS            |

**Importaciones** (`src/components/importaciones/`):
| Pestaña                  | Submódulo              |
|---|---|
| Embarques                | IMP_EMBARQUES          |
| Despachos                | IMP_DESPACHOS          |
| Costos de Importación    | IMP_COSTOS             |
| ISC Vehículos            | IMP_ISC_VEHICULOS      |
| Reportes                 | IMP_REPORTES           |

**Recursos Humanos** (más extenso — `src/components/rrhh/*` y backend `src/rrhh/controllers/*`):
| Pestaña                  | Submódulo              |
|---|---|
| Empleados / Legajos      | RRHH_EMPLEADOS         |
| Conceptos                | RRHH_CONCEPTOS         |
| Parámetros               | RRHH_PARAMETROS        |
| Liquidaciones            | RRHH_LIQUIDACIONES     |
| Anticipos                | RRHH_ANTICIPOS         |
| Préstamos                | RRHH_PRESTAMOS         |
| Vacaciones               | RRHH_VACACIONES        |
| Desvinculaciones         | RRHH_DESVINCULACIONES  |
| Novedades                | RRHH_NOVEDADES         |
| Marcaciones / Presentismo| RRHH_PRESENTISMO       |
| Turnos                   | RRHH_TURNOS            |
| Relojes marcadores       | RRHH_RELOJES           |
| IPS / Acreditaciones     | RRHH_IPS               |
| Planillas                | RRHH_PLANILLAS         |
| Formatos bancarios       | RRHH_FORMATOS_BANCARIOS|
| Reportes IPS             | RRHH_REPORTES_IPS      |
| Dashboard Legajos        | RRHH_DASHBOARD         |

**Contactos** (`src/components/contactos/`):
| Pestaña                  | Submódulo              |
|---|---|
| Clientes                 | ADM_CLIENTES           |
| Proveedores              | ADM_PROVEEDORES        |
| Transportistas           | ADM_TRANSPORTISTAS     |
| Choferes                 | ADM_CHOFERES           |
| Vehículos                | ADM_VEHICULOS          |
| Agentes Transporte       | ADM_AGENTES_TRANSPORTE |

#### Pantalla "Configuración" — vista transversal

`src/pages/ConfiguracionNew.jsx` agrega screens de configuración de **todos los módulos**. Con el refactor:

- La pantalla sigue existiendo como **navegador unificado** ("centro de configuración del sistema").
- Cada item del menú izquierdo se mapea a un submódulo de configuración del módulo raíz correspondiente:

| Item actual                  | Módulo raíz       | Submódulo                   | Privilegio típico |
|---|---|---|---|
| **Empresa**                  | | | |
| Datos de la Empresa          | ADMINISTRACION    | ADM_EMPRESA                 | ADM_EMPRESA_CONFIGURAR |
| Empresas Asociadas           | ADMINISTRACION    | ADM_EMPRESAS                | ADM_EMPRESAS_GESTIONAR |
| Suscripción                  | ADMINISTRACION    | ADM_SUSCRIPCION             | ADM_SUSCRIPCION_VER |
| **Puntos de Venta**          | | | |
| Sucursales y Cajas           | ADMINISTRACION    | ADM_SUCURSALES              | ADM_SUCURSALES_GESTIONAR |
| Configuración POS            | VENTAS            | VEN_POS                     | VEN_POS_CONFIGURAR |
| **Usuarios y Permisos**      | | | |
| Usuarios                     | ADMINISTRACION    | ADM_USUARIOS                | ADM_USUARIOS_GESTIONAR |
| Perfiles y Roles             | ADMINISTRACION    | ADM_PERFILES                | ADM_PERFILES_GESTIONAR |
| **Facturación**              | | | |
| Monedas                      | ADMINISTRACION    | ADM_MONEDAS                 | ADM_MONEDAS_GESTIONAR |
| Métodos de Pago              | ADMINISTRACION    | ADM_METODOS_PAGO            | ADM_METODOS_PAGO_GESTIONAR |
| Listas de Precios            | VENTAS            | VEN_LISTAS_PRECIOS          | VEN_LISTAS_PRECIOS_GESTIONAR |
| Configuración de Compras     | COMPRAS           | COM_CONFIG                  | COM_CONFIG_CONFIGURAR |
| **Cobranzas**                | | | |
| Configuración de Mora        | COBRANZAS         | COB_MORA                    | COB_MORA_CONFIGURAR |
| **Órdenes de Venta**         | | | |
| Configuración Global         | VENTAS            | VEN_ORDENES_VENTA           | VEN_ORDENES_VENTA_CONFIGURAR |
| **Presupuestos**             | | | |
| Configuración Global         | VENTAS            | VEN_PRESUPUESTOS            | VEN_PRESUPUESTOS_CONFIGURAR |
| **Importaciones**            | | | |
| Configuración del módulo     | IMPORTACIONES     | IMP_CONFIG                  | IMP_CONFIG_CONFIGURAR |
| **Tesorería y Bancos**       | | | |
| Cuentas de Tesorería         | TESORERIA         | TES_CUENTAS                 | TES_CUENTAS_GESTIONAR |
| Categorías de Movimiento     | TESORERIA         | TES_CATEGORIAS              | TES_CATEGORIAS_GESTIONAR |
| Bancos                       | TESORERIA         | TES_BANCOS                  | TES_BANCOS_GESTIONAR |
| Parámetros de Tesorería      | TESORERIA         | TES_PARAMETROS              | TES_PARAMETROS_CONFIGURAR |
| Reglas Automáticas           | TESORERIA         | TES_REGLAS_AUTO             | TES_REGLAS_AUTO_GESTIONAR |
| Bancard VPOS                 | TESORERIA         | TES_BANCARD_VPOS            | TES_BANCARD_VPOS_CONFIGURAR |
| **Asistente IA**             | | | |
| NovaIA (Holding)             | REPORTES          | RPT_DASHBOARD_IA            | RPT_DASHBOARD_IA_CONFIGURAR |

**Cambio de UX**: el menú izquierdo de Configuración se reordena para **agrupar por módulo raíz** (en vez de las secciones temáticas actuales). Ej:

```
CONFIGURACIÓN
├── 🛒 Ventas
│   ├── Configuración POS
│   ├── Listas de Precios
│   ├── Órdenes de Venta — Config Global
│   └── Presupuestos — Config Global
├── 💰 Cobranzas
│   └── Configuración de Mora
├── 🛍 Compras
│   └── Configuración de Compras
├── 🏛 Contabilidad
│   ├── Plan de Cuentas
│   ├── Ejercicios
│   ├── Mapeo de Cuentas
│   └── …
├── 💵 Tesorería
│   ├── Cuentas de Tesorería
│   ├── Bancos
│   ├── Bancard VPOS
│   └── …
└── ⚙️ Administración
    ├── Empresa
    ├── Sucursales
    ├── Usuarios
    ├── Perfiles y Roles
    ├── Monedas
    ├── Métodos de Pago
    └── Suscripción
```

Esto hace que la pantalla de configuración sea **consistente con el sidebar y los permisos**: el usuario que no tenga acceso al módulo Tesorería no ve la sección de Tesorería en Configuración tampoco.

#### Pantalla "Reportes" — vista transversal

`src/pages/Reportes.jsx` agrupa todos los reportes del sistema. Mapeo:

| Card / Reporte actual            | Módulo raíz       | Submódulo                   |
|---|---|---|
| **Ventas**                       | | |
| Resumen de Ventas                | REPORTES          | RPT_VENTAS                  |
| Productos y Servicios            | REPORTES          | RPT_PRODUCTOS               |
| Rentabilidad                     | REPORTES          | RPT_RENTABILIDAD            |
| **Administrativos y Financieros**| | |
| Cuentas por Cobrar               | COBRANZAS         | COB_CUENTAS_COBRAR (vista reporte) |
| Cuentas por Pagar                | COMPRAS           | COM_CUENTAS_PAGAR (vista reporte) |
| Resumen de Compras               | REPORTES          | RPT_COMPRAS                 |
| Posición de Caja                 | REPORTES          | RPT_POSICION_CAJA           |
| Flujo de Caja                    | REPORTES          | RPT_FLUJO_CAJA              |
| Movimientos de Caja              | REPORTES          | RPT_MOVIMIENTOS_CAJA        |
| **Inventario**                   | | |
| Movimientos de Stock             | REPORTES          | RPT_INV_MOVIMIENTOS         |
| Niveles de Stock                 | REPORTES          | RPT_INV_NIVELES             |
| Lotes de Productos               | REPORTES          | RPT_INV_LOTES               |
| **Fiscal e Impuestos**           | | |
| Libro IVA Ventas                 | REPORTES          | RPT_LIBRO_IVA_VENTAS        |
| Libro IVA Compras                | REPORTES          | RPT_LIBRO_IVA_COMPRAS       |
| Liquidación de IVA               | REPORTES          | RPT_LIQUIDACION_IVA         |
| **Contabilidad Financiera**      | | |
| Libro Diario                     | CONTABILIDAD      | CONT_REPORTES (Libro Diario)|
| Libro Mayor                      | CONTABILIDAD      | CONT_REPORTES               |
| Balance de Comprobación          | CONTABILIDAD     | CONT_REPORTES               |
| Estado de Resultados             | CONTABILIDAD     | CONT_REPORTES               |
| Balance General                  | CONTABILIDAD     | CONT_REPORTES               |
| **Cobranzas**                    | | |
| Reporte por Cobrador             | COBRANZAS         | COB_REPORTES                |
| Dashboard de Morosidad           | COBRANZAS         | COB_REPORTES                |
| Reporte de Rendiciones           | COBRANZAS         | COB_REPORTES                |
| **Auditoría y Seguridad**        | | |
| Logs de Auditoría                | ADMINISTRACION    | ADM_AUDITORIA               |

**Decisión de diseño**: dos opciones para los reportes que naturalmente pertenecen a un módulo (ej. "Libro Diario" es de Contabilidad):

- **(a)** El submódulo "Reportes Contables" vive bajo Contabilidad y NO se duplica en Reportes (la pantalla Reportes solo muestra lo verdaderamente transversal: ventas, compras, fiscal, posición de caja).
- **(b)** Los reportes son **accesos directos** desde la pantalla Reportes pero los privilegios viven en su módulo natural (ej. "Libro Diario" requiere `CONT_REPORTES_VER`, no un privilegio nuevo). La pantalla Reportes funciona como un *index*, no como un dueño de privilegios.

Recomiendo **(b)**: la pantalla "Reportes" es un launcher / index, los privilegios viven en cada módulo. Si el usuario no tiene `CONT_REPORTES_VER`, la card de Libro Diario aparece deshabilitada (o no aparece).

#### Acciones concretas para 6.0

1. Validar con el equipo el mapeo de sidebar (corregir cualquier ambigüedad: ¿Finanzas se mantiene? ¿Mayoristas es addon? ¿Créditos vs. Cuentas por Cobrar son lo mismo?).
2. Validar el mapeo de cada pestaña interna (probablemente aparezcan pestañas que no listé en este doc; agregarlas).
3. Producir un **archivo único de mapeo** en `src/seguridad/seeds/mapeo-screens.ts` que sea consumido por:
   - El seed de submódulos (define qué submódulos existen).
   - El seed de privilegios (qué privilegios necesita cada pantalla).
   - El sidebar (qué item del menú lleva a qué pantalla con qué privilegio).
   - El `screenKeyMap.js` de AyudaIA (qué `screen_key` corresponde a cada pathname).
4. Solo después de esto, ejecutar 6.1 (auditoría de códigos).

#### Inventario backend — mapeo controllers NestJS → módulo/submódulo

El frontend no es la única superficie a refactorizar: el backend tiene ~120 controllers que usan `@RequirePermission(modulo, privilegio)` y `@RequireModule(modulo)`. Cada controller debe quedar mapeado al **mismo** par módulo raíz/submódulo que su pantalla en el frontend (consistencia 1:1). Si el mapeo difiere entre back y front, el guard fallará al chequear `submodulo_id`.

Mapeo por módulo raíz (los privilegios listados son ejemplos representativos, no exhaustivos):

| Controller (`src/...`) | Módulo raíz | Submódulo | Privilegios principales a renombrar |
|---|---|---|---|
| `facturas/facturas.controller.ts` | VENTAS | FACTURACION | `VENTAS_FACTURA_VER/CREAR/ANULAR/REIMPRIMIR` |
| `nota-creditos/nota-creditos.controller.ts` | VENTAS | NOTA_CREDITO | `VENTAS_NC_VER/CREAR/APLICAR/ANULAR` |
| `nota-remision/nota-remision.controller.ts` | VENTAS | REMISION | `VENTAS_REMISION_VER/CREAR/ANULAR` |
| `pedidos/pedidos.controller.ts` | VENTAS | PEDIDOS | `VENTAS_PEDIDO_VER/CREAR/APROBAR` |
| `pedidos/ordenes-venta.controller.ts` | VENTAS | ORDEN_VENTA | `VENTAS_OV_VER/CREAR/CONFIRMAR` |
| `ofertas/ofertas.controller.ts` | VENTAS | OFERTAS | `VENTAS_OFERTA_VER/GESTIONAR` |
| `lista-precios/lista-precios.controller.ts` | VENTAS | LISTA_PRECIOS | `VENTAS_LP_VER/GESTIONAR` |
| `pos-config/pos-config.controller.ts` | VENTAS | POS | `VENTAS_POS_CONFIGURAR` |
| `cobros/cobros.controller.ts` | COBROS | RECIBOS | `COBROS_RECIBO_VER/CREAR/ANULAR` |
| `recibos/recibos.controller.ts` | COBROS | RECIBOS | (PDF/KUDE → mismo submódulo) |
| `recibos/retenciones.controller.ts` | COBROS | RETENCIONES | `COBROS_RET_VER/CREAR/ANULAR` |
| `cobranzas/panel-cobrador.controller.ts` | COBROS | COBRADORES | `COBROS_COBRADOR_PANEL_VER` |
| `cobranzas/promesas.controller.ts` | COBROS | PROMESAS | `COBROS_PROMESA_VER/REGISTRAR` |
| `cobranzas/autorizaciones.controller.ts` | COBROS | AUTORIZACIONES | `COBROS_AUTORIZAR` |
| `cobranzas/config-mora.controller.ts` | COBROS | CONFIG_MORA | `COBROS_CONFIG_MORA_GESTIONAR` |
| `cobranzas/reporte-cobrador.controller.ts` | COBROS | REPORTES | `COBROS_REPORTES_VER` |
| `solicitudes-credito/solicitudes-credito.controller.ts` | CREDITOS | SOLICITUDES | `CREDITOS_SOLICITUD_VER/EVALUAR/APROBAR` |
| `planes-cuotas/planes-cuotas.controller.ts` | CREDITOS | PLANES_CUOTA | `CREDITOS_PLAN_GESTIONAR` |
| `producto-precio-cuotas/producto-precio-cuotas.controller.ts` | CREDITOS | PRECIOS_CUOTA | `CREDITOS_PRECIO_CUOTA_GESTIONAR` |
| `vendedores-cobradores/vendedores-cobradores.controller.ts` | COBROS | COBRADORES | `COBROS_COBRADOR_GESTIONAR` |
| `vendedores-cobradores/asignacion-facturas.controller.ts` | COBROS | ASIGNACION | `COBROS_ASIGNAR_FACTURAS` |
| `vendedores-cobradores/rutas-cobranza.controller.ts` | COBROS | RUTAS | `COBROS_RUTA_GESTIONAR` |
| `vendedores-cobradores/zonas-cobranza.controller.ts` | COBROS | ZONAS | `COBROS_ZONA_GESTIONAR` |
| `vendedores-cobradores/liquidaciones.controller.ts` | COBROS | LIQUIDACIONES | `COBROS_LIQUIDACION_VER/CERRAR` |
| `compras/compras.controller.ts` | COMPRAS | FACTURA_COMPRA | `COMPRAS_FACTURA_VER/CARGAR/ANULAR` |
| `ordenes-compra/ordenes-compra.controller.ts` | COMPRAS | ORDEN_COMPRA | `COMPRAS_OC_VER/CREAR/APROBAR` |
| `recepciones-compra/recepciones-compra.controller.ts` | COMPRAS | RECEPCION | `COMPRAS_RECEPCION_VER/REGISTRAR` |
| `requisiciones/requisiciones.controller.ts` | COMPRAS | REQUISICIONES | `COMPRAS_REQ_VER/CREAR/APROBAR` |
| `pagos-proveedor/pagos-proveedor.controller.ts` | COMPRAS | PAGOS | `COMPRAS_PAGO_VER/REGISTRAR/ANULAR` |
| `gastos/gastos.controller.ts` | COMPRAS | GASTOS | `COMPRAS_GASTO_VER/REGISTRAR` |
| `compra-asistida/compra-asistida.controller.ts` | COMPRAS | ASISTIDA | `COMPRAS_ASISTIDA_USAR` (addon) |
| `productos/productos.controller.ts` | INVENTARIO | PRODUCTOS | `INV_PRODUCTO_VER/CREAR/EDITAR` |
| `producto-atributos/producto-atributos.controller.ts` | INVENTARIO | ATRIBUTOS | `INV_ATRIBUTO_GESTIONAR` |
| `producto-presentaciones/producto-presentaciones.controller.ts` | INVENTARIO | PRESENTACIONES | `INV_PRESENTACION_GESTIONAR` |
| `categorias/categorias.controller.ts` | INVENTARIO | CATEGORIAS | `INV_CATEGORIA_GESTIONAR` |
| `marcas/marcas.controller.ts` | INVENTARIO | MARCAS | `INV_MARCA_GESTIONAR` |
| `lotes/lotes.controller.ts` | INVENTARIO | LOTES | `INV_LOTE_VER/GESTIONAR` |
| `stock/stock.controller.ts` | INVENTARIO | STOCK | `INV_STOCK_VER/AJUSTAR` |
| `movimientos-inventario/movimientos-inventario.controller.ts` | INVENTARIO | MOVIMIENTOS | `INV_MOV_VER/REGISTRAR` |
| `inventario-fisico/inventario-fisico.controller.ts` | INVENTARIO | INVENTARIO_FISICO | `INV_FISICO_VER/INICIAR/CERRAR` |
| `depositos/depositos.controller.ts` | INVENTARIO | DEPOSITOS | `INV_DEPOSITO_GESTIONAR` |
| `cajas/cajas.controller.ts` | TESORERIA | CAJAS | `TES_CAJA_VER/ABRIR/CERRAR` |
| `tesoreria/tesoreria.controller.ts` | TESORERIA | BANCOS | `TES_BANCO_VER/GESTIONAR` |
| `tesoreria/tes.controller.ts` | TESORERIA | MOVIMIENTOS | `TES_MOV_VER/REGISTRAR/ANULAR` |
| `rendiciones/rendiciones.controller.ts` | TESORERIA | RENDICIONES | `TES_RENDICION_VER/CERRAR` |
| `autorizaciones-caja/autorizaciones-caja.controller.ts` | TESORERIA | AUTORIZACIONES | `TES_CAJA_AUTORIZAR` |
| `medio-pago/medio-pago.controller.ts` | TESORERIA | MEDIOS_PAGO | `TES_MEDIO_PAGO_GESTIONAR` |
| `bancard/bancard-config.controller.ts` | TESORERIA | BANCARD | `TES_BANCARD_CONFIGURAR` (addon) |
| `bancard/bancard.controller.ts` | TESORERIA | BANCARD | `TES_BANCARD_USAR` |
| `bancard/pago-publico.controller.ts` | (público, sin guard) | — | — |
| `bancard/bancard-webhook.controller.ts` | (público, sin guard) | — | — |
| `contabilidad/controllers/plan-cuentas.controller.ts` | CONTABILIDAD | PLAN_CUENTAS | `CONT_PC_VER/GESTIONAR` |
| `contabilidad/controllers/asientos.controller.ts` | CONTABILIDAD | ASIENTOS | `CONT_ASIENTO_VER/CREAR/CONFIRMAR/REVERTIR` |
| `contabilidad/controllers/ejercicios.controller.ts` | CONTABILIDAD | EJERCICIOS | `CONT_EJERCICIO_GESTIONAR` |
| `contabilidad/controllers/periodos.controller.ts` | CONTABILIDAD | PERIODOS | `CONT_PERIODO_CERRAR/REABRIR/BLOQUEAR` |
| `contabilidad/controllers/centros-costo.controller.ts` | CONTABILIDAD | CENTROS_COSTO | `CONT_CC_GESTIONAR` |
| `contabilidad/controllers/mapeo-cuentas.controller.ts` | CONTABILIDAD | MAPEO | `CONT_MAPEO_GESTIONAR` |
| `contabilidad/controllers/tipo-cambio.controller.ts` | CONTABILIDAD | TIPO_CAMBIO | `CONT_TC_GESTIONAR` |
| `contabilidad/controllers/reportes.controller.ts` | CONTABILIDAD | REPORTES | `CONT_REPORTES_VER` |
| `rrhh/controllers/empleados.controller.ts` | RRHH | EMPLEADOS | `RRHH_EMP_VER/CREAR/EDITAR` |
| `rrhh/controllers/legajos-*.controller.ts` (4) | RRHH | LEGAJOS | `RRHH_LEGAJO_VER/GESTIONAR` |
| `rrhh/controllers/liquidaciones.controller.ts` | RRHH | LIQUIDACIONES | `RRHH_LIQ_VER/PROCESAR/CERRAR` |
| `rrhh/controllers/planillas.controller.ts` | RRHH | PLANILLAS | `RRHH_PLANILLA_VER/EMITIR` |
| `rrhh/controllers/marcaciones.controller.ts` | RRHH | MARCACIONES | `RRHH_MARCACION_VER/REGISTRAR` |
| `rrhh/controllers/presentismo-novedades.controller.ts` | RRHH | PRESENTISMO | `RRHH_PRESENTISMO_VER` |
| `rrhh/controllers/permisos-presentismo.controller.ts` | RRHH | PERMISOS | `RRHH_PERMISO_VER/APROBAR` |
| `rrhh/controllers/tolerancias-presentismo.controller.ts` | RRHH | TOLERANCIAS | `RRHH_TOLERANCIA_GESTIONAR` |
| `rrhh/controllers/turnos.controller.ts` | RRHH | TURNOS | `RRHH_TURNO_GESTIONAR` |
| `rrhh/controllers/relojes-marcadores.controller.ts` | RRHH | RELOJES | `RRHH_RELOJ_GESTIONAR` |
| `rrhh/controllers/anticipos.controller.ts` | RRHH | ANTICIPOS | `RRHH_ANTICIPO_VER/REGISTRAR` |
| `rrhh/controllers/prestamos.controller.ts` | RRHH | PRESTAMOS | `RRHH_PRESTAMO_VER/REGISTRAR` |
| `rrhh/controllers/vacaciones.controller.ts` | RRHH | VACACIONES | `RRHH_VACACION_VER/APROBAR` |
| `rrhh/controllers/desvinculaciones.controller.ts` | RRHH | DESVINCULACIONES | `RRHH_DESV_GESTIONAR` |
| `rrhh/controllers/novedades.controller.ts` | RRHH | NOVEDADES | `RRHH_NOVEDAD_GESTIONAR` |
| `rrhh/controllers/conceptos.controller.ts` | RRHH | CONCEPTOS | `RRHH_CONCEPTO_GESTIONAR` |
| `rrhh/controllers/parametros.controller.ts` | RRHH | PARAMETROS | `RRHH_PARAM_GESTIONAR` |
| `rrhh/controllers/catalogos.controller.ts` | RRHH | CATALOGOS | `RRHH_CATALOGO_GESTIONAR` |
| `rrhh/controllers/acreditaciones-bancarias.controller.ts` | RRHH | ACREDITACIONES | `RRHH_ACRED_GENERAR` |
| `rrhh/controllers/formatos-bancarios.controller.ts` | RRHH | FORMATOS_BANCARIOS | `RRHH_FORMATO_GESTIONAR` |
| `rrhh/controllers/reportes-ips.controller.ts` | RRHH | REPORTES_IPS | `RRHH_IPS_VER` |
| `rrhh/controllers/legajos-dashboard.controller.ts` | RRHH | DASHBOARD | `RRHH_DASH_VER` |
| `rrhh/controllers/legajos-consulta.controller.ts` | RRHH | CONSULTA | `RRHH_CONSULTA_VER` |
| `rrhh/controllers/seed.controller.ts` | (admin / sin guard de perfil) | — | requiere rol admin |
| `clientes/clientes.controller.ts` | CONTACTOS | CLIENTES | `CONTACTOS_CLI_VER/CREAR/EDITAR` |
| `proveedores/proveedores.controller.ts` | CONTACTOS | PROVEEDORES | `CONTACTOS_PROV_VER/CREAR/EDITAR` |
| `personas/personas.controller.ts` | CONTACTOS | PERSONAS | `CONTACTOS_PERSONA_VER/GESTIONAR` |
| `transportistas/transportistas.controller.ts` | CONTACTOS | TRANSPORTISTAS | `CONTACTOS_TRANSP_GESTIONAR` |
| `choferes/choferes.controller.ts` | CONTACTOS | CHOFERES | `CONTACTOS_CHOFER_GESTIONAR` |
| `vehiculos/vehiculos.controller.ts` | CONTACTOS | VEHICULOS | `CONTACTOS_VEHICULO_GESTIONAR` |
| `agentes-transporte/agentes-transporte.controller.ts` | CONTACTOS | AGENTES_TRANSP | `CONTACTOS_AGENTE_GESTIONAR` |
| `supervisores/supervisores.controller.ts` | CONTACTOS | SUPERVISORES | `CONTACTOS_SUP_GESTIONAR` |
| `importaciones/importaciones.controller.ts` | IMPORTACIONES | (varios) | `IMPORT_*` (addon) |
| `marangatu/marangatu.controller.ts` | FISCAL | MARANGATU | `FISCAL_MARANGATU_GENERAR` |
| `middleware-sifen/middleware-sifen.controller.ts` | FISCAL | SIFEN | `FISCAL_SIFEN_*` |
| `kude/kude.controller.ts` | FISCAL | KUDE | (PDF DTE, generalmente público interno) |
| `numeraciones/numeraciones.controller.ts` | FISCAL | NUMERACIONES | `FISCAL_NUM_GESTIONAR` |
| `punto-expediciones/punto-expediciones.controller.ts` | FISCAL | PUNTO_EXP | `FISCAL_PE_GESTIONAR` |
| `reportes-rentabilidad/reportes-rentabilidad.controller.ts` | REPORTES | RENTABILIDAD | `REP_RENTAB_VER` (addon) |
| `ai-dashboard/*.controller.ts` (3) | DASHBOARD_IA | (varios) | `DASH_IA_*` (addon) |
| `ayuda-ia/controllers/*.controller.ts` (2) | AYUDA_IA | (varios) | `AYUDA_IA_*` (transversal) |
| `empresas/empresas.controller.ts` | CONFIGURACION | EMPRESA | `CONFIG_EMPRESA_GESTIONAR` |
| `empresas/comisiones.controller.ts` | CONFIGURACION | COMISIONES | `CONFIG_COMISION_GESTIONAR` |
| `sucursales/sucursales.controller.ts` | CONFIGURACION | SUCURSALES | `CONFIG_SUC_GESTIONAR` |
| `asignaciones-sucursal/asignaciones-sucursal.controller.ts` | CONFIGURACION | ASIGNACIONES | `CONFIG_ASIGNACION_GESTIONAR` |
| `monedas/monedas.controller.ts` | CONFIGURACION | MONEDAS | `CONFIG_MONEDA_GESTIONAR` |
| `condiciones-pago/condiciones-pago.controller.ts` | CONFIGURACION | COND_PAGO | `CONFIG_CP_GESTIONAR` |
| `referenciales/referenciales.controller.ts` | CONFIGURACION | REFERENCIALES | `CONFIG_REF_GESTIONAR` |
| `users/users.controller.ts` | SEGURIDAD | USUARIOS | `SEG_USR_VER/CREAR/EDITAR` |
| `auth/user-empresas.controller.ts` | SEGURIDAD | USUARIOS_EMPRESA | `SEG_USR_EMPRESA_GESTIONAR` |
| `perfiles/perfiles.controller.ts` | SEGURIDAD | PERFILES | `SEG_PERFIL_VER/CREAR/EDITAR` |
| `privilegios/privilegios.controller.ts` | SEGURIDAD | PRIVILEGIOS | `SEG_PRIV_VER` (lectura) |
| `modulos/modulos.controller.ts` | SEGURIDAD | MODULOS | `SEG_MODULO_VER` (lectura) |
| `audit/audit.controller.ts` | SEGURIDAD | AUDITORIA | `SEG_AUDIT_VER` |
| `codigos-verificacion/codigos-verificacion.controller.ts` | SEGURIDAD | 2FA | (interno) |
| `notificaciones/notificaciones.controller.ts` | NOTIFICACIONES | INBOX | `NOTIF_VER/GESTIONAR` |
| `mail/mail.controller.ts` | NOTIFICACIONES | EMAIL | `NOTIF_EMAIL_ENVIAR` |
| `twilio/twilio.controller.ts` | NOTIFICACIONES | WHATSAPP | `NOTIF_WHATSAPP_*` (addon) |
| `qz/qz.controller.ts` | (interno, sin RBAC de pantalla) | — | token de cert |
| `redis/redis.controller.ts` | (interno admin) | — | requiere rol admin |
| `sync/sync.controller.ts` | (interno admin) | — | requiere rol admin |
| `plan-limits/plan-limits.controller.ts` | SUSCRIPCION | LIMITES | `SUSC_LIMITE_VER` |
| `planes/planes.controller.ts` | (super-admin Novasis) | — | rol Novasis-admin |
| `suscripciones/suscripciones.controller.ts` | SUSCRIPCION | MI_SUSCRIPCION | `SUSC_VER/GESTIONAR` |
| `app.controller.ts`, `auth/auth.controller.ts` | (público) | — | sin guard |

#### Auditoría adicional en backend

Además de los controllers, deben revisarse:

1. **Services con lógica de permisos embebida** — algunos services llaman `this.checkPermission(...)` o referencian códigos de módulo en cadenas (ej. validaciones cruzadas en `cobros.service.ts` que verifican si el usuario tiene `COBROS_AUTORIZAR`). Script:
   ```bash
   rg -n "checkPermission|requirePermission|RequireModule|RequirePermission" smartfactvoice-backend/src \
     --type ts > /tmp/backend-permisos.txt
   ```
2. **DTOs y guards específicos** — `src/auth/decorators/require-permission.decorator.ts`, `src/auth/guards/permission.guard.ts`, `src/common/decorators/require-module.decorator.ts`. Estos son el corazón del sistema y se reescriben en Fase 2; aquí solo se documenta su uso.
3. **Seeds existentes** — `prisma/seed-*.ts` que insertan privilegios o perfiles deben quedar inhabilitados y reemplazados por el seed maestro de Fase 1.
4. **Subscribers / handlers de eventos** — algunos `@OnEvent(...)` ejecutan acciones que dependen del módulo activo (ej. al crear factura se dispara movimiento de stock). Validar que el evento siga ejecutándose aunque el usuario no tenga el módulo destino activo (eventos del sistema vs. acciones de usuario).
5. **Endpoints públicos** — confirmar que ninguno de los listados como "(público)" arriba haya quedado expuesto sin querer; revisar `@Public()` o ausencia de `@UseGuards`.

#### Consistencia frontend ↔ backend

Regla de oro: **el código de privilegio que aparece en `@RequirePermission(...)` del controller es exactamente el mismo string que el frontend chequea con `can(...)`**. Si difieren, hay un bug. La auditoría 6.1 cruza ambos archivos (`/tmp/codigos-frontend.txt` vs `/tmp/codigos-backend.txt`) y reporta:

- Códigos solo en frontend → la UI deshabilita un botón cuyo endpoint no existe o no está protegido (bug pasivo).
- Códigos solo en backend → el endpoint rechaza llamadas que la UI no sabe ocultar (UX rota).
- Códigos duplicados con typos (`VENTAS_FACTURA_VER` vs `VENTAS_FACTURAS_VER`) → bug histórico, consolidar.

El mapeo de la tabla de arriba alimenta tanto al seed de submódulos como al diccionario `legacy → nuevo` que usa el codemod (6.6).

### 6.1 Auditoría inicial — inventario completo

Antes de tocar código, generar el inventario completo de qué códigos usa cada pantalla / controller. Script único:

```bash
# Frontend — extraer todos los códigos usados en can("...")
rg -oN 'can\(\s*["'\''][^"'\'']+["'\'']' pos-ventas/src \
  | sort -u > /tmp/codigos-frontend.txt

# Frontend — extraer todos los módulos usados en usePermission("...")
rg -oN 'usePermission\(\s*["'\''][^"'\'']+["'\'']' pos-ventas/src \
  | sort -u > /tmp/modulos-frontend.txt

# Backend — extraer @RequirePermission('MODULO', 'PRIVILEGIO')
rg -oN "@RequirePermission\([^)]+\)" smartfactvoice-backend/src \
  | sort -u > /tmp/permisos-backend.txt
```

Output esperado: lista deduplicada con ~150-250 códigos únicos. Esa lista es la **fuente de verdad** del refactor: cada entrada debe tener una decisión documentada ("se renombra a X", "se queda igual", "se elimina porque era genérico sin uso real").

### 6.2 Tabla de mapeo legacy → nuevo

En `src/seguridad/seeds/codigos-legacy-map.ts` (backend):

```ts
export const CODIGOS_LEGACY_MAP: Record<string, string | null> = {
  // Genéricos sueltos → renombrados al estándar
  "LEER":     null,            // se elimina, no tenía contexto
  "CREAR":    null,
  "EDITAR":   null,
  "ELIMINAR": null,
  "PROCESAR": null,

  // Códigos prefijados que cambian de nombre
  "CONT_GESTIONAR_PLAN_CUENTAS": "CONT_PLAN_CUENTAS_GESTIONAR",
  "CONT_GESTIONAR_PERIODOS":     "CONT_PERIODOS_GESTIONAR",
  "POS_VENTA_CREDITO":           "POS_VENTA_CREAR_CREDITO",
  // … (la lista completa sale de la auditoría 6.1)

  // Códigos que se quedan igual (no aparecen aquí — el alias map solo lista cambios)
};

export const MODULOS_LEGACY_MAP: Record<string, string> = {
  // Códigos viejos de módulos planos → módulo raíz nuevo
  "AJUSTES_INVENTARIO":  "INVENTARIO",   // antes era módulo plano, ahora submódulo de INVENTARIO
  "PLAN_CUENTAS":        "CONTABILIDAD", // idem
  // … etc
};
```

El mapa se construye una sola vez al inicio del refactor, en base al output de 6.1. Casos:
- **Código se mantiene** → no aparece en el map.
- **Código se renombra** → entrada `"VIEJO": "NUEVO"`.
- **Código se elimina** → entrada `"VIEJO": null` (uso del código en código fuente queda como warning para reescribir manualmente).

### 6.3 Guard con resolución de alias (ventana de transición)

`src/auth/guards/permission.guard.ts` se extiende para resolver alias al vuelo:

```ts
const codigoResuelto = CODIGOS_LEGACY_MAP[codigoPedido] ?? codigoPedido;
if (codigoResuelto === null) {
  this.logger.warn(`[permisos-legacy] Privilegio "${codigoPedido}" deprecado y sin reemplazo en ${controller}.${handler}`);
  return false; // o true según política
}
return await this.tieneAcceso(user, codigoResuelto);
```

Idem para `MODULOS_LEGACY_MAP` en `module.guard.ts`. Cada vez que alguien usa un código viejo en código fuente, el guard sigue funcionando **y loguea un warning** para que el equipo sepa qué falta refactorizar.

Esto desacopla la migración de datos (cambio en frío, decisión 6) de la migración de código (graduable módulo por módulo).

### 6.4 Refactor módulo por módulo

Orden de prioridad sugerido (de menor a mayor superficie):
1. **Contabilidad** (~6 archivos frontend, 6 controllers) — buen primer caso, dominio acotado.
2. **Tesorería** (~3 archivos)
3. **Cobranzas** (~5 archivos)
4. **Importaciones** (~3 archivos)
5. **Ventas / POS** (~10 archivos, el más complejo)
6. **Inventario** (~10 archivos)
7. **RRHH** (~15 controllers backend, el más grande)
8. **Administración** (perfiles, usuarios, empresas, sucursales, etc.)
9. **Reportes** (~10 pantallas)

Para cada módulo:

1. Identificar todas las pantallas del módulo (búsqueda por nombre o por estructura de carpetas).
2. Listar los códigos viejos que usan (de la auditoría 6.1).
3. Mapearlos al nuevo `submodulo_id` / `codigo` nuevo.
4. Reemplazar en frontend (`usePermission`, `can`) y backend (`@RequirePermission`).
5. Probar la pantalla manualmente: con perfil que tenga el permiso → entra; sin permiso → 403.
6. Eliminar las entradas correspondientes del `CODIGOS_LEGACY_MAP` (forzar que si quedó algún rezago, falle ruidosamente).
7. Commit por módulo.

### 6.5 Cleanup final

Cuando todos los módulos están refactorizados y el `CODIGOS_LEGACY_MAP` quedó vacío:

1. Eliminar el archivo `codigos-legacy-map.ts`.
2. Eliminar la lógica de resolución en los guards.
3. Cualquier código que todavía use un alias viejo va a fallar en compilación / 403 en runtime → indicador claro de que faltó algo.

### 6.6 Herramientas para acelerar el refactor

**Codemod automático** con `jscodeshift` o `ast-grep` para los cambios obvios:

```bash
# Reemplazo masivo del nombre del privilegio en JSX
ast-grep --pattern 'can("CONT_GESTIONAR_PLAN_CUENTAS")' \
         --rewrite  'can("CONT_PLAN_CUENTAS_GESTIONAR")' \
         pos-ventas/src
```

Cubre el 60-70% de los reemplazos sin riesgo. El resto requiere revisión manual porque hay casos donde un solo código viejo se desdobla en varios nuevos (ej. `EDITAR` genérico → `INV_AJUSTE_EDITAR` o `INV_STOCK_EDITAR` según contexto).

**Test de smoke por módulo**: Playwright o Cypress con tests mínimos por pantalla que loguean con cada perfil y verifican que cargan / 403. No reemplaza testing manual pero da una primera red de seguridad. Si ya hay tests E2E, ampliarlos. Si no, justificar el costo (probablemente no vale para esta etapa; el smoke manual alcanza).

**Hook de pre-commit** que rechaza commits que introduzcan códigos del `CODIGOS_LEGACY_MAP` en archivos nuevos (solo permite removerlos). Evita regresiones durante el refactor.

### 6.7 Compatibilidad con el hook `usePermission`

La API del hook se mantiene idéntica:
```jsx
const { can } = usePermission("CONTABILIDAD");
if (can("CONT_PLAN_CUENTAS_GESTIONAR")) { ... }
```

Internamente, el hook ahora consulta los privilegios del usuario contra la nueva tabla (que ya tiene jerarquía), pero la firma no cambia. Esto significa que las pantallas se actualizan **solo cambiando los strings**, sin tocar la estructura del componente. Es una migración 1-a-1 sin refactor estructural.

### 6.8 Tracker de implementación módulo por módulo (living doc)

Este tracker se actualiza **en cada PR**. Cada módulo tiene su sub-checklist: backend (controllers + privilegios + guard) y frontend (pantalla + tabs + acciones + UI guards). Las acciones por defecto son `VER / CREAR / EDITAR / ANULAR / EXPORTAR / CONFIGURAR`, ajustables por contexto.

**Convenciones del tracker**:
- `[ ]` = pendiente · `[~]` = en curso · `[x]` = completado · `[—]` = no aplica.
- **Backend** se completa cuando: (a) controller usa `@RequirePermission(SUBMODULO_CODIGO, PRIVILEGIO_CODIGO)` con códigos nuevos; (b) seed inserta el submódulo + privilegios; (c) test smoke 200/403 con perfil sin permiso.
- **Frontend** se completa cuando: (a) `usePermission(SUBMODULO_CODIGO)` reemplaza al legacy; (b) cada botón/acción usa `can('PRIVILEGIO')` con código nuevo; (c) sidebar/breadcrumb correctos; (d) verificación visual en pantalla.
- **Integración** se completa cuando: frontend ↔ backend ↔ seed coinciden en strings (sin entradas en `CODIGOS_LEGACY_MAP`).

**Orden recomendado** (de menor a mayor superficie, copiado de 6.4):
1. Contabilidad → 2. Tesorería → 3. Cobranzas → 4. Importaciones → 5. Ventas/POS → 6. Inventario → 7. RRHH → 8. Administración → 9. Reportes.

Para **cada módulo** se debe seguir esta secuencia interna:

```
A. Seed: declarar submódulos + privilegios en seguridad.seed-data.ts y correr seed.
B. Backend (controller por controller, acción por acción):
   B1. Renombrar @RequirePermission con código nuevo.
   B2. Si el controller tenía @RequireModule legacy, migrar a submódulo.
   B3. Smoke test: con perfil sin privilegio → 403; con perfil que sí lo tiene → 200.
C. Frontend (pantalla por pantalla, tab por tab):
   C1. usePermission(SUBMODULO_CODIGO_NUEVO).
   C2. can('PRIVILEGIO_CODIGO_NUEVO') en cada botón/menú/sección.
   C3. Sidebar item y breadcrumb apuntando al submódulo correcto.
   C4. Verificación visual con perfil sin permiso → botón oculto/disabled.
D. Cleanup: quitar entradas del CODIGOS_LEGACY_MAP, commit por módulo.
```

---

#### 6.8.1 — CONTABILIDAD

Controllers backend: 8 · Pantallas frontend: ~6 · Submódulos: 8.

**Seed** ✅ (paso A completado 2026-06-02)
- [x] Submódulos declarados (8): CONT_ASIENTOS, CONT_PLAN_CUENTAS, CONT_EJERCICIOS, CONT_PERIODOS, CONT_MAPEO, CONT_CENTROS_COSTO, CONT_TIPO_CAMBIO, CONT_REPORTES.
- [x] Privilegios declarados (34) — ver columna "Privilegio" en cada pestaña.
- [x] `seguridad.seed-data.ts` corrido vía `scripts/seed-seguridad.ts` (idempotente).

**Paso B (Backend controllers)** ✅ (completado 2026-06-02)
- [x] `asientos.controller.ts` — 8 decorators actualizados a `CONT_ASI_ASIENTO_*`.
- [x] `plan-cuentas.controller.ts` — 5 decorators a `CONT_PC_CUENTA_*` (+IMPORTAR en `/seed`).
- [x] `ejercicios.controller.ts` — 4 decorators a `CONT_EJE_EJERCICIO_*` (crearPeriodo → EDITAR).
- [x] `periodos.controller.ts` — 5 decorators a `CONT_PER_PERIODO_*`.
- [x] `mapeo-cuentas.controller.ts` — 3 decorators a `CONT_MAP_MAPEO_*`.
- [x] `centros-costo.controller.ts` — 4 decorators a `CONT_CC_CENTRO_COSTO_*`.
- [x] `tipo-cambio.controller.ts` — 3 decorators a `CONT_TC_TIPO_CAMBIO_*`.
- [x] `reportes.controller.ts` — class-level `CONT_REP_REPORTE_VER` + override `CONT_REP_REPORTE_EXPORTAR` en 7 endpoints PDF/Excel.
- [x] Verificado: 0 ocurrencias de códigos legacy (`CONT_VER`, `CONT_CONFIG`, `CONT_CREAR_ASIENTO`, `CONT_GESTIONAR_*`, `CONT_CONFIRMAR_ASIENTO`, `CONT_REVERTIR_ASIENTO`, `CONT_VER_REPORTES`) en `src/contabilidad/`.
- [ ] Acciones aún sin endpoint: `CONT_ASI_ASIENTO_APROBAR`, `CONT_ASI_ASIENTO_EXPORTAR` (no implementadas en controller actual — diferidas).

**Paso C (Frontend pantallas)** ✅ (completado 2026-06-02)
- [x] `AsientosTab.jsx` — split `CONT_CREAR_ASIENTO` umbrella en EDITAR/CREAR/ELIMINAR; rename CONFIRMAR/REVERTIR.
- [x] `PlanCuentasTab.jsx` — `canManage` reemplazado por `canCreate/canEdit/canDelete/canImport`; TreeNode recibe `canEdit/canDelete` split.
- [x] `EjerciciosTab.jsx` — `canGestionar` reemplazado por `canCrearEjer/canCerrarEjer/canCerrarPer/canReabrirPer/canBloquearPer`.
- [x] `MapeoCuentasTab.jsx` — `CONT_CONFIG` → `CONT_MAP_MAPEO_EDITAR`.
- [x] `CentrosCostoTab.jsx` — `canConfig` split en `canCreate/canEdit/canDelete`.
- [x] `TipoCambioTab.jsx` — `CONT_CONFIG` → `CONT_TC_TIPO_CAMBIO_CREAR`.
- [x] `ReportesContablesTab.jsx` — `CONT_VER_REPORTES` → `CONT_REP_REPORTE_VER`.
- [x] `pages/Reportes.jsx` — gate dashboard de reportes actualizado a `CONT_REP_REPORTE_VER`.
- [x] Verificado: 0 ocurrencias de códigos legacy CONT_* en `pos-ventas/src/`.

**Pestaña: Asientos** (`CONT_ASIENTOS`) — controller: `contabilidad/asientos.controller.ts`
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver | `CONT_ASI_ASIENTO_VER` | asientos.controller | `src/components/contabilidad/Asientos*.jsx` | [ ] |
| Crear | `CONT_ASI_ASIENTO_CREAR` | idem | idem | [ ] |
| Editar | `CONT_ASI_ASIENTO_EDITAR` | idem | idem | [ ] |
| Eliminar | `CONT_ASI_ASIENTO_ELIMINAR` | idem | idem | [ ] |
| Confirmar | `CONT_ASI_ASIENTO_CONFIRMAR` | idem | idem | [ ] |
| Aprobar | `CONT_ASI_ASIENTO_APROBAR` | idem | idem | [ ] |
| Revertir | `CONT_ASI_ASIENTO_REVERTIR` | idem | idem | [ ] |
| Exportar | `CONT_ASI_ASIENTO_EXPORTAR` | idem | idem | [ ] |

**Pestaña: Plan de Cuentas** (`CONT_PLAN_CUENTAS`) — controller: `plan-cuentas.controller.ts`
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver | `CONT_PC_CUENTA_VER` | plan-cuentas.controller | `PlanCuentas*.jsx` | [ ] |
| Crear | `CONT_PC_CUENTA_CREAR` | idem | idem | [ ] |
| Editar | `CONT_PC_CUENTA_EDITAR` | idem | idem | [ ] |
| Eliminar | `CONT_PC_CUENTA_ELIMINAR` | idem | idem | [ ] |
| Importar | `CONT_PC_CUENTA_IMPORTAR` | idem | idem | [ ] |
| Exportar | `CONT_PC_CUENTA_EXPORTAR` | idem | idem | [ ] |

**Pestaña: Ejercicios** (`CONT_EJERCICIOS`) — controller: `ejercicios.controller.ts`
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver | `CONT_EJE_EJERCICIO_VER` | ejercicios.controller | `Ejercicios*.jsx` | [ ] |
| Crear | `CONT_EJE_EJERCICIO_CREAR` | idem | idem | [ ] |
| Editar | `CONT_EJE_EJERCICIO_EDITAR` | idem | idem | [ ] |
| Cerrar ejercicio | `CONT_EJE_EJERCICIO_CERRAR` | idem | idem | [ ] |

**Pestaña: Períodos** (`CONT_PERIODOS`) — controller: `periodos.controller.ts`
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver | `CONT_PER_PERIODO_VER` | periodos.controller | `Periodos*.jsx` / dentro de Ejercicios | [ ] |
| Cerrar | `CONT_PER_PERIODO_CERRAR` | idem | idem | [ ] |
| Reabrir | `CONT_PER_PERIODO_REABRIR` | idem | idem | [ ] |
| Bloquear | `CONT_PER_PERIODO_BLOQUEAR` | idem | idem | [ ] |

**Pestaña: Mapeo de Cuentas** (`CONT_MAPEO`) — controller: `mapeo-cuentas.controller.ts`
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver | `CONT_MAP_MAPEO_VER` | mapeo-cuentas.controller | `MapeoCuentas*.jsx` | [ ] |
| Editar | `CONT_MAP_MAPEO_EDITAR` | idem | idem | [ ] |

**Pestaña: Centros de Costo** (`CONT_CENTROS_COSTO`) — controller: `centros-costo.controller.ts`
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver | `CONT_CC_CENTRO_COSTO_VER` | centros-costo.controller | `CentrosCosto*.jsx` | [ ] |
| Crear | `CONT_CC_CENTRO_COSTO_CREAR` | idem | idem | [ ] |
| Editar | `CONT_CC_CENTRO_COSTO_EDITAR` | idem | idem | [ ] |
| Eliminar | `CONT_CC_CENTRO_COSTO_ELIMINAR` | idem | idem | [ ] |

**Pestaña: Tipo de Cambio** (`CONT_TIPO_CAMBIO`) — controller: `tipo-cambio.controller.ts`
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver | `CONT_TC_TIPO_CAMBIO_VER` | tipo-cambio.controller | `TipoCambio*.jsx` | [ ] |
| Crear | `CONT_TC_TIPO_CAMBIO_CREAR` | idem | idem | [ ] |
| Editar | `CONT_TC_TIPO_CAMBIO_EDITAR` | idem | idem | [ ] |
| Eliminar | `CONT_TC_TIPO_CAMBIO_ELIMINAR` | idem | idem | [ ] |

**Pestaña: Reportes Contables** (`CONT_REPORTES`) — controller: `reportes.controller.ts`
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver (Libro Diario, Mayor, Balance Comprob., E. Resultados, B. General) | `CONT_REP_REPORTE_VER` | reportes.controller | `Reportes*.jsx` | [ ] |
| Exportar | `CONT_REP_REPORTE_EXPORTAR` | idem | idem | [ ] |

**Validación de cierre del módulo CONTABILIDAD**:
- [x] Cada controller con `@RequirePermission` apuntando a su privilegio nuevo.
- [x] Sidebar muestra `Contabilidad › <submódulo>` correctamente.
- [x] Perfil sin `CONT_*` → no ve el módulo en el sidebar.
- [x] Endpoints responden 403 ante perfil sin privilegio (smoke test manual con `usucontador`).
- [x] `CODIGOS_LEGACY_MAP` ya no contiene strings `CONT_*` viejos.

**Bugs encontrados y resueltos durante validación (2026-06-02)**

1. **`perfiles_privilegios.modulo_id` se insertaba NULL** desde 3 paths distintos:
   - Seed orchestrator (`seed-orchestrator.service.ts`) → arreglado, agrega `modulo_id` resuelto vía `privilegio → submodulo → modulo`.
   - `seguridad.service.ts` (`crearPerfil`/`actualizarPerfil`/`clonarPerfil`) → arreglado con helper `mapearPrivilegiosAModulos()`.
   - `auth.service.ts` → robustecido para leer modulo desde `privilegios.submodulos.modulos` con fallback a la columna legacy.
   - Backfill SQL ejecutado: 0 NULLs restantes en `perfiles_privilegios`.

2. **Perfil sistema "Contador" quedó con 21/34 privs CONTABILIDAD** porque el seed se corrió antes de declarar los nuevos códigos (`CONT_PER_*`, `CONT_TC_*`, `CONT_CC_*`, etc.) y el orquestador hacía skip total sobre perfiles ya existentes.
   - SQL backfill: 13 privilegios faltantes agregados al perfil "Contador" (ahora 34/34).
   - Orquestador mejorado (`seed-orchestrator.service.ts:222-275`): cuando un perfil sistema ya existe, agrega los privilegios faltantes según selectores en lugar de skip total. Idempotente, seguro para re-correr.

3. **React Query cache collision en perfiles** entre `UsuariosStack.jsx` y `PerfilesStack.jsx` (mismo `queryKey: ["perfiles"]` con `queryFn` distintos) → causaba que perfiles clonados no aparecieran en el dropdown de Editar Usuario. Arreglado: `UsuariosStack.jsx` ahora usa `getPerfilesEmpresa` (mismo endpoint que PerfilesStack).

**Acción manual pendiente**: re-clonar la "Copia de Contador" existente (o re-marcar los 14 privilegios faltantes a mano) para que herede los 34/34. Para nuevas copias el bug ya está fijado en código.

---

#### 6.8.2 — TESORERIA  ✅ Pasos A/B/C completos · ⏳ Paso D (validación runtime) pendiente

Controllers backend: 7 · Pantallas frontend: ~3 (con tabs) · Submódulos: 12.

**Seed** ✅
- [x] Submódulos implementados: TES_MOVIMIENTOS, TES_CHEQUES, TES_CONCILIACION, TES_REPORTES, TES_CATEGORIAS, TES_CUENTAS, TES_BANCOS, TES_CAJAS, TES_TRANSFERENCIAS, TES_REGLAS_AUTO, TES_PARAMETROS.
- Códigos finales con formato 2-niveles `TES_<SUB>_<RECURSO>_<ACCION>` (ej. `TES_MOV_MOVIMIENTO_VER`).
- Perfiles sistema (Tesorero, Cajero, Supervisor) actualizados; selectores corregidos `TES_CAJA.*` → `TES_CAJAS.*`.

**Pestaña: Movimientos** (`TES_MOVIMIENTOS`) ✅
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver | `TES_MOV_MOVIMIENTO_VER` | `tesoreria/tes.controller.ts`, `tesoreria.controller.ts` | `TesMovimientosTab.jsx` | [x] |
| Crear | `TES_MOV_MOVIMIENTO_CREAR` | idem | idem | [x] |
| Aprobar | `TES_MOV_MOVIMIENTO_APROBAR` | idem | idem | [x] |
| Anular | `TES_MOV_MOVIMIENTO_ANULAR` | idem | idem | [x] |

**Pestaña: Cheques** (`TES_CHEQUES`) ✅
- [x] `TES_CHQ_CHEQUE_{VER,CREAR,DEPOSITAR,ANULAR,ELIMINAR}` — `tes.controller.ts` / `TesChequesTab.jsx`.

**Pestaña: Conciliación** (`TES_CONCILIACION`) ✅
- [x] `TES_CONC_CONCILIACION_VER` (GETs) / `TES_CONC_CONCILIACION_PROCESAR` (mutaciones).

**Pestaña: Reportes Tesorería** (`TES_REPORTES`) ✅
- [x] `TES_REP_LIBRO_CAJA_VER`, `TES_REP_LIBRO_BANCO_VER`, `TES_REP_FLUJO_CAJA_VER`, `TES_REP_REPORTE_EXPORTAR` (Excel).
- [x] Frontend: `ReportePosicionCaja.jsx`, `ReporteFlujoCaja.jsx`, `TesReportesTab.jsx`, `TesoreriaYBancos.jsx`.

**Pestaña: Transferencias** (`TES_TRANSFERENCIAS`) ✅
- [x] `TES_TRF_TRANSFERENCIA_{VER,CREAR,ANULAR}`.

**Configuración** ✅
- [x] Cuentas: `TES_CTA_CUENTA_BANCARIA_{VER,CREAR,EDITAR}` — `TesBancosTab.jsx`.
- [x] Categorías: `TES_CAT_CATEGORIA_{VER,CREAR,EDITAR,ELIMINAR}`.
- [x] Bancos: `TES_BAN_BANCO_{VER,CREAR,EDITAR,ELIMINAR}`.
- [x] Cajas: `TES_CAJ_CAJA_{VER,CREAR,EDITAR,ELIMINAR,ABRIR,CERRAR,ARQUEAR}` — controlador `tesoreria.controller.ts` (que antes NO tenía decoradores, era gap de seguridad).
- [x] Reglas Auto: `TES_REG_REGLA_CONCILIACION_{VER,EDITAR,ELIMINAR}`.
- [x] Parámetros: `TES_PAR_PARAMETRO_{VER,CONFIGURAR}`.

**Validación de cierre**: ⏳ probar runtime con perfil sistema "Tesorero": cada tab/acción respeta privilegio, sin priv → 403, admin → OK.

---

#### 6.8.2.1 — Mapeo pantalla "Finanzas" (`pages/Tesoreria.jsx`)

La pantalla del sidebar "Finanzas" es un **dashboard cross-módulo**, no un módulo de permisos. Decisión: **no crear módulo FINANZAS** en el catálogo; cada pestaña usa el privilegio de su módulo real (TESORERIA / COBRANZAS / COMPRAS).

| Pestaña | Módulo destino | Privilegio | Frontend | Estado |
|---|---|---|---|---|
| Caja del Día | TESORERIA | `TES_CAJ_CAJA_VER` (+ ABRIR/CERRAR) | `Tesoreria.jsx` | ✅ |
| Comisiones | COBRANZAS | `COB_COM_COMISION_VER` | `ComisionesWrapper` | ✅ |
| Liquidaciones | COBRANZAS | `COB_COM_LIQUIDACION_VER` (+ PAGAR) | `LiquidacionesPanel` | ✅ |
| Asignación | COBRANZAS | `COB_ASG_ASIGNACION_VER` (+ ASIGNAR) | `AsignacionCobranzaPanel` | ✅ |
| Rendiciones | COBRANZAS | `COB_RND_RENDICION_VER` | `RendicionesPanel` | ✅ |
| Recibos | COBRANZAS | `COB_REC_RECIBO_VER` | `RecibosPanel` | ✅ |
| Libro Retenciones | COBRANZAS | `COB_RET_RETENCION_VER` | `LibroRetencionesPanel` | ✅ |
| Saldos a Favor | COBRANZAS | `COB_SDF_SALDO_FAVOR_VER` | `SaldosFavorPanel` | ✅ |
| Revisión CxC | COBRANZAS | `COB_CC_CUENTA_COBRAR_VER` | `RevisionCxCTab` | ✅ |
| Hoja de Ruta | COBRANZAS | `COB_HRT_HOJA_RUTA_VER` | `HojaRutaTab` | ✅ |
| Cuentas a Pagar | COMPRAS | `CMP_CXP_CUENTA_PAGAR_VER` | `CuentasPagarTab` | ✅ |
| Orden de Pago | COMPRAS | `CMP_OP_ORDEN_PAGO_VER` (+ EMITIR/ANULAR) | `ComprasPagosTab` | ✅ |

**Submódulos nuevos agregados al seed:**
- COBRANZAS → `COB_ASIGNACION`, `COB_RETENCIONES`, `COB_SALDOS_FAVOR`, `COB_HOJA_RUTA`.
- COMPRAS → `COMP_CUENTAS_PAGAR`, `COMP_ORDEN_PAGO`.

**Fase 1 backend Finanzas — ✅ COMPLETA**

Controllers decorados con `@RequireModule` + `@RequirePermission` (módulo + privilegio 2-niveles):

| Controller | Módulo | Estado |
|---|---|---|
| `recibos/recibos.controller.ts` | COBRANZAS | ✅ |
| `recibos/retenciones.controller.ts` | COBRANZAS | ✅ |
| `cobros/cobros.controller.ts` | COBRANZAS | ✅ |
| `rendiciones/rendiciones.controller.ts` | COBRANZAS | ✅ |
| `vendedores-cobradores/liquidaciones.controller.ts` | COBRANZAS | ✅ |
| `vendedores-cobradores/asignacion-facturas.controller.ts` | COBRANZAS | ✅ |
| `pagos-proveedor/pagos-proveedor.controller.ts` | COMPRAS | ✅ |

Nota: `empresas/comisiones.controller.ts` quedó **fuera** de Fase 1 — corresponde a comisiones SaaS reseller/holding (dominio ADMINISTRACION/JERARQUIA), no a comisiones de cobradores/vendedores. Se trata en su propia fase.

---

#### 6.8.3 — COBROS / COBRANZAS  ✅ Fase 2 backend completa · ✅ Fase 3 frontend completa (lotes A–D)

Controllers backend: 13 · Pantallas frontend: ~14 · Submódulos: 14.

**Submódulos en seed (`seguridad.seed-data.ts`)**

| Submódulo | Privilegios principales | Estado |
|---|---|---|
| `COB_RECIBOS` | `COB_REC_RECIBO_VER/CREAR/ANULAR/IMPRIMIR`, `COB_REC_DESCUENTO_APLICAR` | ✅ |
| `COB_RETENCIONES` | `COB_RET_RETENCION_VER/CREAR/ANULAR` | ✅ |
| `COB_RENDICIONES` | `COB_RND_RENDICION_VER/CERRAR/REABRIR` | ✅ |
| `COB_ASIGNACION` | `COB_ASG_ASIGNACION_VER/ASIGNAR` | ✅ |
| `COB_COMISIONES` | `COB_COM_COMISION_VER/GENERAR/ELIMINAR`, `COB_COM_LIQUIDACION_VER/GENERAR/PAGAR/ANULAR` | ✅ |
| `COB_CUENTAS_COBRAR` | `COB_CC_CUENTA_COBRAR_VER/EDITAR`, `COB_CC_CUENTA_COBRAR_EXPORTAR` | ✅ |
| `COB_COBRADORES` | `COB_CBR_COBRADOR_VER/CREAR/EDITAR/ELIMINAR`, `COB_CBR_PANEL_COBRADOR_VER` | ✅ |
| `COB_RUTAS` (addon) | `COB_RUT_RUTA_VER/CREAR/EDITAR/ELIMINAR` | ✅ |
| `COB_ZONAS` | `COB_ZON_ZONA_VER/CREAR/EDITAR/ELIMINAR` | ✅ |
| `COB_PROMESAS` | `COB_PRM_PROMESA_VER/CREAR/EDITAR` | ✅ |
| `COB_AUTORIZACIONES` | `COB_AUT_AUTORIZACION_VER/SOLICITAR/AUTORIZAR` | ✅ |
| `COB_CONFIG_MORA` | `COB_CFM_CONFIG_VER/EDITAR` | ✅ |
| `COB_REPORTES` | `COB_REP_REPORTE_VER/EXPORTAR` | ✅ |
| `COB_SALDOS_FAVOR` | `COB_SDF_SALDO_FAVOR_VER/...` | ✅ |
| `COB_HOJA_RUTA` | `COB_HRT_HOJA_RUTA_VER` | ✅ |

**Fase 2 backend — Controllers decorados (`@RequireModule('COBRANZAS')` + `@RequirePermission`)**

| Controller | Privilegios principales | Estado |
|---|---|---|
| `cobranzas/config-mora.controller.ts` | `COB_CFM_CONFIG_VER/EDITAR` | ✅ |
| `cobranzas/promesas.controller.ts` | `COB_PRM_PROMESA_VER/CREAR/EDITAR` | ✅ |
| `cobranzas/autorizaciones.controller.ts` | `COB_AUT_AUTORIZACION_VER/SOLICITAR/AUTORIZAR` (+ `COB_REC_RECIBO_CREAR` en `usar`) | ✅ |
| `cobranzas/panel-cobrador.controller.ts` | `COB_CBR_PANEL_COBRADOR_VER` | ✅ |
| `cobranzas/reporte-cobrador.controller.ts` | `COB_REP_REPORTE_VER` | ✅ |
| `vendedores-cobradores/vendedores-cobradores.controller.ts` | `COB_CBR_COBRADOR_VER/CREAR/EDITAR/ELIMINAR` (+ `COB_CBR_PANEL_COBRADOR_VER` en `mi-cobrador`) | ✅ |
| `vendedores-cobradores/rutas-cobranza.controller.ts` | `COB_RUT_RUTA_VER/CREAR/EDITAR` | ✅ |
| `vendedores-cobradores/zonas-cobranza.controller.ts` | `COB_ZON_ZONA_VER/CREAR/EDITAR/ELIMINAR` | ✅ |

**Fase 3 frontend — Refactor de pantallas a `usePermission("COBRANZAS")` con códigos 2-niveles**

Lote A — Pantallas top-level
| Pantalla | Mapeo | Estado |
|---|---|---|
| `CobrosTemplateV2.jsx` | `canPos("DESCUENTO")` → `COB_REC_DESCUENTO_APLICAR`; gating asignar cobrador | ✅ |
| `PanelSupervisor.jsx` | `FINANZAS` → `COB_AUT_AUTORIZACION_VER` (gate principal); `PANEL_COBRADOR` → `COB_CBR_PANEL_COBRADOR_VER` (tab) | ✅ |
| `PanelCobrador.jsx` | Sin gate previo → `COB_CBR_PANEL_COBRADOR_VER` + `AccesoRestringido` | ✅ |
| `ReporteCobrador.jsx` | `CUENTAS_COBRAR` → `COB_REP_REPORTE_VER` | ✅ |
| `DashboardMorosidad.jsx` | `INTERES_MORA` (addon) + `COB_REP_REPORTE_VER` | ✅ |
| `ConfigMora.jsx` | `INTERES_MORA` (addon) + `COB_CFM_CONFIG_VER/EDITAR` (botón Guardar gateado) | ✅ |

Lote B — Módulo VendedoresCobradores
| Pantalla | Mapeo | Estado |
|---|---|---|
| `VendedoresCobradoresList.jsx` | `VENDEDORES` (legacy) → `COB_CBR_COBRADOR_VER/CREAR/EDITAR` | ✅ |
| `AsignacionCobranzaPanel.jsx` | `VENDEDORES.ASIGNAR` → `COB_ASG_ASIGNACION_ASIGNAR` | ✅ |
| `ZonasCobranzaList.jsx` | Sin gate previo → `COB_ZON_ZONA_CREAR/EDITAR` (botones Nueva/Editar/Toggle) | ✅ |
| `ComisionesPanel.jsx` | Sin gate previo → `COB_COM_COMISION_GENERAR` (botón Generar históricas) | ✅ |
| `LiquidacionesPanel.jsx` | DetalleDialog → `COB_COM_LIQUIDACION_ANULAR/PAGAR` (Anular + Marcar pagada) | ✅ |

Lote C — CxC
| Pantalla | Mapeo | Estado |
|---|---|---|
| `CuentasCobrar.jsx` | Sin gate previo → `COB_CC_CUENTA_COBRAR_VER` (top-level) + `COB_CC_CUENTA_COBRAR_EXPORTAR` (botón Excel) | ✅ |

> ⚠️ Solicitudes de Crédito se movió a **VENTAS** (sub-módulo `VENTAS_SOLICITUDES_CREDITO`) porque es una operación de la vendedora en la casa comercial cuando el cliente solicita la línea de crédito, no una operación del cobrador. Ver sección 6.8.5.

Lote D — Vistas transversales
| Pantalla | Mapeo | Estado |
|---|---|---|
| `Reportes.jsx` (categoría Cobranzas) | `PANEL_COBRADOR`/`CUENTAS_COBRAR` → `COBRANZAS` + `COB_REP_REPORTE_VER` | ✅ |
| `ConfiguracionNew.jsx` (tab Mora) | `INTERES_MORA.canRead` → `COB_CFM_CONFIG_VER` (manteniendo addon `INTERES_MORA`) | ✅ |

**Pendiente / fuera de Fase 3:**
- `HojaRutaTab.jsx`: sin gating frontend (backend enforces vía `COB_HRT_HOJA_RUTA_VER`/`COB_RUT_RUTA_VER`).
- Validación runtime end-to-end con perfiles de privilegios parciales.

**Validación de cierre**: idem 6.8.1.

---

#### 6.8.4 — IMPORTACIONES

Controllers backend: 3-5 · Pantallas frontend: ~3 (con tabs) · Submódulos: 5.

**Seed**
- [ ] Submódulos: IMP_EMBARQUES, IMP_DESPACHOS, IMP_COSTOS, IMP_ISC_VEHICULOS, IMP_REPORTES, IMP_CONFIG (es_addon=true a nivel módulo raíz).

**Pestañas**: Embarques, Despachos, Costos, ISC Vehículos, Reportes, Config.
- [ ] Por cada uno: `*_VER`, `*_GESTIONAR`, `*_EXPORTAR` donde aplique.

**Validación de cierre**: addon → toggle de habilitación funciona.

---

#### 6.8.5 — VENTAS / POS

Controllers backend: ~10 · Pantallas frontend: ~10 (el más grande del lado ventas) · Submódulos: 12+.

**Seed**
- [ ] Submódulos: VEN_POS, VEN_FACTURACION, VEN_NOTAS_CREDITO, VEN_NOTAS_DEBITO, VEN_PEDIDOS, VEN_ORDENES_VENTA, VEN_PRESUPUESTOS, VEN_OFERTAS, VEN_LISTAS_PRECIOS, VEN_REMISIONES, VEN_MAYORISTAS (es_addon), VEN_CONFIG.

**Pestaña: POS** (`VEN_POS`)
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Operar POS | `VEN_POS_OPERAR` | `pos-config.controller.ts` + ventas | `POS*.jsx` | [ ] |
| Aplicar descuento | `VEN_POS_DESCUENTO_APLICAR` | idem | idem | [ ] |
| Cambiar precio | `VEN_POS_PRECIO_EDITAR` | idem | idem | [ ] |
| Abrir/Cerrar caja POS | `VEN_POS_CAJA_GESTIONAR` | idem | idem | [ ] |
| Configurar POS | `VEN_POS_CONFIGURAR` | idem | idem | [ ] |

**Pestaña: Facturación** (`VEN_FACTURACION`)
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver facturas | `VEN_FACTURACION_VER` | `facturas/facturas.controller.ts` | `Facturas*.jsx` | [ ] |
| Crear factura | `VEN_FACTURACION_CREAR` | idem | idem | [ ] |
| Anular | `VEN_FACTURACION_ANULAR` | idem | idem | [ ] |
| Reimprimir | `VEN_FACTURACION_REIMPRIMIR` | idem | idem | [ ] |
| Editar precios al facturar | `VEN_FACTURACION_PRECIO_EDITAR` | idem | idem | [ ] |
| Exportar | `VEN_FACTURACION_EXPORTAR` | idem | idem | [ ] |

**Pestaña: Notas de Crédito / Débito** (`VEN_NOTAS_CREDITO`, `VEN_NOTAS_DEBITO`)
- [ ] Ver / Crear / Aplicar / Anular (NC).
- [ ] Ver / Crear / Anular (ND).

**Pestaña: Pedidos / Órdenes de Venta** (`VEN_PEDIDOS`, `VEN_ORDENES_VENTA`)
- [ ] Pedidos: Ver / Crear / Aprobar / Anular.
- [ ] OV: Ver / Crear / Confirmar / Anular / Configurar.

**Pestaña: Presupuestos** (`VEN_PRESUPUESTOS`) ✅ ya migrado parcialmente en seed actual
- [ ] Ver / Crear / Editar / Convertir-a-factura / Configurar.

**Pestaña: Ofertas / Listas de Precios** (`VEN_OFERTAS`, `VEN_LISTAS_PRECIOS`)
- [ ] Ver / Gestionar para ambos.

**Pestaña: Remisiones** (`VEN_REMISIONES`)
- [ ] Ver / Crear / Anular.

**Pestaña: Mayoristas (addon)** (`VEN_MAYORISTAS`)
- [ ] Operar / Configurar.

**Pestaña: Solicitudes de Crédito** (`VENTAS_SOLICITUDES_CREDITO`) ✅ migrado
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver solicitudes | `VEN_SOL_SOLICITUD_VER` | `solicitudes-credito/solicitudes-credito.controller.ts` (clase `@RequireModule('VENTAS')`) | `SolicitudesCredito.jsx`, `SolicitudesCreditoTab.jsx`, `ProtectedRoute permission="VEN_SOL_SOLICITUD_VER"` | ✅ |
| Crear solicitud | `VEN_SOL_SOLICITUD_CREAR` | idem (Post `/`, Post `/:id/items`) | `SolicitudesCredito.jsx` botón Nueva + `NuevaSolicitudCredito` route | ✅ |
| Editar / Enviar a aprobación / Items / Facturada | `VEN_SOL_SOLICITUD_EDITAR` | idem (Put, Patch enviar/facturada, items CRUD) | `SolicitudCreditoDetalleDialog.jsx` `canEdit` | ✅ |
| Aprobar / Rechazar | `VEN_SOL_SOLICITUD_APROBAR` | Patch `/:id/aprobar`, Patch `/:id/rechazar` | `SolicitudCreditoDetalleDialog.jsx` `canApprove` | ✅ |
| Eliminar / Cancelar | `VEN_SOL_SOLICITUD_ELIMINAR` | Delete `/:id`, Patch `/:id/cancelar` | (no expuesto en frontend; backend enforce) | ✅ |

Cambios colaterales:
- `dataEstatica.jsx` — entrada "Créditos" pasa de `modulo: "SOLICITUD_CREDITO"` a `modulo: "VENTAS", permission: "VEN_SOL_SOLICITUD_VER"`.
- `Sidebar.jsx` / `MenuMovil.jsx` — filtro extendido para honrar `item.permission` además de `item.modulo`.
- `ProtectedRoute.jsx` — acepta prop opcional `permission` (capa 3: privilegio dentro del módulo).
- `POSAdminTemplate.jsx` — `hasModule("SOLICITUD_CREDITO")` → `hasPermission("VENTAS", "VEN_SOL_SOLICITUD_VER")`.
- Perfil seed `Vendedor` recibe `VENTAS_SOLICITUDES_CREDITO.VER`, `VEN_SOL_SOLICITUD_CREAR`, `VEN_SOL_SOLICITUD_EDITAR`.

**Validación de cierre**: idem + verificar que el POS no rompa al loguearse con perfil sin permiso.

---

#### 6.8.6 — INVENTARIO

Controllers backend: ~10 · Pantallas frontend: ~10 (con tabs) · Submódulos: 12.

**Seed**
- [ ] Submódulos: INV_PRODUCTOS, INV_STOCK, INV_AJUSTES, INV_MOVIMIENTOS, INV_TRANSFERENCIAS, INV_CATEGORIAS, INV_MARCAS, INV_ATRIBUTOS, INV_OFERTAS, INV_CONTEO_FISICO, INV_DEPOSITOS, INV_LOTES, INV_PRESENTACIONES.

**Pestaña: Productos** (`INV_PRODUCTOS`)
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver | `INV_PRODUCTOS_VER` | `productos/productos.controller.ts` | `Productos*.jsx` | [ ] |
| Crear | `INV_PRODUCTOS_CREAR` | idem | idem | [ ] |
| Editar | `INV_PRODUCTOS_EDITAR` | idem | idem | [ ] |
| Eliminar | `INV_PRODUCTOS_ELIMINAR` | idem | idem | [ ] |
| Importar masivo | `INV_PRODUCTOS_IMPORTAR` | idem | idem | [ ] |
| Precios — editar costos | `INV_PRODUCTOS_COSTO_EDITAR` | idem | idem | [ ] |

**Pestaña: Stock** (`INV_STOCK`)
- [ ] Ver / Ajustar / Exportar.

**Pestaña: Ajustes / Movimientos / Transferencias**
- [ ] Cada uno: Ver / Registrar / Anular.

**Pestaña: Categorías / Marcas / Atributos / Lotes / Presentaciones / Depósitos**
- [ ] Cada uno: Ver / Gestionar.

**Pestaña: Inventario Físico** (`INV_CONTEO_FISICO`)
- [ ] Ver / Iniciar / Cerrar / Anular.

**Pestaña: Ofertas** (`INV_OFERTAS`)
- [ ] Ver / Gestionar.

**Validación de cierre**: idem 6.8.1.

---

#### 6.8.7 — RRHH

Controllers backend: ~20 (el más grande) · Pantallas frontend: ~17 · Submódulos: 17.

**Seed**
- [ ] Submódulos completos: RRHH_EMPLEADOS, RRHH_LEGAJOS, RRHH_CONCEPTOS, RRHH_PARAMETROS, RRHH_LIQUIDACIONES, RRHH_ANTICIPOS, RRHH_PRESTAMOS, RRHH_VACACIONES, RRHH_DESVINCULACIONES, RRHH_NOVEDADES, RRHH_PRESENTISMO, RRHH_MARCACIONES, RRHH_PERMISOS, RRHH_TOLERANCIAS, RRHH_TURNOS, RRHH_RELOJES, RRHH_IPS, RRHH_ACREDITACIONES, RRHH_PLANILLAS, RRHH_FORMATOS_BANCARIOS, RRHH_REPORTES_IPS, RRHH_DASHBOARD, RRHH_CONSULTA, RRHH_CATALOGOS.

**Pestaña: Empleados / Legajos** (`RRHH_EMPLEADOS`, `RRHH_LEGAJOS`)
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver | `RRHH_EMPLEADOS_VER` | `empleados.controller.ts` | `Empleados*.jsx` | [ ] |
| Crear | `RRHH_EMPLEADOS_CREAR` | idem | idem | [ ] |
| Editar | `RRHH_EMPLEADOS_EDITAR` | idem | idem | [ ] |
| Ver legajos | `RRHH_LEGAJOS_VER` | `legajos-*.controller.ts` (4) | `Legajos*.jsx` | [ ] |
| Gestionar legajos | `RRHH_LEGAJOS_GESTIONAR` | idem | idem | [ ] |

**Pestaña: Conceptos / Parámetros / Catálogos**
- [ ] Cada uno: Ver / Gestionar.

**Pestaña: Liquidaciones** (`RRHH_LIQUIDACIONES`)
- [ ] Ver / Procesar / Cerrar / Reabrir / Exportar.

**Pestaña: Anticipos / Préstamos / Vacaciones / Desvinculaciones / Novedades**
- [ ] Cada uno: Ver / Registrar (o Gestionar) / Aprobar donde aplique.

**Pestaña: Marcaciones / Presentismo / Permisos / Tolerancias / Turnos / Relojes**
- [ ] Marcaciones: Ver / Registrar.
- [ ] Presentismo: Ver.
- [ ] Permisos: Ver / Aprobar.
- [ ] Tolerancias / Turnos / Relojes: Gestionar.

**Pestaña: IPS / Acreditaciones / Planillas / Formatos Bancarios / Reportes IPS**
- [ ] IPS: Ver.
- [ ] Acreditaciones: Generar.
- [ ] Planillas: Ver / Emitir.
- [ ] Formatos: Gestionar.
- [ ] Reportes IPS: Ver.

**Pestaña: Dashboard / Consulta**
- [ ] Dashboard: Ver.
- [ ] Consulta: Ver.

**Validación de cierre**: idem 6.8.1 + verificar que cobradores y vendedores también puedan consultar su legajo si tiene `RRHH_CONSULTA_VER`.

---

#### 6.8.8 — ADMINISTRACION / CONFIGURACION / SEGURIDAD

Controllers backend: ~15 · Pantallas: dispersas (Configuración + propias) · Submódulos: 15+.

**Seed**
- [ ] ADM_EMPRESA, ADM_EMPRESAS, ADM_SUCURSALES, ADM_USUARIOS, ADM_PERFILES, ADM_MONEDAS, ADM_METODOS_PAGO, ADM_CLIENTES, ADM_PROVEEDORES, ADM_TRANSPORTISTAS, ADM_CHOFERES, ADM_VEHICULOS, ADM_AGENTES_TRANSPORTE, ADM_SUSCRIPCION, ADM_AUDITORIA, SEG_USUARIOS, SEG_PERFILES, SEG_PRIVILEGIOS, SEG_MODULOS, SEG_AUDITORIA, SEG_2FA.

**Empresa / Sucursales / Comisiones**
- [ ] Empresa: Ver / Configurar.
- [ ] Sucursales: Ver / Gestionar / Asignar.
- [ ] Comisiones: Ver / Gestionar.

**Usuarios / Perfiles / Roles** (`SEG_USUARIOS`, `SEG_PERFILES`)
| Acción | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Ver usuarios | `SEG_USUARIOS_VER` | `users.controller.ts` | `Usuarios*.jsx` | [ ] |
| Crear/Editar usuario | `SEG_USUARIOS_GESTIONAR` | idem | idem | [ ] |
| Gestionar empresas del usuario | `SEG_USUARIOS_EMPRESA_GESTIONAR` | `user-empresas.controller.ts` | idem | [ ] |
| Ver perfiles | `SEG_PERFILES_VER` | `perfiles.controller.ts` | `SeguridadPerfiles.jsx` ✅ | [ ] |
| Crear/Clonar/Editar/Eliminar | `SEG_PERFILES_GESTIONAR` | idem | idem | [ ] |
| Ver privilegios catálogo | `SEG_PRIVILEGIOS_VER` | `privilegios.controller.ts` | (interno) | [ ] |
| Ver módulos catálogo | `SEG_MODULOS_VER` | `modulos.controller.ts` | (interno) | [ ] |
| Ver auditoría | `SEG_AUDITORIA_VER` | `audit.controller.ts` | `AuditLog*.jsx` | [ ] |

**Contactos (Clientes / Proveedores / Transportistas / Choferes / Vehículos / Agentes)**
- [ ] Cada uno: Ver / Crear / Editar / Eliminar.

**Monedas / Métodos de Pago / Condiciones de Pago / Referenciales**
- [ ] Cada uno: Ver / Gestionar.

**Suscripción / Plan-limits**
- [ ] Ver suscripción / Gestionar suscripción / Ver límites.

**Validación de cierre**: idem 6.8.1.

---

#### 6.8.9 — REPORTES (transversal)

Submódulos: RPT_VENTAS, RPT_COMPRAS, RPT_RENTABILIDAD (addon), RPT_PRODUCTOS, RPT_POSICION_CAJA, RPT_FLUJO_CAJA, RPT_MOVIMIENTOS_CAJA, RPT_LIBRO_IVA_VENTAS, RPT_LIBRO_IVA_COMPRAS, RPT_LIQUIDACION_IVA, RPT_INV_MOVIMIENTOS, RPT_INV_NIVELES, RPT_INV_LOTES, RPT_DASHBOARD_IA (addon).

**Política** (decisión (b) de §6.0): la pantalla `Reportes` es un **launcher**; cada card requiere el privilegio del módulo natural (ej. Libro Diario → `CONT_REPORTES_VER`). Aquí sólo se listan los privilegios **propios** de Reportes (los que no caen bajo otro módulo).

| Card | Privilegio | Backend | Frontend | Done |
|---|---|---|---|---|
| Resumen de Ventas | `RPT_VENTAS_VER` | (vista, sin controller dedicado) | `Reportes*.jsx` card | [ ] |
| Productos / Rentabilidad | `RPT_PRODUCTOS_VER` / `RPT_RENTABILIDAD_VER` | `reportes-rentabilidad.controller.ts` | idem | [ ] |
| Posición / Flujo / Movimientos Caja | `RPT_POSICION_CAJA_VER`, etc. | (queries directas) | idem | [ ] |
| Libros IVA / Liquidación IVA | `RPT_LIBRO_IVA_VENTAS_VER`, etc. | `marangatu.controller.ts` | idem | [ ] |
| Stock / Niveles / Lotes | `RPT_INV_*_VER` | (queries) | idem | [ ] |
| Dashboard IA (addon) | `RPT_DASHBOARD_IA_VER`, `_CONFIGURAR` | `ai-dashboard/*` | `AIDashboard*.jsx` | [ ] |

**Validación de cierre**: el usuario sin acceso a un módulo no ve sus cards de reportes.

---

#### 6.8.10 — FISCAL / NOTIFICACIONES / SUSCRIPCION (módulos pequeños)

**FISCAL** (`marangatu`, `middleware-sifen`, `kude`, `numeraciones`, `punto-expediciones`):
- [ ] Submódulos: FISCAL_MARANGATU, FISCAL_SIFEN, FISCAL_NUMERACIONES, FISCAL_PUNTO_EXP.
- [ ] Privilegios: `_GENERAR`, `_GESTIONAR`, `_CONFIGURAR` según pestaña.

**NOTIFICACIONES** (`notificaciones`, `mail`, `twilio`):
- [ ] Submódulos: NOTIF_INBOX, NOTIF_EMAIL, NOTIF_WHATSAPP (addon).
- [ ] Privilegios: `_VER`, `_GESTIONAR`, `_ENVIAR`.

**SUSCRIPCION** (`plan-limits`, `suscripciones`, `planes`):
- [ ] `planes.controller.ts` queda reservado a super-admin Novasis (no rol cliente).
- [ ] `suscripciones.controller.ts`: `SUSC_VER`, `SUSC_GESTIONAR`.
- [ ] `plan-limits.controller.ts`: `SUSC_LIMITES_VER`.

---

#### 6.8.11 — Resumen global de avance

| Módulo | Submódulos seed | Backend (controllers refactor) | Frontend (pantallas refactor) | Cierre |
|---|---|---|---|---|
| Contabilidad     | 8/8 ✅ | 8/8 ✅ | 7/7 ✅ | [x] |
| Tesorería        | 14/14 ✅ | 7/7 ✅ | 3/3 ✅ | [x] |
| Cobros           | 16/16 ✅ | 10/10 ✅ | 5/5 ✅ | [x] |
| Importaciones    | 5/5 ✅ | 1/1 ✅ | 5/5 ✅ | [x] |
| Ventas / POS     | 8/12 ✅ (POS+Fact+NC+Pres+Ped+OV+NR+May) | 7/10 ✅ | 15/15 ✅ | [x] |
| Inventario       | 13/13 ✅ | 11/11 ✅ | 13/13 ✅ | [x] |
| RRHH             | 18/18 ✅ | 25/25 ✅ | 1/1 ✅ | [x] |
| Administración   | 7/7 ✅ | 6/6 ✅ | 3/3 ✅ | [x] |
| Reportes         | 4/4 ✅ | 1/1 ✅ | 0/0 (gating vía submódulo de origen) | [x] |
| Fiscal / Notif / Susc | 8/8 ✅ | 7/7 ✅ | 2/2 ✅ | [x] |

Actualizar esta tabla en cada PR. El "Cierre" se marca cuando el módulo completo pasó por la secuencia A→B→C→D, sus entradas salieron del `CODIGOS_LEGACY_MAP`, y un smoke manual cubrió las pantallas principales.

---

### Estimación de esfuerzo

Asumiendo que la auditoría 6.1 + tabla de mapeo 6.2 toman 1-2 días:

- **Codemod automático** cubre ~60%: ~1 día para correr + validar.
- **Refactor manual** del resto: ~80 archivos × 5 min/archivo promedio = ~7-10 horas reales, distribuidas en 3-5 días con testing.
- **Backend** (controllers): ~42 archivos × 3 min/archivo = ~3 horas, 1 día con testing.
- **Testing manual smoke** por módulo: ~1 día.

**Total estimado**: 6-9 días de trabajo enfocado para refactorizar todo el código existente, después de que las Fases 0-5 estén implementadas.

### Riesgos y mitigaciones

| Riesgo | Mitigación |
|---|---|
| Alguien usa un código que no estaba en la auditoría inicial (encontrado solo en runtime) | El guard loguea warning con stack — monitorear logs durante 2 semanas post-deploy |
| Codemod renombra un string que no era un privilegio (false positive) | Revisar diff antes de commitear; los strings de privilegios siguen un patrón reconocible (`^[A-Z_]+$`) |
| Perfiles existentes pierden privilegios después de la migración (decisión 6: cambio en frío) | Botón "Cargar perfiles sugeridos" + documentación clara para que el cliente sepa que debe re-asignar |
| Algún hardcode oculto en SQL crudo / `$queryRaw` | Auditoría de `$queryRaw` y RPC functions de Postgres como paso adicional |

### Checklist de salida

- [ ] Auditoría 6.1 ejecutada y archivos `/tmp/codigos-*.txt` revisados
- [ ] `CODIGOS_LEGACY_MAP` y `MODULOS_LEGACY_MAP` definidos
- [ ] Guards con resolución de alias y logging activos
- [ ] Codemod corrido para los cambios obvios
- [ ] Cada módulo (lista de 9) tiene PR propio con testing manual
- [ ] Smoke E2E (si existe) verde
- [ ] `CODIGOS_LEGACY_MAP` queda vacío
- [ ] Archivo de alias y lógica del guard eliminados
- [ ] 0 warnings de `[permisos-legacy]` en logs por 1 semana

---

## Fase 7 — Migración y corte

1. **Backup** completo de `perfiles`, `perfiles_privilegios`, `privilegios`, `usuario_perfiles`.
2. Deploy backend con la migración Prisma (que trunca `perfiles_privilegios` y `privilegios`).
3. Correr seed maestro (`POST /seguridad/seed-maestro` desde super-admin).
4. Para cada empresa: opcionalmente correr "Cargar perfiles sugeridos".
5. Avisar a clientes existentes que sus perfiles quedaron vacíos y deben reasignar (con guía).
6. Deploy frontend con la nueva UI.

---

## Archivos críticos

### Backend nuevos
```
prisma/migrations/<ts>_refactor_seguridad/migration.sql
src/seguridad/seguridad.module.ts
src/seguridad/modulos/*.{controller,service}.ts
src/seguridad/submodulos/*.{controller,service}.ts
src/seguridad/privilegios/*.{controller,service}.ts
src/seguridad/perfiles/*.{controller,service}.ts
src/seguridad/seeds/{modulos,submodulos,privilegios,perfiles-sistema}.seed.ts
src/seguridad/seeds/seed-orchestrator.service.ts
```

### Backend modificar
```
prisma/schema.prisma                              (modelos modulos, privilegios + submodulos, suscripcion_submodulos)
src/auth/guards/permission.guard.ts               (query por submódulo)
src/suscripciones/suscripciones.service.ts        (chequeo de submódulos addon)
src/modulos/                                       (deprecar o redirigir al nuevo módulo seguridad)
src/privilegios/                                   (idem)
src/perfiles/                                      (idem)
```

### Frontend nuevos
```
src/pages/SeguridadPerfiles.jsx
src/components/seguridad/PerfilEditorDialog.jsx
src/components/seguridad/PerfilEditorSidebar.jsx
src/components/seguridad/PerfilEditorPanel.jsx
src/components/seguridad/SubmoduloAddonBadge.jsx
src/api/seguridad.service.js
src/tanstack/SeguridadStack.jsx
```

### Frontend reemplazar/deprecar
```
La pantalla actual de "Editar Perfil"                             (reescribir entera)
La pantalla actual de "Editar Plan" (Gestión de Planes)           (reescribir, ver Fase 4)
src/api/perfiles.service.js / privilegios.service.js              (deprecar a favor de seguridad.service.js)
```

---

## Verificación end-to-end

1. Migración Prisma corre limpia, `submodulos` poblado con ~60 submódulos.
2. `privilegios` reseedeado con ~250-400 privilegios, todos con `submodulo_id NOT NULL`.
3. Super-admin abre Administración → Perfiles → ve lista vacía + botón "Cargar sugeridos".
4. Click en "Cargar sugeridos" → aparecen 8 perfiles del sistema con sus privilegios.
5. Click en "Clonar" sobre "Cajero" → crea "Copia de Cajero" editable.
6. Editor 2-columnas: sidebar muestra módulos con contadores correctos; panel derecho muestra solo privilegios del módulo seleccionado.
7. Búsqueda "anular" filtra ambos lados.
8. Submódulo addon no contratado → checkboxes deshabilitados + badge.
9. Asignar perfil "Cajero" a un usuario → al loguear y entrar al POS funciona; al intentar abrir Contabilidad recibe 403.
10. Guard backend `@RequirePermission('VENTAS','POS_VENTA_CREAR')` sigue funcionando sin cambios en los controllers existentes.

---

## Pendientes (v1.x — post merge inicial)

### Perfiles sugeridos sensibles al plan/suscripción

Hoy `PERFILES_SISTEMA_SEED` (`src/seguridad/seeds/seguridad.seed-data.ts`) materializa los selectores contra **todos** los privilegios `is_sistema=true` activos en BD, sin filtrar por la suscripción de la empresa. Resultado: al hacer "Cargar perfiles sugeridos" en una empresa con un plan acotado, los perfiles se crean con privilegios de módulos que la empresa no tiene contratados (quedan inertes pero ensucian la UI).

**A futuro:** filtrar la expansión de selectores por los módulos/submódulos activos en la suscripción de la empresa que dispara la carga.

- Resolver `empresa_id` actual → `suscripcion` vigente → set de `modulo_id` + `submodulo_id` permitidos.
- Antes de insertar `perfiles_privilegios`, descartar todo privilegio cuyo `submodulo.modulo_id` no esté en ese set (o cuyo submódulo addon no esté contratado).
- Mantener los perfiles aunque queden con 0 privilegios para que aparezcan visibles si después se amplía el plan (alternativa: omitirlos enteros — decidir al implementar).
- Considerar exponer en UI un aviso "este perfil omitió X privilegios porque tu plan no los incluye".

Tocar: `SeguridadService.cargarPerfilesSugeridos()` (o equivalente), seed de selectores, y posiblemente el contrato del endpoint para recibir `empresa_id` explícito si todavía no llega.

---

## Iteración 2026-06-02 — ajustes post-Fase 3

Resumen consolidado de cambios aplicados en la sesión de hoy (incrementales sobre la Fase 3 ya completa). Sirve de base para continuar Fases 6.8.4+.

### Catálogo de privilegios — alineación con BD

La migración `privilegios_transporte_vendedor_finanzas.sql` ya estaba aplicada en BD y divergía del seed TS. Como BD es la fuente de runtime, se alineó **el seed al estado real**:

| Submódulo | Códigos antes (seed) | Códigos finales (BD + seed) |
|---|---|---|
| COB_ASIGNACION | `COB_ASG_ASIGNACION_ASIGNAR / _QUITAR` | `COB_ASG_ASIGNACION_VER / _CREAR / _ELIMINAR` |
| COB_SALDOS_FAVOR | `COB_SDF_SALDO_FAVOR_VER / _APLICAR / _EXPORTAR` | `COB_SF_SALDO_VER / _APLICAR / _AJUSTAR` |
| COB_HOJA_RUTA | `COB_HRT_HOJA_RUTA_VER / _EMITIR / _IMPRIMIR` | `COB_HR_HOJA_RUTA_VER / _GENERAR / _CERRAR / _IMPRIMIR` |
| COMP_ORDEN_PAGO | `CMP_OP_ORDEN_PAGO_VER / _EMITIR / _APROBAR / _ANULAR / _IMPRIMIR` | `CMP_OP_ORDEN_PAGO_VER / _CREAR / _APROBAR / _PAGAR / _ANULAR` |
| COB_COBRADORES | `COB_CBR_PANEL_VER` (panel) | `COB_CBR_PANEL_COBRADOR_VER` |

Controllers y componentes frontend afectados se ajustaron al nuevo nomenclátor (8 archivos para `COB_CBR_PANEL_COBRADOR_VER`, 5 para `COB_ASG_ASIGNACION_CREAR`, etc.).

### Submódulos faltantes agregados al seed

Existían solo en migración SQL. Agregados a `seguridad.seed-data.ts` para mantener consistencia bidireccional:

- VENTAS → `VEN_TRANSPORTISTAS` (CRUD `VEN_TRP_TRANSPORTISTA_*`)
- VENTAS → `VEN_CHOFERES` (CRUD `VEN_CHO_CHOFER_*`)
- VENTAS → `VEN_VEHICULOS` (CRUD `VEN_VEH_VEHICULO_*`)
- VENTAS → `VEN_AGENTES_TRANSPORTE` (CRUD `VEN_AGT_AGENTE_*`)
- VENTAS → `VENTAS_VENDEDOR_COBRADOR` (`VEN_VEND_VENDEDOR_CREAR/EDITAR/ELIMINAR`)

### Reclasificación: Solicitudes de Crédito → VENTAS

Estaba bajo COBRANZAS; conceptualmente es una operación de venta (un cliente solicita crédito en una casa comercial y la vendedora la carga). Movido a VENTAS:

- Backend: `solicitudes-credito.controller.ts` re-decorado con `@RequireModule('VENTAS')` + privilegios `VEN_SOL_SOLICITUD_VER / _CREAR / _EDITAR / _ELIMINAR / _APROBAR`.
- Frontend: `SolicitudesCredito.jsx`, `SolicitudesCreditoTab.jsx`, `SolicitudCreditoDetalleDialog.jsx`, `POSAdminTemplate.jsx` migrados al módulo VENTAS.
- Seed: nuevo submódulo `VENTAS_SOLICITUDES_CREDITO` y selectores agregados al perfil Vendedor.
- Sección 6.8.3 (Cobranzas) actualizada; documentación movida a sección 6.8.5 (VENTAS).

### ProtectedRoute / Sidebar — soporte multi-módulo (OR)

Las pantallas tipo dashboard cross-módulo (Finanzas) no encajan en un único `modulo`. Se extendieron tres componentes para aceptar `modulos: string[]`:

- `src/hooks/ProtectedRoute.jsx` — nueva prop `modulos`. Si el usuario tiene **cualquiera** de los módulos listados (plan + permiso), accede; cada pestaña interna gate por su privilegio real.
- `src/components/organismos/sidebar/Sidebar.jsx` y `MenuMovil.jsx` — `allowItem()` reconoce `item.modulos: []` con semántica OR.
- `src/utils/dataEstatica.jsx` — entrada "Finanzas" usa `modulos: ["TESORERIA", "COBRANZAS", "COMPRAS"]`.
- `src/routers/routes.jsx` — ruta `/finanzas` usa `modulos={["TESORERIA","COBRANZAS","COMPRAS"]}`.

Compatibilidad: `modulo` (string) sigue funcionando para el resto del sidebar; `modulos` (array) toma precedencia cuando está presente.

### ProtectedRoute — Capa 3 (privilegio específico)

`ProtectedRoute` ahora acepta `permission` opcional además de `modulo`. Tres capas de gating:
1. Capa 1 — `hasModulePlan(modulo)`: módulo en plan (holdings bypasean).
2. Capa 2 — `hasModule(modulo)`: usuario tiene ≥1 privilegio del módulo.
3. Capa 3 (opcional) — `hasPermission(modulo, permission)`: privilegio específico.

Rutas migradas a Capa 3:
- `/cobros` → COBRANZAS + `COB_REC_RECIBO_VER`
- `/cuentas-cobrar` → COBRANZAS + `COB_CC_CUENTA_COBRAR_VER`
- `/cobranzas/config-mora` → COBRANZAS + `COB_CFM_CONFIG_VER`
- `/cobranzas/panel-cobrador` → COBRANZAS + `COB_CBR_PANEL_COBRADOR_VER`
- `/reportes/cobranzas/cobrador` → COBRANZAS + `COB_REP_REPORTE_VER`
- `/reportes/cobranzas/rendiciones` → COBRANZAS + `COB_RND_RENDICION_VER`
- `/solicitudes-credito` → VENTAS + `VEN_SOL_SOLICITUD_VER`
- `/solicitudes-credito/nueva` → VENTAS + `VEN_SOL_SOLICITUD_CREAR`

### Sidebar — `permission` opcional en items

`Sidebar.jsx` / `MenuMovil.jsx` ahora también filtran por `item.permission`. Items afectados:
- Créditos → VENTAS + `VEN_SOL_SOLICITUD_VER`
- Cobros → COBRANZAS + `COB_REC_RECIBO_VER`
- Panel Cobrador → COBRANZAS + `COB_CBR_PANEL_COBRADOR_VER`

### Gate UI de botones en RecibosPanel / RecibosMultiPanel

Backend ya validaba `COB_REC_RECIBO_EDITAR` y `COB_REC_RECIBO_ANULAR`, pero la UI mostraba los botones igual y devolvía 403 al click. Ahora:

- `RecibosPanel.jsx` (drawer detalle + menú de acciones del listado): botones "Modificar fecha" y "Fecha" gateados por `COB_REC_RECIBO_EDITAR`; botones "Anular recibo" y "Anular" gateados por `COB_REC_RECIBO_ANULAR`.
- `RecibosMultiPanel.jsx`: migrado de `usePermission("CUENTAS_COBRAR")` (legacy) a `usePermission("COBRANZAS")`; códigos `COB_REC_CREAR` → `COB_REC_RECIBO_CREAR` y `COB_REC_ANULAR` → `COB_REC_RECIBO_ANULAR`.

### Sesión activa — opt-in por permiso

`useSesionActivaQuery` polleaba `/tesoreria/caja/activa` cada 30s desde `CobrosTemplate`, `CobrosTemplateV2` y `RecibosPanel`, generando un loop de 403 para cobradores sin `TES_CAJ_CAJA_VER`. Cambios:

- Hook acepta `options` (incluido `enabled`) y `retry: false`.
- Los tres callers pasan `enabled: canTesoreria("TES_CAJ_CAJA_VER")`. Si el usuario no tiene el permiso, la query nunca se monta.

### Panel Cobrador — búsqueda libre (backend + frontend)

El input de búsqueda de `PanelCobrador.jsx` filtraba localmente sobre cuotas ya cargadas (con filtros de fecha/estado aplicados), por lo que no encontraba clientes fuera del rango. Ahora:

**Backend** (`panel-cobrador.controller.ts` → `GET /panel-cobrador/cuotas`):
- Nuevo query param `search`.
- Cuando viene, **ignora** filtros de fecha, estado y asignación (el cobrador busca en toda la empresa, no solo en sus asignaciones).
- Match `contains` insensitive sobre: `factura_cab.dnumdoc`, `clientes.nombre_fantasia`, `personas.razon_social`, `personas.ruc`, `personas.nro_documento`.
- Early return por "usuario sin registro en `vendedores_cobradores`" solo aplica cuando no hay `search` (permite que usuarios con permiso pero sin ficha de cobrador puedan buscar).
- Campo `cobrador` del response es `null` cuando no hay ficha (sin null-ref).

**Frontend** (`PanelCobrador.jsx`):
- Debounce de 400ms (`busquedaDebounced`).
- Cuando hay término, `cargarCuotas` envía `search=...` y omite `estado` / `fecha_*`.
- Eliminado el filtro local sobre `cuotas` (ya viene filtrado del backend).

### Pendiente / próximos pasos

- **Fase 6.8.4 IMPORTACIONES**, **6.8.5 VENTAS/POS**, **6.8.6 INVENTARIO**, **6.8.7 RRHH**, **6.8.8 ADMIN** — refactor a 2-niveles (mismo patrón ya aplicado a TESORERIA, COBRANZAS, COMPRAS).
- **Tesorería Paso D — validación runtime** (task #129).
- Verificar selectores `VENTAS_NOTA_REMISION.*` y `VENTAS_ORDEN_VENTA.*` en perfil Vendedor.
- Considerar mover `vendedores_cobradores`: hoy un usuario sin ficha no es cobrador funcional. Decisión pendiente: ¿auto-crear ficha al asignar perfil Cobrador, o seguir requiriendo alta explícita?

---

## Iteración 2026-06-03 — Fase 4 cerrada + AI Dashboard 2-niveles

Sesión orientada a cerrar la gestión por empresa de submódulos/addons y a desacoplar Dashboard normal vs Dashboard IA.

### Cambios de backend

- `suscripciones.service.ts`:
  - `getSubmodulosIncluidosSuscripcionHolding` ahora devuelve **todo el catálogo** de módulos activos (no solo los contratados) para que el dialog admin pueda mostrar submódulos de módulos que la empresa esté por agregar como "Adicional".
  - `getSubmodulosActivos` aplica exclusiones por empresa vía `suscripcion_submodulos.active=false` para no-addon (override per-empresa).
  - `updateAddonsSuscripcionHolding`: fix Prisma — `precio` es `Decimal @default(0)` non-nullable; cambio `null` → `0` cuando no se provee precio_override.
  - `changePlan` recibe `fechaFinOverride`, `costoMensualOverride`, `diaCobro` y los aplica a la nueva suscripción creada.
- `empresas.service.ts` + `empresas.controller.ts`: propagan los nuevos overrides al service de suscripciones.
- `dto/change-empresa-plan.dto.ts`: agrega `fecha_fin?`, `costo_mensual?`, `dia_cobro?` (con validaciones ISO8601 / numéricas / 1-31).
- Seed: nuevo submódulo `REP_DASHBOARD` (no-addon) bajo REPORTES con privilegio `REP_DSH_DASHBOARD_VER` — permite gating del dashboard normal de la misma forma que `REP_DASHBOARD_IA` gobierna el Dashboard IA.

### Refactor AI Dashboard a 2-niveles (problema observado)

Empresa con plan **PLAN-RRHH** veía contexto de COBRANZAS y VENTAS en Dashboard IA. Causa: el plan tenía módulos extra (`ADMINISTRACION`, `COBRANZAS`) además de RRHH. Como además el gating del Dashboard IA mapeaba dominios solo por módulo raíz, se mezclaban.

Cambios:
- `AI_DOMAINS` ahora declara `{ modulos: [...], submodulos: [...] }` por dominio.
- `codigoToDomains` resuelve dominios visibles cruzando módulos contratados + submódulos activos por empresa.
- Configuración de Dashboard IA también respeta la jerarquía (no muestra dominios cuyo submódulo está excluido).

### Cambios de frontend

- `CambiarPlanDialog.jsx`: nueva sección "Condiciones de la nueva suscripción (opcional)" con grilla 2×2 (`fecha_inicio`, `fecha_fin`, `costo_mensual`, `dia_cobro`). Útil para reconstruir limpia una suscripción cuando se cambia de plan a otro con submódulos distintos (decisión: descartamos propagación automática de addons — historial de pagos puede regenerarse en esta etapa, hay pocos clientes).
- `GestionModulosDialog.jsx`:
  - Save secuencial (primero módulos, después addons + submódulos) para evitar race con la validación backend que exige `suscripcion_modulos.activo=true` antes de aceptar submódulos.
  - Filtra payload de submódulos a los módulos efectivamente activos tras el primer paso (evita 400 al guardar submódulos de módulos que se acaban de quitar).
- `routes.jsx`: `/dashboard` protegido por `modulo="REPORTES" submodulo="REP_DASHBOARD"` con `fallback="/ai-dashboard"`.
- `dataEstatica.jsx`: item Dashboard del sidebar agrega `submodulo: "REP_DASHBOARD"`.
- `api/suscripciones.service.js`: `cambiarPlanEmpresa` envía los nuevos campos opcionales.

### Decisión descartada

Implementé y luego revertí `planes.service.propagateAddonsToSuscripciones`. El usuario prefirió: editar plan + cambiar plan de la empresa con override de fechas/costo/día de cobro, aceptando regenerar la suscripción (pocos clientes en producción todavía).

---

## Fuera de alcance v1

- Overrides de privilegios por usuario (descartado en decisión 4.3).
- Soporte de 3+ niveles de jerarquía (descartado en decisión 1).
- Multi-tenant compartido de perfiles (los perfiles siguen siendo por empresa).
- Auditoría detallada de cambios en perfiles (queda para v2 — el `audit.log` actual sigue grabando los CRUD).
- UI de gestión de suscripciones de addons (queda para módulo de billing).
- Permisos a nivel de fila / data-level (ej: "el cobrador X solo ve clientes de su ruta"). Eso vive en la lógica de negocio, no en este módulo.

---

## Relación con módulos existentes

- **No tocar** la lógica de los módulos funcionales (`facturas`, `cobros`, `contabilidad`, etc.); solo cambia el guard de permisos, que mantiene su API pública (`@RequirePermission(modulo, privilegio)`).
- **Deprecar** los módulos `src/modulos/`, `src/privilegios/`, `src/perfiles/` actuales en favor del nuevo `src/seguridad/`. Mantener temporalmente para no romper imports, marcar `@deprecated` en código.
- El módulo de suscripciones consume la jerarquía: `suscripcion_modulos` da acceso al módulo raíz y submódulos no-addon; `suscripcion_submodulos` da acceso a submódulos addon.
