# Módulo de Operación — API Reference

Base URL: `/api`  
Todos los endpoints requieren `Authorization: Bearer <token>` y el permiso correspondiente.  
Prefijo real: `/api/operacion/{modulo}/`

---

## Empresa activa — IMPORTANTE

Todos los modelos de este módulo filtran sus datos por la empresa con la que trabaja el usuario. El backend resuelve la empresa activa en este orden:

| Prioridad | Fuente | Cómo enviarlo |
|---|---|---|
| 1 | Sesión del navegador | Automático (Blade/web) |
| 2 | Header `X-Empresa-Id` | **Vue / axios** |
| 3 | Body/query `idEmpresaId` | **Postman / curl** |
| 4 | Primera empresa del usuario | Fallback automático |

### Vue / axios

Configura el header globalmente en tu instancia de axios:

```js
http.interceptors.request.use(config => {
    const idEmpresa = localStorage.getItem('idEmpresaActiva')
    if (idEmpresa) {
        config.headers['X-Empresa-Id'] = idEmpresa
    }
    return config
})
```

### Postman

**Opción A — Header:**
```
X-Empresa-Id: 2
```

**Opción B — Body JSON:**
```json
{
  "idEmpresaId": 2
}
```

**Opción C — Query string:**
```
POST /api/operacion/sector/listar?idEmpresaId=2
```

> El backend valida que el ID enviado pertenezca al usuario autenticado. Si mandas una empresa que no tienes asignada, cae al fallback (primera empresa del usuario).

---

## IDs en base64

Los IDs de registros viajan en base64 en URLs y bodies.

```js
btoa(String(id))           // 5 → "NQ=="
parseInt(atob("NQ=="), 10) // → 5
```

### IDs ya codificados en el `listar`

El endpoint `POST /listar` devuelve los IDs **ya codificados en base64** dentro del Resource.
Los clientes no deben aplicar `btoa()` sobre el ID recibido; úsalo directamente como `data-id`
o como parámetro de URL hacia `cargar-datos-edicion/{id}`.

```js
// ✅ Correcto — el id viene de row.idXxx del listar (ya es base64)
data-id="${row.idSelector}"

// ❌ Incorrecto — doble codificación
data-id="${btoa(row.idSelector)}"
```

El endpoint `GET /cargar-datos-edicion/{id}` devuelve el modelo crudo con el **entero** de BD.
Si necesitas reutilizar ese ID en el form hidden, sí debes codificarlo:

```js
// El id que viene de cargarDatosEdicion es entero → codificar antes de poner en el form
$('#form-idSelector').val(btoa(String(d.idSelector)));
```

| Fuente del ID | Tipo | Necesita `btoa()` |
|---|---|---|
| `listar` (Resource) | base64 | No |
| `cargar-datos-edicion` (modelo crudo) | entero | Sí |
| URL de detalle/edición | base64 | No |

---

## Respuesta paginada

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

---

## 1. Norma

Prefijo: `/api/operacion/norma/`

### Endpoints

| Método | URL | Permiso | Descripción |
|---|---|---|---|
| `POST` | `/api/operacion/norma/listar` | `operacion.norma.listar` | Lista paginada |
| `GET` | `/api/operacion/norma/detalle/{id}` | `operacion.norma.detalle` | Detalle |
| `GET` | `/api/operacion/norma/cargar-datos-edicion/{id}` | `operacion.norma.cargarDatosEdicion` | Datos para edición |
| `POST` | `/api/operacion/norma/guardar` | `operacion.norma.guardar` | Crear o actualizar |
| `POST` | `/api/operacion/norma/cambiar-estado` | `operacion.norma.cambiarEstado` | Activar / Eliminar |
| `GET` | `/api/operacion/norma/descargar-plantilla` | `operacion.norma.descargarPlantilla` | Plantilla Excel |
| `POST` | `/api/operacion/norma/guardar-archivo-temporal` | `operacion.norma.guardarArchivoTemporal` | Subir Excel |
| `POST` | `/api/operacion/norma/obtener-datos-archivo` | `operacion.norma.obtenerDatosArchivo` | Total filas |
| `POST` | `/api/operacion/norma/validar-chunk` | `operacion.norma.validarChunk` | Validar chunk |
| `POST` | `/api/operacion/norma/importar-chunk` | `operacion.norma.importarChunk` | Importar chunk |
| `POST` | `/api/operacion/norma/eliminar-archivo-temporal` | `operacion.norma.eliminarArchivoTemporal` | Limpiar temporal |
| `POST` | `/api/operacion/norma/exportar-excel` | `operacion.norma.exportarExcel` | Excel |
| `POST` | `/api/operacion/norma/exportar-pdf` | `operacion.norma.exportarPdf` | PDF |

