refactor: las afluencias viven en Oportunidades (no en Leads)
- POST /api/leads crea UN solo item en Oportunidades (Estado Afluencia) con todo el perfil (desarrollo, modelo, estado civil, credito, etc.) en las columnas del board; dedup por telefono contra Oportunidades; asignacion al capturista via columna Asesor o round-robin del equipo. - Round-robin, escalado e inactividad operan sobre Oportunidades (solo etapa Afluencia sin asesor entra al reparto). - Acciones del vendedor (contactado/cita/descartar) escriben directo en la oportunidad; descarte = Perdido + Motivo No Compra. - Mi dia y reportes leen Oportunidades; el funnel de /dash arranca en Afluencia; misOportunidades.porEtapa ya es real (columna Asesor). - El picker del alta busca en Leads (historico) Y Oportunidades, con insignia de origen; moverAAparto tolera ambos ids. - Lectura de status con value null = vacio (quirk del default Monday). - Script de migracion del historial de agosto Leads -> Oportunidades.
This commit is contained in:
@@ -2,7 +2,6 @@ import type { Request, Response } from 'express'
|
||||
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'
|
||||
|
||||
@@ -46,7 +45,6 @@ export function webhookMonday(req: Request, res: Response): void {
|
||||
}
|
||||
|
||||
const { type, boardId, itemId, pulseId } = parsed.data.event
|
||||
const esBoardLeads = String(boardId) === config.monday.boards.leads
|
||||
const esBoardOportunidades = String(boardId) === config.monday.boards.oportunidades
|
||||
const idEvento = itemId ?? pulseId
|
||||
|
||||
@@ -55,7 +53,15 @@ export function webhookMonday(req: Request, res: Response): void {
|
||||
|
||||
// Monday registra el webhook como 'create_item' pero el payload real llega
|
||||
// como 'create_pulse'; se aceptan ambos.
|
||||
if ((type === 'create_item' || type === 'create_pulse') && esBoardLeads && idEvento !== undefined) {
|
||||
// Item nuevo en Oportunidades → solo round-robin (la afluencia entra en
|
||||
// etapa 'Afluencia' sin asesor; las del cotizador nacen en 'Cotizador' y el
|
||||
// servicio las salta). El grupo NO se arregla aquí: recién creado el item aún
|
||||
// no tiene Estado (moverAGrupoSegunEstado no haría nada) y el create con
|
||||
// column_values no dispara el webhook de change — para items creados a mano
|
||||
// en la UI, el grupo lo corrige la próxima vez que cambie una columna.
|
||||
// (El tablero Leads quedó como archivo histórico: ya no tiene automatización
|
||||
// de create; su webhook se desregistra en Monday.)
|
||||
if ((type === 'create_item' || type === 'create_pulse') && esBoardOportunidades && idEvento !== undefined) {
|
||||
const id = String(idEvento)
|
||||
// Fire-and-forget: la respuesta 200 no espera a que termine la asignación
|
||||
void automatizacionesService
|
||||
@@ -68,18 +74,6 @@ export function webhookMonday(req: Request, res: Response): void {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`[webhook monday] error en round-robin item=${id}:`, err)
|
||||
})
|
||||
// El lead entra también al funnel de Oportunidades (etapa 'Afluencia').
|
||||
// El payload no trae nombre/teléfono: el servicio los consulta en Monday.
|
||||
void funnelService
|
||||
.asegurarOportunidadAfluenciaPorLeadId(id)
|
||||
.then((opId) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`[webhook monday] afluencia item=${id}: oportunidad ${opId ?? 'no creada'}`)
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`[webhook monday] error en afluencia item=${id}:`, err)
|
||||
})
|
||||
}
|
||||
|
||||
// Cambio de etapa en Oportunidades → crear Apartado/Venta si aplica.
|
||||
|
||||
Reference in New Issue
Block a user