# 🔄 Reglas de Negocio: Servicios Programados y Cronjob

**Última actualización:** 17 de noviembre de 2025  
**Documento crítico:** Estas reglas definen cómo funciona el sistema de programación automática

---

## 📋 Resumen Ejecutivo

El sistema tiene un **cronjob automático** (`php artisan service:scheduled`) que habilita servicios programados según su `time_request`. Para que esto funcione correctamente:

> **REGLA DE ORO:** Todo servicio con `flag_visible=false` DEBE tener `flag_scheduled=true`

---

## 🎯 Reglas de Negocio

### **Regla 1: Ocultar servicio → Convertir a programado**

Cuando un usuario oculta un servicio (programado o normal), **automáticamente se marca como programado** para que el cronjob lo habilite según su hora programada.

**Implementación:**
```php
// ServicesRepository.php - línea 171
if ($request->flag_visible == false && !$details->flag_scheduled) {
    $details->flag_scheduled = true;
    if ($service->id_status != 5) {
        $service->id_status = 5; // N/A Reservado
    }
}
```

**Flujo:**
```
Servicio Normal Visible (flag_scheduled=false, flag_visible=true, id_status=1)
    ↓ Usuario hace click "Ocultar"
Servicio Programado Oculto (flag_scheduled=true, flag_visible=false, id_status=5)
    ↓ Cronjob detecta time_request <= now() + 30min
Servicio Programado Visible (flag_scheduled=true, flag_visible=true, id_status=1)
```

---

### **Regla 2: Mostrar servicio reservado → Habilitar inmediatamente**

Cuando un usuario hace click en "Mostrar" en un servicio reservado, **se habilita inmediatamente** sin esperar al cronjob.

**Implementación:**
```php
// ServicesRepository.php - línea 185
if ($request->flag_visible == true && $oldVisible == false && $service->id_status == 5) {
    $service->id_status = 1; // N/A normal - disponible inmediatamente
    // flag_scheduled se mantiene en true (característica permanente)
}
```

**Flujo:**
```
Servicio Reservado Oculto (flag_scheduled=true, flag_visible=false, id_status=5)
    ↓ Usuario hace click "Mostrar"
Servicio Normal Visible (flag_scheduled=true, flag_visible=true, id_status=1)
```

---

### **Regla 3: Cronjob solo procesa servicios programados**

El cronjob **filtra por `flag_scheduled=true`** para evitar habilitar servicios que no deberían ser automáticos.

**Implementación:**
```php
// scheduledServices.php - línea 56
$services = $this->service->whereIdStatus(5)
    ->whereHas('detail', function ($query) {
        $query->where('flag_scheduled', true);  // Solo servicios realmente programados
    })
    ->whereHas('time', function ($query) use ($month) {
        $query->whereMonth('time_request', $month);
    })->get();
```

**Lógica:**
```
Cronjob ejecuta cada X minutos
    ↓
Busca: id_status=5 AND flag_scheduled=true AND time_request <= now()+30min
    ↓
Por cada servicio encontrado:
    - Cambia id_status: 5 → 1 (N/A Reservado → N/A normal)
    - Cambia flag_visible: false → true
    - Mantiene flag_scheduled: true (permanente)
```

---

### **Regla 4: `flag_scheduled` es permanente**

Una vez que `flag_scheduled=true`, **nunca se cambia a false**. Es una característica histórica del servicio.

**Razón:**
- Permite identificar servicios que fueron/son programados
- El cronjob mantiene esta flag después de habilitar
- Útil para auditorías y reportes

---

## 📊 Tabla de Estados Válidos

| `flag_scheduled` | `flag_visible` | `id_status` | Descripción | Comportamiento |
|------------------|----------------|-------------|-------------|----------------|
| `true` | `false` | `5` | Servicio reservado esperando cronjob | Cronjob lo habilitará automáticamente |
| `true` | `true` | `1` | Servicio programado ya habilitado | Disponible para asignación |
| `false` | `true` | `1` | Servicio normal (no programado) | Creado y visible inmediatamente |
| `false` | `false` | - | ❌ **COMBINACIÓN INVÁLIDA** | Se corrige automáticamente a `flag_scheduled=true` |

---

## 🚨 Combinaciones Inválidas

### ❌ `flag_visible=false` + `flag_scheduled=false`

**Problema:** Un servicio oculto sin ser programado no tiene propósito - nunca se habilitará automáticamente.

**Corrección Automática:**
```php
if ($request->flag_visible == false && !$details->flag_scheduled) {
    $details->flag_scheduled = true;  // Corrección
    $service->id_status = 5;           // Cambiar a reservado
}
```

---

## 🔧 Comandos y Cronjob

### Comando Manual

```bash
php artisan service:scheduled
```

### Configuración Cronjob (ejemplo)

```bash
# Ejecutar cada 5 minutos
*/5 * * * * cd /path/to/project && php artisan service:scheduled >> /dev/null 2>&1
```

### Logging

El comando genera logs en `storage/logs/laravel.log`:

```
[2025-11-17 23:00:00] local.INFO: TrafficApp - Procesando 3 servicios programados
[2025-11-17 23:00:01] local.INFO: TrafficApp - Servicio programado habilitado automáticamente: 311505 | Origin: AV. TECNOLOGICO 401 COORPORA... | Time: 2025-11-21 17:00:00
```