---

### `POST /listar` — con empresa activa

**Vue:**
```js
// El interceptor agrega X-Empresa-Id automáticamente
const res = await http.post('/api/operacion/norma/listar', {
    perPage: 10,
    searchQuery: 'ISO',
    normaEstado: 'Activo',
})
```

**Postman:**
```json
{
  "idEmpresaId":    2,
  "perPage":        10,
  "searchQuery":    "ISO",
  "normaEstado":    "Activo",
  "sort_field":     "normaCodigo",
  "sort_direction": "asc"
}
```

---

### `POST /guardar` — Crear

```json
{
  "normaCodigo":                "ISO-9001",
  "normaAlias":                 "Sistemas de Gestión de Calidad",
  "normaDuracion":              3,
  "normaVersion":               "2015",
  "normaRequiereExpertoTecnico": "Si",
  "normaEsquema":               "Anexo SL"
}
```

### `POST /guardar` — Actualizar

Agrega `idNorma` en base64:

```json
{
  "idNorma":                    "MQ==",
  "normaCodigo":                "ISO-9001",
  "normaAlias":                 "Sistemas de Gestión de Calidad",
  "normaDuracion":              3,
  "normaVersion":               "2015",
  "normaRequiereExpertoTecnico": "No",
  "normaEsquema":               "Anexo SL"
}
```

| Campo | Regla |
|---|---|
| `normaCodigo` | Requerido |
| `normaAlias` | Requerido |
| `normaDuracion` | Opcional, entero (días/años según configuración) |
| `normaVersion` | Opcional |
| `normaRequiereExpertoTecnico` | `"Si"` \| `"No"` |
| `normaEsquema` | Opcional |

---

### `POST /cambiar-estado`

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

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

---

### Importación Excel (Norma)

Flujo igual al de Empresa (ver documentación empresa). Columnas de la plantilla:

| Columna plantilla | Campo BD |
|---|---|
| `Codigo` | `normaCodigo` |
| `Alias` | `normaAlias` |
| `Duracion` | `normaDuracion` |
| `Version` | `normaVersion` |
| `Requiere experto tecnico` | `normaRequiereExpertoTecnico` |
| `Esquema` | `normaEsquema` |

---

## 2. Auditor

Prefijo: `/api/operacion/auditor/`

> El número de identificación (`AUD-001`, `AUD-002`…) es **generado automáticamente** al crear. No se envía en el body ni se puede modificar después.

### Endpoints

| Método | URL | Permiso | Descripción |
|---|---|---|---|
| `POST` | `/api/operacion/auditor/listar` | `operacion.auditor.listar` | Lista paginada |
| `GET` | `/api/operacion/auditor/detalle/{id}` | `operacion.auditor.detalle` | Detalle |
| `GET` | `/api/operacion/auditor/cargar-datos-edicion/{id}` | `operacion.auditor.cargarDatosEdicion` | Datos para edición |
| `POST` | `/api/operacion/auditor/guardar` | `operacion.auditor.guardar` | Crear o actualizar |
| `POST` | `/api/operacion/auditor/cambiar-estado` | `operacion.auditor.cambiarEstado` | Activar / Inactivar |
| `POST` | `/api/operacion/auditor/exportar-excel` | `operacion.auditor.exportarExcel` | Excel |
| `POST` | `/api/operacion/auditor/exportar-pdf` | `operacion.auditor.exportarPdf` | PDF |

---

### `POST /listar`

**Postman:**
```json
{
  "idEmpresaId":    2,
  "perPage":        10,
  "searchQuery":    "García",
  "auditorTipo":    "Interno",
  "auditorEstado":  "Activo",
  "sort_field":     "auditorNombre",
  "sort_direction": "asc"
}
```

---

### `POST /guardar` — Crear

```json
{
  "auditorNombre":                   "Juan",
  "auditorApellidoPaterno":          "García",
  "auditorApellidoMaterno":          "López",
  "auditorEmail":                    "juan@empresa.com",
  "auditorTelefono":                 "5512345678",
  "auditorTipo":                     "Interno",
  "auditorFechaUltimaTestificacion": "2025-01-15"
}
```

