Files
reformix-hackaton/mvp/b2c/tests/budget/derive.test.ts
Goyo Cancio 2e3cd78216 Añade impermeabilización, extras fijos y zonas al motor de presupuesto
Acerca el cálculo a tarifas de mercado sin rehacer el modelo lineal €/m²:
- Impermeabilización como partida propia en zonas húmedas (cocina/baño/integral)
- Extras fijos que no escalan con m²: boletín (siempre), tuberías (piso anterior
  a 2000) y cambio de distribución (mover inodoro/ducha/bañera)
- Intensidad por tipo en fontanería/electricidad (baseline cocina) para que un
  integral no escale como un baño
- Factor de zona por provincia en tramos (Madrid/BCN 1.40, islas 1.30, capitales
  1.20, rural 0.85, resto 1.00)
- 2 preguntas nuevas en el formulario del cliente para disparar los extras
- Panel de precios: campo de impermeabilización + sección de extras fijos
- Seed recalibrado (mano de obra, extras, catálogo suelo/pared)
- Migración 0009 (leads.anterior_a_2000, leads.cambio_distribucion, pricing_config.extras)
- Tests del motor ampliados (impermeabilización, extras, intensidad por tipo)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 14:02:57 +02:00

51 lines
1.7 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { deriveCantidades } from '@/budget/derive';
import type { BudgetInputs, PricingConfig } from '@/budget/types';
const config: PricingConfig = {
alturaTechoDefault: 2.5,
factorZona: {},
manoObra: { demolicion: 0, impermeabilizacion: 0, fontaneria: 0, electricidad: 0, mano_de_obra: 0 },
};
function inputs(partial: Partial<BudgetInputs>): BudgetInputs {
return {
tipoReforma: 'cocina',
m2Suelo: null,
alturaTecho: null,
calidadGlobal: 'media',
estructural: false,
provincia: null,
materialSelections: {},
...partial,
};
}
describe('deriveCantidades', () => {
it('usa m² aportados y deriva perímetro y paredes con números limpios', () => {
// m2Suelo=16 -> sqrt=4 -> perimetro=16 -> pared=16*2.5=40 -> mobiliario=16*0.5=8 (cocina)
const c = deriveCantidades(inputs({ m2Suelo: 16 }), config);
expect(c.m2Suelo).toBe(16);
expect(c.perimetro).toBe(16);
expect(c.m2Pared).toBe(40);
expect(c.mlMobiliario).toBe(8);
expect(c.alturaTecho).toBe(2.5);
});
it('cae a la mediana por tipo cuando no hay m²', () => {
const c = deriveCantidades(inputs({ tipoReforma: 'bano', m2Suelo: null }), config);
expect(c.m2Suelo).toBe(5); // mediana baño
});
it('usa la altura aportada por encima del default', () => {
const c = deriveCantidades(inputs({ m2Suelo: 16, alturaTecho: 3 }), config);
expect(c.alturaTecho).toBe(3);
expect(c.m2Pared).toBe(48); // 16 * 3
});
it('no calcula mobiliario para tipos sin cocina/baño', () => {
const c = deriveCantidades(inputs({ tipoReforma: 'salon', m2Suelo: 16 }), config);
expect(c.mlMobiliario).toBe(0);
});
});