---

## 🧪 Tests

### Tests de Reglas de Negocio

```bash
./vendor/bin/phpunit tests/Unit/ScheduledServiceBusinessRulesTest.php --testdox
```

**Tests incluidos:**
1. ✅ `hiding_normal_service_converts_to_scheduled` - Ocultar normal → programado
2. ✅ `showing_reserved_service_enables_immediately` - Mostrar reservado → habilitar
3. ✅ `cronjob_only_processes_scheduled_services` - Cronjob filtra por flag_scheduled
4. ✅ `hidden_service_must_have_flag_scheduled_true` - Combinación inválida detectada
5. ✅ `cronjob_maintains_flag_scheduled_after_enabling` - flag_scheduled permanente
6. ✅ `differentiate_originally_scheduled_vs_converted` - Ambos casos equivalentes

---

## 📖 Ejemplos de Uso

### Caso 1: Servicio Programado Desde Creación

```javascript
// Frontend: Crear servicio programado
POST /api-web/services/v2/store
{
  reservar: true,
  time_request: "2025-11-21 14:00:00",
  origin: "Origen A",
  destination: "Destino B"
}

// Backend crea:
// - flag_scheduled = true
// - flag_visible = true
// - id_status = 5 (N/A Reservado)

// Usuario puede ocultarlo (opcional):
PATCH /api-web/services/v2/hide
{ id_service: 311505, flag_visible: false }

// Backend actualiza:
// - flag_visible = false
// - flag_scheduled = true (sin cambios)
// - id_status = 5 (sin cambios)

// Cronjob a las 13:30 (30 min antes):
// - flag_visible = true
// - id_status = 1 (N/A normal)
// - flag_scheduled = true (permanente)
```

### Caso 2: Servicio Normal Que Se Oculta

```javascript
// Frontend: Crear servicio normal
POST /api-web/services/v2/store
{
  reservar: false,
  time_request: "2025-11-17 10:00:00",
  origin: "Origen C",
  destination: "Destino D"
}

// Backend crea:
// - flag_scheduled = false
// - flag_visible = true
// - id_status = 1 (N/A normal)

// Usuario decide ocultarlo:
PATCH /api-web/services/v2/hide
{ id_service: 311506, flag_visible: false }

// Backend CONVIERTE automáticamente:
// - flag_visible = false
// - flag_scheduled = false → true  ⚡ CONVERSIÓN
// - id_status = 1 → 5

// Cronjob lo habilitará según time_request
```

### Caso 3: Mostrar Servicio Reservado Manualmente

```javascript
// Usuario hace click "Mostrar" en servicio reservado
PATCH /api-web/services/v2/update/311505
{ flag_visible: true }

// Backend habilita inmediatamente:
// - flag_visible = true
// - id_status = 5 → 1  ⚡ HABILITACIÓN INMEDIATA
// - flag_scheduled = true (sin cambios)

// El servicio aparece en dashboard sin esperar cronjob
```

---

## 🔍 Auditoría y Debugging

### Query para encontrar servicios que el cronjob procesará

```sql
SELECT 
    s.id,
    d.flag_scheduled,
    d.flag_visible,
    s.id_status,
    t.time_request,
    d.origin
FROM services s
JOIN details d ON s.id_detail = d.id
JOIN times t ON s.id_time = t.id
WHERE 
    s.id_status = 5
    AND d.flag_scheduled = true
    AND t.time_request <= NOW() + INTERVAL '30 minutes'
ORDER BY t.time_request;
```

### Query para encontrar combinaciones inválidas

```sql
SELECT 
    s.id,
    d.flag_scheduled,
    d.flag_visible,
    s.id_status,
    d.origin
FROM services s
JOIN details d ON s.id_detail = d.id
WHERE 
    d.flag_visible = false 
    AND d.flag_scheduled = false;  -- ❌ INVÁLIDO
```

---

## 📚 Referencias

- **Código del cronjob:** `app/Console/Commands/scheduledServices.php`
- **Reglas de negocio:** `app/Repositories/Web/Services/ServicesRepository.php` líneas 168-189
- **Tests:** `tests/Unit/ScheduledServiceBusinessRulesTest.php`
- **Diccionario completo:** `DICCIONARIO_DATOS.md`
- **API Frontend:** `resources/assets/js/api/Service.js`
- **Store Vuex:** `resources/assets/js/store/modules/services.js`

---

## ⚠️ Advertencias Importantes

1. **NO modificar `flag_scheduled` manualmente en update general**
   - Solo se modifica en flujos específicos (crear, ocultar)
   
2. **Cronjob debe ejecutarse regularmente**
   - Servicios programados dependen del cronjob para habilitarse
   - Si cronjob falla, servicios quedan ocultos indefinidamente

3. **`flag_scheduled` es permanente**
   - Una vez `true`, nunca vuelve a `false`
   - Permite identificar servicios que fueron/son programados

4. **Tests requieren BD de pruebas**
   - Tests en `tests/Feature/Web/V2/ScheduledServicesTest.php` usan BD real
   - Configurar BD de testing para evitar corrupción de datos

---

**Creado por:** Equipo de desarrollo AMX  
**Contacto:** albertogranados@aliomexico.com
