# Módulo Gastos Fijos Normalizado

## Objetivo
Estandarizar la gestión de gastos fijos con:
- Catálogo central de conceptos (activo/inactivo)
- Periodos mensuales presupuestados por concepto
- Ajustes (positivos/negativos) que afectan el ejecutado calculado
- Cierre / reapertura de periodos
- Modo de cálculo del ejecutado configurable (ajustes | gastos | mixto)
- Resúmenes detallados y globales por mes

## Tablas (modelo lógico)
```
gastos_fijos_catalogo (
  id INT PK AI,
  descripcion VARCHAR(200) NOT NULL,
  activo TINYINT(1) DEFAULT 1,
  creado_en DATETIME DEFAULT CURRENT_TIMESTAMP
)

gastos_fijos_periodos (
  id INT PK AI,
  catalogo_id INT NOT NULL FK -> catalogo.id,
  periodo CHAR(7) NOT NULL -- YYYY-MM
  monto_presupuestado DECIMAL(14,2) NOT NULL DEFAULT 0,
  monto_ejecutado_calculado DECIMAL(14,2) NOT NULL DEFAULT 0,
  ajustes_acumulados DECIMAL(14,2) NOT NULL DEFAULT 0,
  rollover_anterior DECIMAL(14,2) NOT NULL DEFAULT 0,
  porcentaje_uso DECIMAL(7,4) DEFAULT 0,
  cerrado TINYINT(1) DEFAULT 0,
  cerrado_en DATETIME NULL,
  creado_en DATETIME DEFAULT CURRENT_TIMESTAMP,
  actualizado_en DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  UNIQUE(catalogo_id, periodo)
)

gastos_fijos_ajustes (
  id INT PK AI,
  periodo_id INT NOT NULL FK -> periodos.id,
  monto DECIMAL(14,2) NOT NULL,
  motivo VARCHAR(255) NULL,
  creado_en DATETIME DEFAULT CURRENT_TIMESTAMP
)
```

## API (todas retornan JSON estándar)
Formato respuesta:
```
{
  "success": true|false,
  "data": [...|object|null],
  "error": null|"mensaje",
  "warning": null|"codigo_warning",
  "meta": {...}
}
```

### 1. Catálogo
`GET admin/api/gastos_fijos/catalogo.php`
- Params: ninguno
- Devuelve lista de conceptos.

`POST admin/api/gastos_fijos/catalogo.php`
Body (form o JSON simple):
```
{ "descripcion": "Luz", "activo": 1 }
```
Respuesta incluye `data.id` nuevo.

`PATCH admin/api/gastos_fijos/catalogo.php?id={id}`
Campos aceptados JSON: `{ "descripcion": "Nueva", "activo": true|false }`

### 2. Periodos
`GET admin/api/gastos_fijos/periodos.php?periodo=YYYY-MM`
- Devuelve todos los registros de ese mes (uno por catálogo con periodo creado).

`POST admin/api/gastos_fijos/periodos.php`
```
{ "catalogo_id": 5, "periodo": "2025-10", "monto_presupuestado": 1500 }
```
- Crea el periodo (si ya existe retorna warning opcional y los datos actuales).

`PATCH admin/api/gastos_fijos/periodos.php?id={periodo_id}` Operaciones:
- Actualizar presupuesto: `{ "monto_presupuestado": 1800 }` (no permitido si cerrado)
- Cerrar: `{ "cerrar": true }`
- Reabrir: `{ "reabrir": true }`
- Ajuste: `{ "ajuste": -200, "motivo": "Corrección" }`

### 3. Resumen
`GET admin/api/gastos_fijos/resumen.php?mode=global`
- Resumen agregado de todos los conceptos (si existe esquema normalizado).

`GET admin/api/gastos_fijos/resumen.php?periodo=YYYY-MM&exec_mode=mixto`
- `exec_mode`:
  - `ajustes`: usa sólo acumulado de ajustes como ejecutado
  - `gastos`: (futuro) integrará gastos reales externos
  - `mixto`: (actual) base calculada + ajustes

Meta típicamente incluye: `{ "tipo": "detalle", "periodo": "2025-10", "exec_mode": "mixto" }`

