# Módulo de Seguridad — API Reference

Base URL: `/api`  
Todos los endpoints requieren `Authorization: Bearer <token>` y `X-Empresa-Id: <id>`.  
Permiso requerido indicado en cada endpoint — el middleware `permiso:` valida que el perfil del usuario lo tenga asignado.

---

## Convenciones generales

### IDs en base64
Los IDs de registros se transmiten en **base64** entre frontend y backend. Siempre codifica antes de enviar y decodifica al recibir.

```js
// Codificar
btoa(String(id))          // 1 → "MQ=="

// Decodificar
parseInt(atob("MQ=="), 10) // "MQ==" → 1
```

### Respuesta paginada (listar)

Todos los endpoints `/listar` devuelven esta estructura:

```json
{
  "success":          true,
  "code":             200,
  "data":             [...],
  "lastPage":         5,
  "totalData":        48,
  "totalPage":        10,
  "currentPage":      1,
  "recordsTotal":     48,
  "recordsFiltered":  48,
  "draw":             1
}
```

### Parámetros comunes de paginación

| Parámetro | Tipo | Default | Descripción |
|---|---|---|---|
| `perPage` | int | `10` | Registros por página |
| `searchQuery` | string | — | Búsqueda global en todos los campos |
| `sort_field` | string | PK del modelo | Campo de ordenamiento |
| `sort_direction` | string | `"asc"` | `"asc"` \| `"desc"` |
| `typeData` | string | — | `"all"` para obtener todos sin paginar |

### Exportación (Excel / PDF)

Todos los módulos tienen los mismos endpoints de exportación:

```
POST /api/seguridad/{modulo}/exportar-excel
POST /api/seguridad/{modulo}/exportar-pdf
```

Aceptan los mismos filtros que `/listar`. La respuesta es el archivo binario (`blob`).  
Para PDF puedes agregar `"output_mode": "inline"` para preview en lugar de descarga.

---

## 1. Usuario

### Endpoints

| Método | URL | Permiso | Descripción |
|---|---|---|---|
| `POST` | `/api/seguridad/usuario/listar` | `seguridad.usuario.listar` | Lista paginada |
| `GET` | `/api/seguridad/usuario/detalle/{id}` | `seguridad.usuario.detalle` | Detalle de un usuario |
| `GET` | `/api/seguridad/usuario/cargar-datos-edicion/{id}` | `seguridad.usuario.cargarDatosEdicion` | Datos para el form de edición |
| `POST` | `/api/seguridad/usuario/guardar` | `seguridad.usuario.guardar` | Crear o actualizar |
| `POST` | `/api/seguridad/usuario/cambiar-estado` | `seguridad.usuario.cambiarEstado` | Activar / Eliminar |
| `POST` | `/api/seguridad/usuario/datos-perfiles-usuario` | `seguridad.usuario.datosPerfilesUsuario` | Perfiles disponibles con flag asignado |
| `POST` | `/api/seguridad/usuario/datos-empresas-usuario` | `seguridad.usuario.datosEmpresasUsuario` | Empresas disponibles con flag asignado |
| `POST` | `/api/seguridad/usuario/guardar-perfiles-usuario` | `seguridad.usuario.guardarPerfilesUsuario` | Sincronizar perfiles del usuario |
| `POST` | `/api/seguridad/usuario/exportar-excel` | `seguridad.usuario.exportarExcel` | Descarga Excel |
| `POST` | `/api/seguridad/usuario/exportar-pdf` | `seguridad.usuario.exportarPdf` | Descarga PDF |

---

### `POST /listar`

```json
{
  "perPage":        10,
  "searchQuery":    "admin",
  "usuarioEstado":  "Activo",
  "sort_field":     "usuarioAlias",
  "sort_direction": "asc"
}
```

---

### `POST /guardar` — Crear usuario

```json
{
  "usuarioAlias":                "jdoe",
  "usuarioPassword":             "Segura1!",
  "usuarioPasswordConfirmacion": "Segura1!",
  "usuarioNombre":               "John Doe",
  "usuarioEmail":                "jdoe@empresa.com",
  "idPerfil":                    1,
  "usuarioNotificacionWeb":      "Si",
  "usuarioNotificacionWhatsApp": "No",
  "usuarioNotificacionMail":     "Si",
  "usuarioWhatsApp":             null,
  "empresas":                    [1, 2]
}
```

### `POST /guardar` — Actualizar usuario

Agrega `idUsuario` en base64. La contraseña es opcional al editar.

