feat(monday): mover items al grupo correcto al cambiar de estado

Webhook change_column_value en los 5 tableros (Leads, Oportunidades,
Unidades, Apartado, Contactos) + mapeo central estado->grupo: el API
re-consulta el item y lo mueve con move_item_to_group (idempotente).
El funnel mueve la oportunidad de Afluencia al crearla (Monday la crea
en el grupo top del board). Incluye script de backfill one-off
(249 items reubicados) y tests del mapeo y del webhook.
This commit is contained in:
2026-09-03 00:14:18 +00:00
parent 1ab29d765e
commit 89fcd1c579
8 changed files with 568 additions and 8 deletions

View File

@@ -3,6 +3,8 @@ import { z } from 'zod'
import { config } from '../config/index.js'
import { automatizacionesService } from '../services/automatizaciones.service.js'
import { funnelService } from '../services/funnel.service.js'
import { GRUPOS_POR_ESTADO, moverAGrupoSegunEstado } from '../services/grupos-estado.service.js'
import { mondayService } from '../services/monday.service.js'
const eventoSchema = z
.object({
@@ -86,12 +88,9 @@ export function webhookMonday(req: Request, res: Response): void {
// en AutomatizacionState). El cron de polling queda como red de seguridad.
// OJO: el webhook se registra como 'change_column_value' pero el payload
// real llega como 'update_column_value'; se aceptan los tres nombres.
if (
(type === 'change_status_column_value' ||
type === 'change_column_value' ||
type === 'update_column_value') &&
esBoardOportunidades
) {
const esEventoColumna =
type === 'change_status_column_value' || type === 'change_column_value' || type === 'update_column_value'
if (esEventoColumna && esBoardOportunidades) {
void automatizacionesService
.escaneoOportunidades()
.then((r) => {
@@ -107,5 +106,24 @@ export function webhookMonday(req: Request, res: Response): void {
})
}
// Cambio de status en cualquier tablero con mapeo estado → grupo: el item se
// mueve al grupo que corresponde a su nuevo estado (regla de negocio: los
// grupos reflejan la etapa del pipeline). Mismo criterio que arriba: el
// payload solo dispara y el servicio re-consulta el item antes de mover.
// En Oportunidades convive con el escaneo (ambos disparan).
if (esEventoColumna && idEvento !== undefined && GRUPOS_POR_ESTADO[String(boardId)]) {
const board = String(boardId)
const id = String(idEvento)
void moverAGrupoSegunEstado(mondayService, board, id)
.then((r) => {
// eslint-disable-next-line no-console
console.log(`[webhook monday] grupo-estado board=${board} item=${id}: ${r.movido ? `movido a ${r.grupo}` : 'sin cambio'}`)
})
.catch((err: unknown) => {
// eslint-disable-next-line no-console
console.error(`[webhook monday] error en grupo-estado board=${board} item=${id}:`, err)
})
}
res.sendStatus(200)
}