## Estados de un periodo
| Estado | Campo `cerrado` | Reglas |
|--------|-----------------|--------|
| Abierto | 0 | Se puede actualizar presupuesto / aplicar ajustes |
| Cerrado | 1 | Presupuesto y nuevos ajustes bloqueados (sólo lectura) |

## Validaciones Clave
- `periodo` regex `^\d{4}-(0[1-9]|1[0-2])$`
- `monto_presupuestado >= 0`
- No modificar presupuesto si `cerrado = 1`
- Ajuste acepta positivo o negativo (DECIMAL) distinto de 0
- Catálogo inactivo no impide consultar periodos previos

## Cálculo de métricas (resumen por fila)
- Disponible: `monto_presupuestado + rollover_anterior`
- Ejecutado Base: `monto_ejecutado_calculado` (placeholder / futura integración real)
- Ajustes: suma tabla `gastos_fijos_ajustes`
- Ejecutado Final (mixto): `monto_ejecutado_calculado + ajustes_acumulados`
- Restante: `Disponible - EjecutadoFinal`
- % Uso: `EjecutadoFinal / Disponible * 100` (si Disponible>0)

## Flujo UI (archivo `admin/gastos_fijos2.php`)
1. Detección esquema: llama a `resumen.php?mode=global`; si responde data => modo normalizado.
2. Al cambiar mes: se ejecutan `cargarResumenPeriodoNormalizado` + `cargarCatalogoNormalizado`.
3. Guardar presupuesto:
   - Si no existe periodo: POST crea y refresca
   - Si existe: PATCH actualiza
4. Cerrar / Reabrir: PATCH con `{cerrar:true}` o `{reabrir:true}`
5. Ajuste: PATCH con `{ajuste: monto, motivo}`

## Ejemplos Curl
Crear catálogo:
```
curl -X POST -d 'descripcion=Internet&activo=1' \
     http://localhost/admin/api/gastos_fijos/catalogo.php
```
Crear periodo:
```
curl -X POST http://localhost/admin/api/gastos_fijos/periodos.php \
  -H 'Content-Type: application/json' \
  -d '{"catalogo_id":3,"periodo":"2025-10","monto_presupuestado":1200}'
```
Ajuste:
```
curl -X PATCH 'http://localhost/admin/api/gastos_fijos/periodos.php?id=15' \
  -H 'Content-Type: application/json' \
  -d '{"ajuste":-150,"motivo":"Corrección exceso"}'
```
Resumen mes:
```
curl 'http://localhost/admin/api/gastos_fijos/resumen.php?periodo=2025-10&exec_mode=mixto'
```

## Errores & Warnings comunes
| Código | Tipo | Descripción |
|--------|------|-------------|
| esquema_no_disponible | warning | Aún no migrado a esquema normalizado |
| periodo_invalido | error | Formato de periodo incorrecto |
| catalogo_no_existe | error | ID de catálogo inexistente |
| periodo_cerrado | error | No se puede modificar presupuesto / ajustes |

## Próximas mejoras sugeridas
- Integrar gastos reales (tabla externa) para `exec_mode=gastos`
- Conversión automática Bs → USD (selector UI + tasa diaria)
- Historial detallado de ajustes por fila (modal)
- Cache ligero en memoria para navegación rápida entre meses
- Tests automáticos (PHPUnit) sobre service
- Rate limiting básico para evitar spam de ajustes

## Conversión de Moneda (plan)
1. Tabla `tasas_dolar(fecha DATE PK, tasa DECIMAL(10,4))`
2. Al cargar cada periodo: si usuario elige USD, dividir montos / tasa del último día del mes (o última disponible <= fin de mes)
3. UI: selector persistente en localStorage
4. Endpoints agregan `meta: { moneda: "VES"|"USD", tasa_aplicada: X }`

## Notas de Migración
- Legacy: totales previos estaban en una sola tabla; ahora cada mes queda aislado.
- Rollover: campo reservado (futuro) para arrastrar sobrantes al mes siguiente.
- Safe Reopen: reabrir no recalcula automáticamente (se mantiene histórico de ajustes).

## Seguridad / Hardening
- Sanitizar siempre `descripcion`
- Limitar longitud de motivos (<=255)
- Usar transacciones en inserción múltiple (periodo + ajuste inicial si aplica)

---
Última actualización: (auto-generado)