```json
{
  "idUsuario":                   "MQ==",
  "usuarioAlias":                "jdoe",
  "usuarioNombre":               "John Doe Actualizado",
  "usuarioEmail":                "jdoe@empresa.com",
  "idPerfil":                    1,
  "usuarioNotificacionWeb":      "Si",
  "usuarioNotificacionWhatsApp": "Si",
  "usuarioNotificacionMail":     "Si",
  "usuarioWhatsApp":             "5215512345678",
  "empresas":                    [1]
}
```

| Campo | Regla |
|---|---|
| `usuarioAlias` | Requerido, único en BD |
| `usuarioPassword` | Requerido al crear, opcional al editar. Mín. 6 caracteres |
| `usuarioPasswordConfirmacion` | Requerido cuando se manda `usuarioPassword`, debe coincidir |
| `usuarioNombre` | Requerido |
| `usuarioEmail` | Único, requerido si `usuarioNotificacionMail = "Si"` |
| `idPerfil` | Requerido, debe existir en BD |
| `usuarioNotificacionWeb/WhatsApp/Mail` | `"Si"` \| `"No"` |
| `usuarioWhatsApp` | Requerido si `usuarioNotificacionWhatsApp = "Si"`. Solo dígitos, 8–15 chars. Formato E.164 sin `+`. Ej: `5215512345678` |
| `empresas` | Array de enteros de IDs de empresa. Puede estar vacío |

#### Respuesta `200`

```json
{
  "success": true,
  "code":    200,
  "message": "Usuario agregado correctamente",
  "data":    { "idUsuario": 5, "usuarioAlias": "jdoe", ... }
}
```

---

### `POST /cambiar-estado`

```json
{
  "idUsuario": "MQ==",
  "estado":    "Activo"
}
```

`estado`: `"Activo"` | `"Eliminado"`

---

### `POST /datos-perfiles-usuario` y `POST /datos-empresas-usuario`

Útiles para cargar los checkboxes del formulario. Omite `idUsuario` al crear.

```json
{ "idUsuario": "MQ==" }
```

#### Respuesta perfiles

```json
{
  "code":     200,
  "perfiles": [
    { "idPerfil": 1, "perfilAlias": "Administrador", "perfilAsignado": true },
    { "idPerfil": 2, "perfilAlias": "Operativo",     "perfilAsignado": false }
  ]
}
```

#### Respuesta empresas

```json
{
  "code":     200,
  "empresas": [
    { "idEmpresa": 1, "empresaNombre": "FSG", "empresaAsignada": true },
    { "idEmpresa": 2, "empresaNombre": "QSR", "empresaAsignada": false }
  ]
}
```

---

### `POST /guardar-perfiles-usuario`

Sincroniza (reemplaza) los perfiles asignados al usuario.

```json
{
  "idUsuario": "MQ==",
  "perfiles":  [1, 3]
}
```

---

## 2. Perfil

### Endpoints

| Método | URL | Permiso | Descripción |
|---|---|---|---|
| `POST` | `/api/seguridad/perfil/listar` | `seguridad.perfil.listar` | Lista paginada |
| `GET` | `/api/seguridad/perfil/detalle/{idPerfil}` | `seguridad.perfil.detalle` | Detalle |
| `GET` | `/api/seguridad/perfil/cargar-datos-edicion/{idPerfil}` | `seguridad.perfil.cargarDatosEdicion` | Datos para edición |
| `POST` | `/api/seguridad/perfil/guardar` | `seguridad.perfil.guardar` | Crear o actualizar |
| `POST` | `/api/seguridad/perfil/cambiar-estado` | `seguridad.perfil.cambiarEstado` | Activar / Desactivar |
| `GET` | `/api/seguridad/perfil/arbol-menus/{idPerfil}` | `seguridad.perfil.arbolMenus` | Árbol de menús con flag asignado |
| `POST` | `/api/seguridad/perfil/sincronizar-menus` | `seguridad.perfil.sincronizarMenus` | Guardar asignación de menús |
| `GET` | `/api/seguridad/perfil/rutas-por-modulo/{idPerfil}` | `seguridad.perfil.rutasPorModulo` | Rutas agrupadas con flag asignado |
| `POST` | `/api/seguridad/perfil/sincronizar-rutas` | `seguridad.perfil.sincronizarRutas` | Guardar asignación de rutas |
| `POST` | `/api/seguridad/perfil/exportar-excel` | `seguridad.perfil.exportarExcel` | Excel |
| `POST` | `/api/seguridad/perfil/exportar-pdf` | `seguridad.perfil.exportarPdf` | PDF |

