feat(pendientes): cotizador (mensualidades, bloqueo de unidad, totales, numMensualidades), cobranza (recibos, atrasos, mora configurable, recordatorios), soporte (adjuntos, filtros, estados), clientes maestro (catalogos reales), contactos (ficha y duplicados) y permisos de captura por rol

This commit is contained in:
2026-09-21 08:34:53 +00:00
parent b5a20aed4e
commit 2fc80e7f9e
66 changed files with 3792 additions and 436 deletions

View File

@@ -11,6 +11,8 @@ function captura(
origen: string | null
desarrollo: string | null
createdAt: Date
updatedAt: Date
datosJson: string | null
}> = {},
) {
return {
@@ -20,6 +22,8 @@ function captura(
origen: overrides.origen ?? 'Facebook',
desarrollo: overrides.desarrollo ?? 'DIAGONAL',
createdAt: overrides.createdAt ?? new Date('2026-09-05T10:00:00'),
updatedAt: overrides.updatedAt ?? overrides.createdAt ?? new Date('2026-09-05T10:00:00'),
datosJson: overrides.datosJson ?? null,
}
}
@@ -85,15 +89,41 @@ describe('construirMiTablero', () => {
expect(tablero.conversion.descartados).toBe(1) // etapa terminal DESCARTADO
})
it('calcula ventas del mes correctamente', () => {
it('calcula ventas del mes con ventas CERRADAS (no cotizaciones)', () => {
// Las cotizaciones ya no cuentan como ventas: se cuentan los apartados con
// firma de cliente o posteriores, sin repetir la misma unidad.
const cots = [
cotizacion({ precioFinal: 850000, createdAt: new Date('2026-09-01T12:00:00') }),
cotizacion({ precioFinal: 750000, createdAt: new Date('2026-09-05T12:00:00') }),
cotizacion({ precioFinal: 900000, createdAt: new Date('2026-08-15T12:00:00') }), // mes anterior
]
const tablero = construirMiTablero([], cots, [], AHORA)
const capturas = [
captura({
leadId: 'loc_apt_1',
board: 'APARTADO',
etapa: 'Firma Cliente',
updatedAt: new Date('2026-09-10T12:00:00'),
datosJson: JSON.stringify({ precioFinal: 1_500_000 }),
}),
captura({
leadId: 'loc_apt_2',
board: 'APARTADO',
etapa: 'Venta',
updatedAt: new Date('2026-09-12T12:00:00'),
datosJson: JSON.stringify({ precioFinal: 2_000_000 }),
}),
// Mismo apartado recotizado: no debe duplicarse (mismo leadId)
captura({
leadId: 'loc_apt_1',
board: 'APARTADO',
etapa: 'Firma Cliente',
updatedAt: new Date('2026-09-15T12:00:00'),
datosJson: JSON.stringify({ precioFinal: 1_500_000 }),
}),
captura({ board: 'APARTADO', etapa: 'Pago Apartado', updatedAt: new Date('2026-09-11T12:00:00') }),
captura({ board: 'APARTADO', etapa: 'Cancelacion Apartado', updatedAt: new Date('2026-09-11T12:00:00') }),
]
const tablero = construirMiTablero(capturas, cots, [], AHORA)
expect(tablero.ventasMes.unidades).toBe(2)
expect(tablero.ventasMes.monto).toBe(1600000) // 850000 + 750000
expect(tablero.ventasMes.monto).toBe(3_500_000)
})
it('agrupa por origen con conteo de convertidos', () => {