Módulo 5: Scheduling y Tiempo
Mini-Proyecto: Workflow Diario/Semanal Complejo
Descripción de la cápsula
Cierre del módulo. Vas a construir un workflow productivo que integra todos los conceptos de M05: scheduling preciso con cron, timezone correctamente, business days con holidays, y verificación de DST. Caso: reporte ejecutivo semanal que se manda lunes a las 9am hora CDMX, excepto si es holiday (entonces el siguiente día hábil).
Lo que vas a aprender
- ✅ Integrar scheduling avanzado, timezone, business days, holidays
- ✅ Diseñar workflows resilientes a edge cases temporales
- ✅ Generar reporte semanal profesional
El escenario
Requirements
- Workflow se dispara lunes 9am hora CDMX
- Si el lunes es holiday, NO ejecutar (esperar al siguiente día hábil)
- Generar reporte de la semana pasada (lunes-domingo de la semana anterior)
- Datos:
- Total ventas del periodo (mock con Sheet)
- Nuevos clientes (mock)
- Top 3 productos vendidos
- Output: Slack con reporte formateado
Paso 1: Setup
1.1. Sheet de holidays
Crear holidays_mx con feriados mexicanos 2026 (también deja algunos lunes específicos):
date
2026-01-01
2026-02-02
2026-03-16 ← lunes
2026-05-01
2026-09-16
2026-11-16 ← lunes
2026-12-25
1.2. Sheet de ventas (mock data)
Crear sales_2026 con: date, product, amount, customer_email. Llena con ~50 filas distribuidas en abril-mayo 2026.
Paso 2: Workflow
2.1. Trigger
Schedule Trigger:
- Cron: 0 9 * * 1 (lunes 9am)
- Timezone: America/Mexico_City
2.2. Verificar si es holiday
[Schedule]
│
[Sheet: leer holidays_mx]
│
[Set: verificar]
- today_date: {{ DateTime.now().setZone('America/Mexico_City').toFormat('yyyy-MM-dd') }}
- is_holiday: {{ $('Sheet holidays').all().some(h => h.json.date === DateTime.now().setZone('America/Mexico_City').toFormat('yyyy-MM-dd')) }}
│
[IF: !is_holiday]
├─ TRUE → continuar
└─ FALSE → [Slack: "Hoy es holiday, reporte se generará mañana"] → fin
2.3. Si no es holiday — alternativa con re-schedule
Para realmente "esperar al siguiente día hábil", podríamos:
- Si es holiday → terminar
- Otro workflow (con Schedule tuesday-friday 9am) verifica si el lunes anterior fue holiday Y el reporte no se generó → generar
Para simplificar este proyecto: si lunes es holiday, simplemente saltar esa semana. En producción real, implementarías el catch-up.
2.4. Calcular semana anterior
[Set: calcular fechas]
- week_end: {{ DateTime.now().setZone('America/Mexico_City').minus({days: 1}).toFormat('yyyy-MM-dd') }} // domingo pasado
- week_start: {{ DateTime.now().setZone('America/Mexico_City').minus({days: 7}).toFormat('yyyy-MM-dd') }} // lunes hace 7 días
2.5. Leer ventas de la semana
[Google Sheets: sales_2026]
- Filter: date entre week_start y week_end
2.6. Calcular stats
[Aggregate: all into 'sales']
│
[Set: stats]
- total_sales: {{ $json.sales.length }}
- total_amount: {{ $json.sales.reduce((a, b) => a + Number(b.amount), 0) }}
- unique_customers: {{ [...new Set($json.sales.map(v => v.customer_email))].length }}
- top_products: {{
Object.entries(
$json.sales.reduce((acc, v) => {
acc[v.product] = (acc[v.product] || 0) + 1;
return acc;
}, {})
)
.sort((a, b) => b[1] - a[1])
.slice(0, 3)
.map(([prod, count]) => `• ${prod}: ${count}`)
.join('\n')
}}
2.7. Enviar reporte
[Slack]
📊 *Reporte Semanal*
Semana: {{ $json.week_start }} a {{ $json.week_end }}
*Resumen:*
• Total operaciones: {{ $json.total_sales }}
• Monto total: ${{ $json.total_amount.toFixed(2) }}
• Clientes únicos: {{ $json.unique_customers }}
*Top 3 productos:*
{{ $json.top_products }}
_Generado: {{ DateTime.now().setZone('America/Mexico_City').setLocale('es').toFormat("EEEE dd 'de' MMMM 'a las' HH:mm") }}_
Paso 3: Test
3.1. Test manual (sin esperar lunes)
Agregar Manual Trigger en paralelo al Schedule. Ejecutar manualmente.
Verifica:
- Datos del Sheet filtrados correctamente por fecha
- Stats calculadas correctamente
- Slack recibe mensaje formateado
3.2. Test holiday
Modifica el Sheet de holidays para incluir HOY (lo que sea). Ejecuta. Debe terminar con "es holiday, saltando".
Remueve y vuelve a ejecutar. Debe procesar normal.
3.3. Verificar timezone
Compara los timestamps en el reporte con tu reloj real (CDMX). Deberían coincidir.
Variantes opcionales
Variante 1: Catch-up automático
Si el lunes fue holiday, el martes verificar si el reporte ya se generó. Si no, generarlo retroactivamente.
[Schedule martes-viernes 9am]
│
[IF: lunes anterior fue holiday AND reporte no se ha generado]
└─ TRUE → ejecutar lógica del reporte con fechas de la semana anterior
Variante 2: Reporte mensual
Mismo concepto pero el primer día hábil del mes:
[Schedule día 1-3 del mes 9am]
│
[IF: hoy es primer día hábil del mes]
└─ procesar reporte mensual
Variante 3: Multi-timezone
Si tu equipo está en CDMX, Madrid, y Buenos Aires, generar 3 reportes en los 3 horarios locales.
Resumen y siguiente paso
Lo que construiste:
- Workflow real con scheduling complejo y consciente de holidays
- Manejo de timezone correcto
- Cálculos de fechas precisos
Lo que sabes:
- Aplicar cron + timezone + holidays
- Calcular fechas relativas con Luxon
- Manejar edge cases del calendario
Antes de pasar a M06:
- Workflow funciona en test y reporta correctamente
- Maneja holidays sin fallar
- Reporte llega a Slack con formato profesional
Lo que sigue (Módulo 6 — Error Handling Básico):
Workflows productivos fallan. APIs caen, datos vienen malformados, credenciales expiran. M06 te enseña los patrones básicos de error handling: retry strategies, fallback paths, On Error connectors, y diseño defensivo.
(Error handling avanzado — try/catch sofisticado, dead letter queues — va en G10.)
Creado: Mayo 11, 2026 Versión: 1.0