# Cron Job — Auto-Conformación de Solicitudes

## Resumen

Se implementó un cron job que auto-conforma solicitudes estancadas en estado **CO** (Ejecutado/Por Conformar) cuando el solicitante no responde dentro de un umbral configurable (por defecto **48 horas**).

## Archivos Modificados

| Archivo | Descripción |
|---------|-------------|
| [AutoConformarSolicitudes.php](file:///c:/xampp82/htdocs/siser/siser_v2/app/Console/Commands/AutoConformarSolicitudes.php) | Comando Artisan completo con máquina de estados, auditoría y dry-run |
| [console.php](file:///c:/xampp82/htdocs/siser/siser_v2/routes/console.php) | Configuración del scheduler (cada hora, con `--cerrar`) |

## Flujo de Auto-Conformación

```mermaid
flowchart TD
    A["⏰ Scheduler ejecuta cada hora"] --> B["Buscar solicitudes en CO\nsin calificación\nupdated_at <= 48h atrás"]
    B --> C{¿Hay solicitudes\nelegibles?}
    C -->|No| D["✅ Log: Sin pendientes"]
    C -->|Sí| E["Para cada solicitud:"]
    E --> F["1. CO → CF\ncalificación = 5 ★\nobservación automática"]
    F --> G["2. AuditLog: ticket_conformado\norigen = cron"]
    G --> H{¿Flag --cerrar?}
    H -->|Sí| I["3. CF → CE\nAuditLog: ticket_cerrado"]
    H -->|No| J["Fin para esta solicitud"]
    I --> J
```

## Uso Manual del Comando

```bash
# Solo ver qué solicitudes se conformarían (sin cambios)
php artisan siser:auto-conformar --dry-run

# Conformar con umbral por defecto (48h)
php artisan siser:auto-conformar

# Conformar + cerrar definitivamente
php artisan siser:auto-conformar --cerrar

# Usar umbral personalizado (72 horas)
php artisan siser:auto-conformar --horas=72

# Combinaciones
php artisan siser:auto-conformar --horas=72 --cerrar --dry-run
```

## Resultado del Dry-Run de Prueba

```
══════════════════════════════════════════════════════
  SISER — Auto-Conformación de Solicitudes
══════════════════════════════════════════════════════
  Fecha/Hora:    13/05/2026 08:20:11
  Umbral:        48 horas (antes de 11/05/2026 08:20)
  Cierre auto:   NO (solo CO → CF)
  Modo:          🧪 DRY-RUN (sin cambios)
══════════════════════════════════════════════════════

Se encontraron 3 solicitud(es) elegible(s):

+----+-----------+------------------+------------------+-------------+
| ID | Número    | Solicitante      | Última Act.      | Horas en CO |
+----+-----------+------------------+------------------+-------------+
| 23 | 2026-0023 | Nathaly Andreina | 17/04/2026 11:33 | 620         |
| 44 | 2026-0040 | Solicitante      | 07/05/2026 11:29 | 140         |
| 22 | 2026-0022 | Nathaly Andreina | 08/05/2026 09:45 | 118         |
+----+-----------+------------------+------------------+-------------+

🧪 Modo DRY-RUN: No se realizaron cambios en la base de datos.
```

## Configuración del Scheduler

El scheduler está configurado en [console.php](file:///c:/xampp82/htdocs/siser/siser_v2/routes/console.php):

- **Frecuencia:** cada hora (`->hourly()`)
- **Flag por defecto:** `--cerrar` (CO → CF → CE en un solo paso)
- **Protección:** `->withoutOverlapping()` evita ejecuciones duplicadas
- **Log dedicado:** `storage/logs/auto-conformar.log`
- **Callback de error:** log automático si el comando falla

## Configuración en Producción

> [!IMPORTANT]
> Para que el scheduler funcione, debe existir **una sola** entrada cron que ejecute `schedule:run` cada minuto. Laravel se encarga internamente de decidir cuándo ejecutar cada tarea.

### Linux (Crontab)
```bash
* * * * * cd /ruta/a/siser_v2 && php artisan schedule:run >> /dev/null 2>&1
```

### Windows (Task Scheduler)
1. Abrir **Programador de Tareas** (`taskschd.msc`)
2. Crear tarea básica:
   - **Programa:** `C:\xampp82\php\php.exe`
   - **Argumentos:** `C:\xampp82\htdocs\siser\siser_v2\artisan schedule:run`
   - **Inicio en:** `C:\xampp82\htdocs\siser\siser_v2`
   - **Frecuencia:** cada 1 minuto
   - **Ejecutar con independencia de que el usuario haya iniciado sesión:** Sí

## Detalles Técnicos Clave

| Aspecto | Implementación |
|---------|----------------|
| Máquina de estados | Usa `$solicitud->transicionarA()` — consistente con el resto del sistema |
| Auditoría | `AuditLog::create()` directo con `user_id = null` y `user_agent = SISER-CronJob/AutoConformar` |
| Trazabilidad | Las observaciones incluyen fecha exacta y horas de espera |
| Idempotencia | Solo busca `estado = CO` + `calificacion IS NULL` → no reprocesa |
| Rollback | Si falla la transición de una solicitud, las demás siguen procesándose |

> [!NOTE]
> El `AuditLog` usa `user_id = null` porque la ejecución es desde CLI (cron), no hay sesión de usuario. Esto permite filtrar fácilmente en reportes las acciones automáticas del sistema vs. las manuales.
