- PlanPago.vendedorId + filtro ?mios=1; toggle Mis planes/Todos en /pay - Motor de recordatorios: próximos a vencer (3d) y vencidos (re-cada 7d), canal Monday Actividad + stub WhatsApp listo para WA CRM, log persistente, cron diario 9:30, run manual con dry-run, historial en /api/cobranza/recordatorios - Agente IA: memoria por usuario (conversaciones aisladas) y tool calling: cotizar_unidad (motor real por desarrollo) y buscar_unidad (inventario Monday) - Tests: api 261, cobranza 75, agente-ia 13
85 lines
3.5 KiB
TypeScript
85 lines
3.5 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'
|
|
import { recordatoriosCobranzaService } from './services/recordatorios-cobranza.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}")`)
|
|
|
|
// Escaneo diario de recordatorios de cobranza (pagos por vencer / vencidos)
|
|
cron.schedule(config.recordatoriosCobranza.cron, () => {
|
|
recordatoriosCobranzaService
|
|
.ejecutarPasada()
|
|
.then((r) => {
|
|
// eslint-disable-next-line no-console
|
|
console.log(
|
|
`[cron cobranza] recordatorios: ${r.enviados.length} enviado(s), ` +
|
|
`${r.evaluados.filter((e) => e.decision === 'dedup').length} deduplicado(s), ${r.fallidos.length} fallido(s)`,
|
|
)
|
|
})
|
|
.catch((err: unknown) => console.error('[cron cobranza] error en recordatorios:', err))
|
|
})
|
|
// eslint-disable-next-line no-console
|
|
console.log(`Recordatorios de cobranza activos (cron: "${config.recordatoriosCobranza.cron}")`)
|
|
}
|
|
|
|
app.listen(config.port, () => {
|
|
// eslint-disable-next-line no-console
|
|
console.log(`API Gateway FRASA escuchando en http://localhost:${config.port} (${config.nodeEnv})`)
|
|
})
|