### `POST /guardar` — Actualizar

```json
{
  "idAuditor":                       "MQ==",
  "auditorNombre":                   "Juan Carlos",
  "auditorApellidoPaterno":          "García",
  "auditorApellidoMaterno":          "López",
  "auditorEmail":                    "jcarlos@empresa.com",
  "auditorTelefono":                 "5512345679",
  "auditorTipo":                     "Externo",
  "auditorFechaUltimaTestificacion": "2025-06-01"
}
```

| Campo | Regla |
|---|---|
| `auditorNombre` | Requerido, máx. 150 chars |
| `auditorApellidoPaterno` | Opcional, máx. 100 chars |
| `auditorApellidoMaterno` | Opcional, máx. 100 chars |
| `auditorEmail` | Opcional, formato email, máx. 150 chars |
| `auditorTelefono` | Opcional, máx. 25 chars |
| `auditorTipo` | Requerido: `"Interno"` \| `"Externo"` |
| `auditorFechaUltimaTestificacion` | Opcional, formato `YYYY-MM-DD` |

---

### `POST /cambiar-estado`

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

`estado`: `"Activo"` | `"Inactivo"`

---

## 3. Sector

Prefijo: `/api/operacion/sector/`

> Catálogo de sectores IAF/NACE con soporte de jerarquía (sector padre). La PK se llama `idSelector` (no `idSector`).

### Endpoints

| Método | URL | Permiso | Descripción |
|---|---|---|---|
| `POST` | `/api/operacion/sector/listar` | `operacion.sector.listar` | Lista paginada |
| `GET` | `/api/operacion/sector/catalogo` | `operacion.sector.listar` | Sectores activos para `<select>` |
| `GET` | `/api/operacion/sector/detalle/{id}` | `operacion.sector.detalle` | Detalle con padre |
| `GET` | `/api/operacion/sector/cargar-datos-edicion/{id}` | `operacion.sector.cargarDatosEdicion` | Datos para edición con padre |
| `POST` | `/api/operacion/sector/guardar` | `operacion.sector.guardar` | Crear o actualizar |
| `POST` | `/api/operacion/sector/cambiar-estado` | `operacion.sector.cambiarEstado` | Activar / Inactivar |
| `POST` | `/api/operacion/sector/exportar-excel` | `operacion.sector.exportarExcel` | Excel |
| `POST` | `/api/operacion/sector/exportar-pdf` | `operacion.sector.exportarPdf` | PDF |

---

### `GET /catalogo` — Select de sectores padre

Devuelve todos los sectores activos de la empresa activa para poblar el `<select>` de "Sector Superior".

**Query params opcionales:**
- `excluir=5` — Excluye un ID del resultado (útil al editar: evita que el propio sector aparezca como opción de padre)

**Vue:**
```js
const res = await http.get('/api/operacion/sector/catalogo', {
    params: { excluir: sectorActualId }
})
```

**Postman:** `GET /api/operacion/sector/catalogo?excluir=5&idEmpresaId=2`

#### Respuesta

```json
{
  "success": true,
  "code":    200,
  "data": [
    { "idSelector": 1, "sectorCodigo": "A01", "sectorAlias": "Agricultura", "sectorTipo": "IAF" }
  ]
}
```

---

### `POST /listar`

**Postman:**
```json
{
  "idEmpresaId":    2,
  "perPage":        10,
  "searchQuery":    "IAF",
  "sectorTipo":     "IAF",
  "sectorEstado":   "Activo",
  "sort_field":     "sectorCodigo",
  "sort_direction": "asc"
}
```

---

### `POST /guardar` — Crear

```json
{
  "sectorCodigo":   "A01",
  "sectorAlias":    "Agricultura, ganadería y pesca",
  "sectorTipo":     "IAF",
  "sectorSuperior": null
}
```

### `POST /guardar` — Actualizar

```json
{
  "idSelector":    "MQ==",
  "sectorCodigo":  "A01",
  "sectorAlias":   "Agricultura, ganadería y pesca (actualizado)",
  "sectorTipo":    "IAF",
  "sectorSuperior": 3
}
```