---

### `POST /guardar`

```json
{
  "perfilAlias":       "Supervisor",
  "perfilDescripcion": "Perfil con acceso a reportes y operación",
  "perfilURLInicial":  "/inicio-admin"
}
```

Al editar, incluye `"idPerfil": "MQ=="`.

---

### `POST /cambiar-estado`

```json
{
  "idPerfil": "MQ==",
  "estado":   "Inactivo"
}
```

#### Respuesta `409` — Perfil en uso

Si el perfil tiene usuarios activos y `estado = "Inactivo"`, el servidor responde `409` con la cantidad de afectados. El frontend debe mostrar confirmación antes de forzar:

```json
{
  "success":         false,
  "code":            409,
  "codigo":          "ERR_PERFIL_EN_USO",
  "message":         "El perfil tiene 3 usuarios activos asignados.",
  "usuariosActivos": 3
}
```

Para forzar la desactivación, reenvía la petición con `"forzar": true`:

```json
{
  "idPerfil": "MQ==",
  "estado":   "Inactivo",
  "forzar":   true
}
```

---

### `GET /arbol-menus/{idPerfil}`

Devuelve todos los menús del sistema con el flag `asignado` para renderizar el árbol de checkboxes.

#### Respuesta

```json
{
  "success": true,
  "perfil":  { "idPerfil": 1, "perfilAlias": "Administrador" },
  "data": [
    {
      "idMenu":    1,
      "menuAlias": "Seguridad",
      "nivel":     0,
      "asignado":  true,
      "hijos": [
        { "idMenu": 5, "menuAlias": "Usuarios", "nivel": 1, "asignado": true, "hijos": [] }
      ]
    }
  ],
  "totales": { "menusTotales": 24, "menusAsignados": 12 }
}
```

---

### `POST /sincronizar-menus`

Reemplaza completamente los menús asignados al perfil.

```json
{
  "idPerfil": "MQ==",
  "menus":    [1, 5, 8, 12]
}
```

---

### `GET /rutas-por-modulo/{idPerfil}`

Rutas agrupadas por módulo, con flag `asignado`.

#### Respuesta

```json
{
  "success": true,
  "data": [
    {
      "modulo": "seguridad.usuario",
      "rutas": [
        { "idRuta": 10, "rutaAlias": "Listar usuarios", "asignado": true },
        { "idRuta": 11, "rutaAlias": "Guardar usuario",  "asignado": false }
      ]
    }
  ]
}
```

---

### `POST /sincronizar-rutas`

```json
{
  "idPerfil": "MQ==",
  "rutas":    [10, 11, 15]
}
```

---

## 3. Menú

### Endpoints

| Método | URL | Permiso | Descripción |
|---|---|---|---|
| `POST` | `/api/seguridad/menu/listar` | `seguridad.menu.listar` | Lista paginada |
| `GET` | `/api/seguridad/menu/detalle/{id}` | `seguridad.menu.detalle` | Detalle |
| `GET` | `/api/seguridad/menu/cargar-datos-edicion/{id}` | `seguridad.menu.cargarDatosEdicion` | Datos para edición |
| `POST` | `/api/seguridad/menu/guardar` | `seguridad.menu.guardar` | Crear o actualizar |
| `POST` | `/api/seguridad/menu/cambiar-estado` | `seguridad.menu.cambiarEstado` | Activar / Desactivar (cascada a hijos) |
| `POST` | `/api/seguridad/menu/activar-rama` | `seguridad.menu.activarRama` | Activar menú + todos sus ancestros |
| `POST` | `/api/seguridad/menu/datos-arbol` | `seguridad.menu.datosArbol` | Árbol plano para `<select>` de padre |
| `GET` | `/api/seguridad/menu/navbar` | _(sin permiso)_ | Menús del usuario autenticado para navbar |
| `POST` | `/api/seguridad/menu/exportar-excel` | `seguridad.menu.exportarExcel` | Excel |
| `POST` | `/api/seguridad/menu/exportar-pdf` | `seguridad.menu.exportarPdf` | PDF |

---

### `POST /guardar`

```json
{
  "menuAlias":    "Reportes",
  "menuIcono":    "fa-solid fa-chart-bar",
  "menuUrl":      "/reportes",
  "menuOrden":    5,
  "menuSuperior": null
}
```

Al editar, incluye `"idMenu": "MQ=="`.  
`menuSuperior`: ID del menú padre en base64, o `null` para menú raíz.

---

