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:
@@ -1,6 +1,7 @@
|
||||
import type { Request, Response } from 'express'
|
||||
import { z } from 'zod'
|
||||
import { mondayService } from '../services/monday.service.js'
|
||||
import { mondayService, type DatosOportunidad } from '../services/monday.service.js'
|
||||
import { funnelService } from '../services/funnel.service.js'
|
||||
import { calcularPropuesta, obtenerPrecioLista } from '../services/cotizacion.service.js'
|
||||
import {
|
||||
procesarAlertasInventario,
|
||||
@@ -72,7 +73,7 @@ export async function crearCotizacion(req: Request, res: Response): Promise<void
|
||||
// Registra la oportunidad en Monday; si Monday falla, la cotización ya quedó persistida
|
||||
let mondayItemId: string | null = null
|
||||
try {
|
||||
mondayItemId = await mondayService.crearOportunidad({
|
||||
const datosOportunidad: DatosOportunidad = {
|
||||
clienteNombre: body.clienteNombre,
|
||||
unidadCodigo: unidad.codigo,
|
||||
tipoPropuestaLabel: LABEL_PROPUESTA_MONDAY[body.tipoPropuesta] ?? body.tipoPropuesta,
|
||||
@@ -83,7 +84,17 @@ export async function crearCotizacion(req: Request, res: Response): Promise<void
|
||||
estadoLabel: 'Cotizador', // etapa del funnel CRM al nacer la oportunidad
|
||||
...(body.clienteEmail !== undefined ? { clienteEmail: body.clienteEmail } : {}),
|
||||
...(body.clienteTelefono !== undefined ? { clienteTelefono: body.clienteTelefono } : {}),
|
||||
})
|
||||
}
|
||||
// Si el teléfono corresponde a un lead del funnel, se ACTUALIZA su
|
||||
// oportunidad (etapa 'Cotizador') en vez de crear una segunda.
|
||||
const opExistente =
|
||||
body.clienteTelefono !== undefined ? await funnelService.oportunidadPorTelefono(body.clienteTelefono) : null
|
||||
if (opExistente !== null) {
|
||||
await mondayService.actualizarOportunidadCotizacion(opExistente, datosOportunidad)
|
||||
mondayItemId = opExistente
|
||||
} else {
|
||||
mondayItemId = await mondayService.crearOportunidad(datosOportunidad)
|
||||
}
|
||||
await prisma.cotizacion.update({ where: { id: cotizacion.id }, data: { mondayItemId } })
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
||||
Reference in New Issue
Block a user