| Campo | Regla |
|---|---|
| `sectorCodigo` | Requerido, único, máx. 10 chars |
| `sectorAlias` | Opcional, máx. 250 chars |
| `sectorTipo` | Requerido: `"IAF"` \| `"NACE"` |
| `sectorSuperior` | Opcional. ID entero del padre (clave primaria, **no base64**) |

---

### `POST /cambiar-estado`

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

`estado`: `"Activo"` | `"Inactivo"`

---

## 4. Tipo de Servicio

Prefijo: `/api/operacion/tiposervicio/`

### Endpoints

| Método | URL | Permiso | Descripción |
|---|---|---|---|
| `POST` | `/api/operacion/tiposervicio/listar` | `operacion.tiposervicio.listar` | Lista paginada |
| `GET` | `/api/operacion/tiposervicio/detalle/{id}` | `operacion.tiposervicio.detalle` | Detalle |
| `GET` | `/api/operacion/tiposervicio/cargar-datos-edicion/{id}` | `operacion.tiposervicio.cargarDatosEdicion` | Datos para edición |
| `POST` | `/api/operacion/tiposervicio/guardar` | `operacion.tiposervicio.guardar` | Crear o actualizar |
| `POST` | `/api/operacion/tiposervicio/cambiar-estado` | `operacion.tiposervicio.cambiarEstado` | Activar / Eliminar |
| `GET` | `/api/operacion/tiposervicio/descargar-plantilla` | `operacion.tiposervicio.descargarPlantilla` | Plantilla Excel |
| `POST` | `/api/operacion/tiposervicio/guardar-archivo-temporal` | `operacion.tiposervicio.guardarArchivoTemporal` | Subir Excel |
| `POST` | `/api/operacion/tiposervicio/obtener-datos-archivo` | `operacion.tiposervicio.obtenerDatosArchivo` | Total filas |
| `POST` | `/api/operacion/tiposervicio/validar-chunk` | `operacion.tiposervicio.validarChunk` | Validar chunk |
| `POST` | `/api/operacion/tiposervicio/importar-chunk` | `operacion.tiposervicio.importarChunk` | Importar chunk |
| `POST` | `/api/operacion/tiposervicio/eliminar-archivo-temporal` | `operacion.tiposervicio.eliminarArchivoTemporal` | Limpiar temporal |
| `POST` | `/api/operacion/tiposervicio/exportar-excel` | `operacion.tiposervicio.exportarExcel` | Excel |
| `POST` | `/api/operacion/tiposervicio/exportar-pdf` | `operacion.tiposervicio.exportarPdf` | PDF |

---

### `POST /listar`

**Postman:**
```json
{
  "idEmpresaId":          2,
  "perPage":              10,
  "searchQuery":          "Certificación",
  "tipoServicioEstado":   "Activo",
  "sort_field":           "tipoServicioAlias",
  "sort_direction":       "asc"
}
```

---

### `POST /guardar` — Crear

```json
{
  "tipoServicioAlias": "Certificación"
}
```

### `POST /guardar` — Actualizar

```json
{
  "idTipoServicio":    "MQ==",
  "tipoServicioAlias": "Certificación ISO"
}
```

| Campo | Regla |
|---|---|
| `tipoServicioAlias` | Requerido |

---

### `POST /cambiar-estado`

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

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

---

### Importación Excel (Tipo de Servicio)

Flujo igual al de Empresa. Columna de la plantilla:

| Columna plantilla | Campo BD |
|---|---|
| `Alias` | `tipoServicioAlias` |

---

## Resumen — empresa activa por módulo

| Módulo | Filtra por empresa activa | Asocia nuevos registros a empresa activa | Mecanismo |
|---|---|---|---|
| Norma | Sí | Sí (al crear) | Pivote `ope_empresanorma` |
| Auditor | Sí | Sí (al crear) | Pivote `ope_empresaauditor` |
| Sector | Sí | Sí (al crear) | Pivote `ope_empresasector` |
| Tipo de Servicio | Sí | Sí (al crear) | Pivote `ope_empresatiposervicio` |

Al crear un registro sin empresa activa resuelta, **no se asocia a ninguna empresa** y no aparecerá en los listados filtrados.

## Resumen — IDs en Resources

Todos los Resources de este módulo devuelven el ID ya en base64.

| Resource | Campo codificado |
|---|---|
| `NormaListaResource` | `idNorma` |
| `AuditorListaResource` | `idAuditor` |
| `SectorListaResource` | `idSelector` |
| `TiposervicioListaResource` | `idTipoServicio` |
