feat(api): automatizaciones Monday — round-robin de leads, recordatorio inactividad 10d, webhooks reales y polling cron

- Servicio de automatizaciones con estado persistido en SQLite (índice RR, dedup de recordatorios)
- Webhook Monday procesa create_item del board Leads y asigna vendedor
- Crons: polling cada 5 min (respaldo sin webhook público) + escaneo diario 9am
- Endpoints admin: /api/automatizaciones/estado, /round-robin/run, /inactividad/preview
- 15 tests nuevos (70/70 verdes)
This commit is contained in:
2026-08-22 02:37:16 +00:00
parent 05f6ceb01a
commit 8407fc24c3
13 changed files with 781 additions and 12 deletions

View File

@@ -1,8 +1,41 @@
import cron from 'node-cron'
import { crearApp } from './app.js'
import { config } from './config/index.js'
import { automatizacionesService } from './services/automatizaciones.service.js'
const app = crearApp()
// Crons de respaldo (el webhook público aún no existe: no hay SSL ni DNS).
// Se desactivan con MONDAY_AUTOMATIONS_ENABLED=false.
if (config.automatizaciones.enabled) {
// Polling: asigna vendedor (round-robin) a leads nuevos sin persona asignada
cron.schedule(config.automatizaciones.pollCron, () => {
automatizacionesService
.runRoundRobin()
.then((r) => {
if (r.asignados > 0) {
// eslint-disable-next-line no-console
console.log(`[cron poll] round-robin: ${r.asignados} lead(s) asignado(s)`)
}
})
.catch((err: unknown) => console.error('[cron poll] error en round-robin:', err))
})
// Escaneo diario de leads inactivos (recordatorio vía update en el item)
cron.schedule(config.automatizaciones.recordatorioCron, () => {
automatizacionesService
.escaneoInactividad({ preview: false })
.then((r) => {
// eslint-disable-next-line no-console
console.log(`[cron inactividad] ${r.recordatoriosEnviados} recordatorio(s) enviado(s)`)
})
.catch((err: unknown) => console.error('[cron inactividad] error en escaneo:', err))
})
// eslint-disable-next-line no-console
console.log(`Automatizaciones Monday activas (poll: "${config.automatizaciones.pollCron}", inactividad: "${config.automatizaciones.recordatorioCron}")`)
}
app.listen(config.port, () => {
// eslint-disable-next-line no-console
console.log(`API Gateway FRASA escuchando en http://localhost:${config.port} (${config.nodeEnv})`)