import { describe, it, expect } from 'vitest' import { APARTADO_DEFAULT, calcularPropuesta, calcularPropuestas, totalPagado, type UnidadCotizable, } from './cotizacion.service.js' /** * Fixtures de los 4 desarrollos nuevos, validados al centavo contra los Excel * de producción (reference/cotizadores/*.md). Espejo de * apps/cotizador/src/lib/calculations-desarrollos.test.ts. */ function unidad(parcial: Partial & Pick): UnidadCotizable { return { modelo: 'X', fechaEntrega: null, supEstacionamiento: 0, supConstruccion: 0, supExcedente: 0, precioSinAcabados: 0, precioConAcabados: 0, status: 'DISPONIBLE', ...parcial, } } const DESC_CERO = { creditoBancario: 0, creditoInfonavit: 0, contado: 0, semiContado: 0 } describe('ATELIER — depto 10 (L = 3,997,500, apartado 20,000, N fijo = 1)', () => { const depto10 = unidad({ codigo: 'ATELIER-10', desarrollo: 'ATELIER', fechaEntrega: '2026-01-21', precioSinAcabados: 3997500, precioConAcabados: 3997500, }) it('BANCARIO: enganche 5% sobre LISTA, mensualidad remanente en 1 pago', () => { const [p] = calcularPropuestas({ unidad: depto10, acabados: false, apartado: APARTADO_DEFAULT, descuentos: DESC_CERO, }) expect(p?.enganche).toBeCloseTo(179875, 2) expect(p?.mensualidadesTotal).toBeCloseTo(199875, 2) expect(p?.pagoFinal).toBeCloseTo(3597750, 2) }) it('COFINAVIT (vía tipo COFINAVIT del API): 2.5% / 95% sobre precio final', () => { const p = calcularPropuesta(depto10, 'COFINAVIT', false, APARTADO_DEFAULT) expect(p.titulo).toBe('Crédito Cofinavit') expect(p.enganche).toBeCloseTo(79937.5, 2) expect(p.mensualidadesTotal).toBeCloseTo(99937.5, 2) expect(p.pagoFinal).toBeCloseTo(3797625, 2) expect(totalPagado(p, APARTADO_DEFAULT)).toBeCloseTo(3997500, 2) }) }) describe('ESTANZA — casa 52 (L = 2,437,250): pago final sobre LISTA (quirk)', () => { const casa52 = unidad({ codigo: 'ESTANZA-52', desarrollo: 'ESTANZA', precioSinAcabados: 2437250, precioConAcabados: 2437250, }) it('las 4 propuestas al centavo', () => { const [bancario, infonavit, contado, semi] = calcularPropuestas({ unidad: casa52, acabados: false, apartado: APARTADO_DEFAULT, descuentos: DESC_CERO, }) expect(bancario?.enganche).toBeCloseTo(40931.25, 2) expect(bancario?.mensualidadesTotal).toBeCloseTo(182793.75, 2) expect(bancario?.pagoFinal).toBeCloseTo(2193525, 2) expect(infonavit?.enganche).toBeCloseTo(40931.25, 2) expect(infonavit?.mensualidadesTotal).toBeCloseTo(60931.25, 2) expect(infonavit?.pagoFinal).toBeCloseTo(2315387.5, 2) expect(contado?.enganche).toBeCloseTo(2173525, 2) expect(contado?.pagoFinal).toBeCloseTo(243725, 2) expect(semi?.enganche).toBeCloseTo(1198625, 2) expect(semi?.mensualidadesTotal).toBeCloseTo(974900, 2) expect(semi?.pagoFinal).toBeCloseTo(243725, 2) }) }) describe('SENDA — 411-1-7 (L = 2,451,750, apartado 0, N = 4)', () => { const casa = unidad({ codigo: 'SENDA-411-1-7', desarrollo: 'SENDA', fechaEntrega: '2026-12-19', // la N real se fuerza con numMeses en el test precioSinAcabados: 2451750, precioConAcabados: 2451750, }) it('semi-contado 50/45/5 con desc 1% default', () => { const p = calcularPropuesta(casa, 'SEMI_CONTADO', false, 0) // N real depende de hoy; validamos montos que no dependen de N expect(p.precioFinal).toBeCloseTo(2427232.5, 2) expect(p.enganche).toBeCloseTo(1213616.25, 2) expect(p.mensualidadesTotal).toBeCloseTo(1092254.625, 3) expect(p.pagoFinal).toBeCloseTo(121361.625, 3) }) it('bancario 5/5/90 con N forzada = 4', () => { const [p] = calcularPropuestas({ unidad: casa, acabados: false, apartado: 0, descuentos: DESC_CERO, numMeses: 4, }) expect(p?.enganche).toBeCloseTo(122587.5, 2) expect(p?.mensualidad).toBeCloseTo(30646.875, 3) expect(p?.pagoFinal).toBeCloseTo(2206575, 2) }) }) describe('SENDA — unidad sin fecha de entrega', () => { it('N = null, mensualidades "—" y total pagado null', () => { const sinFecha = unidad({ codigo: 'SENDA-411-25-A101', desarrollo: 'SENDA', fechaEntrega: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, }) const p = calcularPropuesta(sinFecha, 'BANCARIO', false, APARTADO_DEFAULT) expect(p.numMensualidades).toBeNull() expect(p.mensualidad).toBeNull() expect(p.notaMensualidades).toBe('sin fecha de entrega') expect(totalPagado(p, APARTADO_DEFAULT)).toBeNull() expect(p.enganche).toBeCloseTo(0.05 * 1948000 - APARTADO_DEFAULT, 2) expect(p.pagoFinal).toBeCloseTo(0.9 * 1948000, 2) }) }) describe('SOLANA — casa 6 (L = 2,560,000, mesesParaFirma = 1)', () => { const casa6 = unidad({ codigo: 'SOLANA-6', desarrollo: 'SOLANA', precioSinAcabados: 2560000, precioConAcabados: 2560000, mesesParaFirma: 1, }) it('INFONAVIT con enganche 0% (guarda: no resta el apartado)', () => { const p = calcularPropuesta(casa6, 'INFONAVIT', false, APARTADO_DEFAULT) expect(p.enganche).toBe(0) expect(p.numMensualidades).toBe(1) expect(p.mensualidadesTotal).toBeCloseTo(128000, 2) expect(p.pagoFinal).toBeCloseTo(2432000, 2) }) it('BANCARIO 5% sobre final', () => { const p = calcularPropuesta(casa6, 'BANCARIO', false, APARTADO_DEFAULT) expect(p.enganche).toBeCloseTo(108000, 2) expect(p.mensualidadesTotal).toBeCloseTo(128000, 2) expect(p.pagoFinal).toBeCloseTo(2304000, 2) }) })