Files
FRASA-CAS/api-gateway/src/index.ts
Consult AS Technology 04568a5603 feat: automatizaciones ronda 2 + app de dashboards /dash
- Escalado: lead asignado sin contactar >48h genera actividad URGENTE
  al coordinador (LEAD_ESCALADO_HORAS, MONDAY_COORDINADOR_USER_ID)
- Oportunidad en Aparto crea item en Apartado (fecha límite +7d);
  en Ganado crea la Venta; ambos con dedup
- GET /api/reportes/resumen: leads, funnel, unidades por desarrollo,
  cobranza y actividades en una respuesta (cache 60s)
- App dashboards (/dash): 8 KPIs + 3 gráficas Recharts, con auth
- Landing: tarjeta Tableros; docs/AUTOMATIZACIONES-MONDAY.md con las
  recetas nativas para activar en la UI
- Tests: api 158, dashboards 7, landing 5 — todo verde
2026-08-27 09:05:09 +00:00

68 lines
2.7 KiB
TypeScript

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,
// escala leads sin contactar y crea Apartados/Ventas al moverse Oportunidades.
// Cada regla tiene su propio catch: un fallo de Monday no rompe las demás.
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))
automatizacionesService
.escaneoEscalado()
.then((r) => {
if (r.escalados > 0) {
// eslint-disable-next-line no-console
console.log(`[cron poll] escalado: ${r.escalados} lead(s) escalado(s)`)
}
})
.catch((err: unknown) => console.error('[cron poll] error en escalado:', err))
automatizacionesService
.escaneoOportunidades()
.then((r) => {
if (r.apartadosCreados.length > 0 || r.ventasCreadas.length > 0 || r.errores.length > 0) {
// eslint-disable-next-line no-console
console.log(
`[cron poll] oportunidades: ${r.apartadosCreados.length} apartado(s), ` +
`${r.ventasCreadas.length} venta(s), ${r.errores.length} error(es)`,
)
}
})
.catch((err: unknown) => console.error('[cron poll] error en oportunidades:', 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})`)
})