feat(funnel): leads entran como Afluencia al funnel de Oportunidades
- Captura rápida crea lead + oportunidad en etapa Afluencia (vinculadas por nueva tabla LeadOportunidad); también aplica a leads creados directo en Monday vía webhook - Transiciones automáticas: AGENDA CITA → Agendo Cita, descarte → Perdido (+ dropdown Motivo No Compra), cotización con mismo teléfono ACTUALIZA la oportunidad (no duplica) → Cotizador, alta de cliente con leadMondayId → Aparto (dedup previo para no duplicar Apartado) - Columna Desarrollo agregada al board Oportunidades (color_mm6sa20t) - Migración lead_oportunidad aplicada con migrate diff + resolve (la DB tiene drift histórico por db push; migrate dev pedía reset) - Tests: 322 verdes; smoke en vivo verificado y limpiado
This commit is contained in:
@@ -4,6 +4,7 @@ import type { Request, Response } from 'express'
|
||||
const mocks = vi.hoisted(() => ({
|
||||
runRoundRobin: vi.fn(),
|
||||
escaneoOportunidades: vi.fn(),
|
||||
asegurarAfluencia: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('../services/automatizaciones.service.js', () => ({
|
||||
@@ -13,6 +14,12 @@ vi.mock('../services/automatizaciones.service.js', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('../services/funnel.service.js', () => ({
|
||||
funnelService: {
|
||||
asegurarOportunidadAfluenciaPorLeadId: mocks.asegurarAfluencia,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('../config/index.js', () => ({
|
||||
config: {
|
||||
monday: {
|
||||
@@ -35,6 +42,7 @@ beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mocks.runRoundRobin.mockResolvedValue({ asignados: 1 })
|
||||
mocks.escaneoOportunidades.mockResolvedValue({ apartadosCreados: ['x'], ventasCreadas: [], errores: [] })
|
||||
mocks.asegurarAfluencia.mockResolvedValue('op-1')
|
||||
})
|
||||
|
||||
describe('webhookMonday', () => {
|
||||
@@ -51,6 +59,7 @@ describe('webhookMonday', () => {
|
||||
expect(sendStatus).toHaveBeenCalledWith(200)
|
||||
expect(mocks.runRoundRobin).not.toHaveBeenCalled()
|
||||
expect(mocks.escaneoOportunidades).not.toHaveBeenCalled()
|
||||
expect(mocks.asegurarAfluencia).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('create_item en Leads dispara el round-robin del item', () => {
|
||||
@@ -58,6 +67,7 @@ describe('webhookMonday', () => {
|
||||
webhookMonday(reqCon({ event: { type: 'create_item', boardId: 111, itemId: 999 } }), res)
|
||||
expect(sendStatus).toHaveBeenCalledWith(200)
|
||||
expect(mocks.runRoundRobin).toHaveBeenCalledWith('999')
|
||||
expect(mocks.asegurarAfluencia).toHaveBeenCalledWith('999')
|
||||
})
|
||||
|
||||
it('create_pulse (nombre real del evento) con pulseId también dispara el round-robin', () => {
|
||||
@@ -65,13 +75,15 @@ describe('webhookMonday', () => {
|
||||
webhookMonday(reqCon({ event: { type: 'create_pulse', boardId: 111, pulseId: 777 } }), res)
|
||||
expect(sendStatus).toHaveBeenCalledWith(200)
|
||||
expect(mocks.runRoundRobin).toHaveBeenCalledWith('777')
|
||||
expect(mocks.asegurarAfluencia).toHaveBeenCalledWith('777')
|
||||
})
|
||||
|
||||
it('create_item en otro board no dispara round-robin', () => {
|
||||
it('create_item en otro board no dispara round-robin ni afluencia', () => {
|
||||
const { res, sendStatus } = resFake()
|
||||
webhookMonday(reqCon({ event: { type: 'create_item', boardId: 555, itemId: 999 } }), res)
|
||||
expect(sendStatus).toHaveBeenCalledWith(200)
|
||||
expect(mocks.runRoundRobin).not.toHaveBeenCalled()
|
||||
expect(mocks.asegurarAfluencia).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('change_status_column_value en Oportunidades dispara el escaneo', () => {
|
||||
|
||||
Reference in New Issue
Block a user