feat(mi-dia): tabs Mis oportunidades/Mis apartados con fases y cotizador vinculado
- Resumen Mi día: misOportunidades.items (lista de trabajo con perfil y último cambio) y misApartados (Vendedor = yo, estados no finales) - Nuevos endpoints: POST /oportunidades/:id/etapa (Agendo Cita, Afluencia, Cotizador, Aparto, No Asistio, Abandono; con fecha de cita) y POST /apartados/:id/estado (pipeline Enkontrol: Pago Apartado → Firma Cliente (Venta) / Cancelacion Apartado); ambos mueven el item al grupo Monday correspondiente - Mi día: tabs Mis leads / Mis oportunidades / Mis apartados; botón Cotizar abre /quote con el cliente prellenado y la oportunidad ligada - Cotizador: prefill desde query params (cliente, teléfono, email, desarrollo, unidad) y envío de oportunidadId; al guardar, el API actualiza ESE item (Precio final, Tipo propuesta, etapa Cotizador) en vez de buscar por teléfono o crear duplicado - grupos-estado: 'Cancelacion Apartado' → Historico - Estados finales de apartado: agrega Firma Cliente (Venta) y Cancelacion Apartado - Tests: 455 API + 14 mi-dia + 88 cotizador, todo en verde
This commit is contained in:
@@ -20,6 +20,9 @@ const crearCotizacionSchema = z.object({
|
||||
clienteTelefono: z.string().min(7).optional(),
|
||||
tipoPropuesta: z.enum(['BANCARIO', 'INFONAVIT', 'COFINAVIT', 'CONTADO', 'SEMI_CONTADO']),
|
||||
descuento: z.number().min(0).max(1).optional(), // fracción (0.02 = 2%)
|
||||
// Item del tablero Oportunidades al que queda vinculada la cotización
|
||||
// (botón "Cotizar" de Mi día manda el id; sin él se busca por teléfono)
|
||||
oportunidadId: z.string().trim().min(1).optional(),
|
||||
})
|
||||
|
||||
const hoyISO = (): string => {
|
||||
@@ -88,9 +91,15 @@ export async function crearCotizacion(req: Request, res: Response): Promise<void
|
||||
...(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.
|
||||
// oportunidad (etapa 'Cotizador') en vez de crear una segunda. Con
|
||||
// oportunidadId explícito (botón "Cotizar" de Mi día) se actualiza ESE
|
||||
// item directamente y la cotización queda vinculada al cliente.
|
||||
const opExistente =
|
||||
body.clienteTelefono !== undefined ? await funnelService.oportunidadPorTelefono(body.clienteTelefono) : null
|
||||
body.oportunidadId !== undefined
|
||||
? body.oportunidadId
|
||||
: body.clienteTelefono !== undefined
|
||||
? await funnelService.oportunidadPorTelefono(body.clienteTelefono)
|
||||
: null
|
||||
if (opExistente !== null) {
|
||||
await mondayService.actualizarOportunidadCotizacion(opExistente, datosOportunidad)
|
||||
mondayItemId = opExistente
|
||||
|
||||
Reference in New Issue
Block a user