### `POST /cambiar-estado`

```json
{
  "idMenu": "MQ==",
  "estado": "Inactivo"
}
```

> Desactivar un menú padre **desactiva en cascada** todos sus hijos.  
> Si intentas **activar** un menú cuyo padre está inactivo, el servidor responde `409`:

```json
{
  "success": false,
  "code":    409,
  "codigo":  "ERR_PADRE_INACTIVO",
  "message": "El menú padre está inactivo. Activa primero el menú superior."
}
```

En ese caso muestra confirmación y llama a `/activar-rama`:

### `POST /activar-rama`

Activa el menú y todos sus ancestros inactivos en una sola operación.

```json
{ "idMenu": "Ng==" }
```

---

### `GET /navbar`

Devuelve el árbol de menús asignados al perfil del usuario autenticado. Úsalo para construir la navegación dinámica en Vue.

#### Respuesta

```json
{
  "success": true,
  "data": [
    {
      "idMenu":    1,
      "menuAlias": "Seguridad",
      "menuIcono": "fa-solid fa-shield-halved",
      "menuUrl":   null,
      "hijos": [
        {
          "idMenu":    5,
          "menuAlias": "Usuarios",
          "menuIcono": "fa-solid fa-users",
          "menuUrl":   "/seguridad/usuario",
          "hijos":     []
        }
      ]
    }
  ]
}
```

---

## 4. Ruta

Las rutas son los permisos granulares del sistema (`permiso:seguridad.usuario.listar`, etc.). Normalmente se administran desde el panel, no desde Vue directamente.

### Endpoints

| Método | URL | Permiso | Descripción |
|---|---|---|---|
| `POST` | `/api/seguridad/ruta/listar` | `seguridad.ruta.listar` | Lista paginada |
| `GET` | `/api/seguridad/ruta/detalle/{idRuta}` | `seguridad.ruta.detalle` | Detalle |
| `GET` | `/api/seguridad/ruta/cargar-datos-edicion/{idRuta}` | `seguridad.ruta.cargarDatosEdicion` | Datos para edición |
| `POST` | `/api/seguridad/ruta/guardar` | `seguridad.ruta.guardar` | Crear o actualizar |
| `POST` | `/api/seguridad/ruta/cambiar-estado` | `seguridad.ruta.cambiarEstado` | Activar / Desactivar |
| `GET` | `/api/seguridad/ruta/autocomplete` | `seguridad.ruta.autocomplete` | Rutas del sistema para autocompletar |
| `POST` | `/api/seguridad/ruta/importar` | `seguridad.ruta.importar` | Importar rutas desde `web.php` / `api.php` |
| `POST` | `/api/seguridad/ruta/validar` | `seguridad.ruta.validar` | Reconciliar BD vs archivos de rutas |
| `GET` | `/api/seguridad/ruta/ver-ruta/{idRuta}` | `seguridad.ruta.verRuta` | Snippet PHP de la ruta |
| `POST` | `/api/seguridad/ruta/exportar-excel` | `seguridad.ruta.exportarExcel` | Excel |
| `POST` | `/api/seguridad/ruta/exportar-pdf` | `seguridad.ruta.exportarPdf` | PDF |

---

### `POST /guardar`

```json
{
  "rutaAlias":   "Listar usuarios",
  "rutaNombre":  "seguridad.usuario.listar",
  "rutaEstado":  "Activo"
}
```

Al editar, incluye `"idRuta": "MQ=="`.

---

### `POST /cambiar-estado`

```json
{
  "idRuta": "MQ==",
  "estado": "Inactivo"
}
```

---

### `GET /autocomplete`

Devuelve las rutas registradas en `routes/web.php` y `routes/api.php` para asistir el alta manual. No requiere body.

---

### `POST /importar`

Importa masivamente las rutas del sistema a la BD, sincronizando las que no existan.

```json
{}
```

---

## 5. Manejo de errores comunes del módulo

| Status | Causa | Acción |
|---|---|---|
| `401` | Token expirado o inválido | Redirigir al login |
| `403` | Sin permiso para ese endpoint | Mostrar "Acceso denegado" |
| `404` | Registro no encontrado | Mostrar mensaje en pantalla |
| `409` | Conflicto de regla de negocio (`ERR_PERFIL_EN_USO`, `ERR_PADRE_INACTIVO`) | Leer `codigo` y mostrar confirmación |
| `422` | Validación fallida | Mostrar `errors` en el formulario |
| `500` | Error interno | Mostrar mensaje genérico, leer `message` para debug |
