From 61e0f5dbe5d16f2759c2907311360ba86638e477 Mon Sep 17 00:00:00 2001 From: Carlos Narro Date: Sat, 30 May 2026 12:20:10 +0200 Subject: [PATCH] feat: resolve unit price from catalog with selection override Co-Authored-By: Claude Sonnet 4.6 --- mvp/b2c/src/budget/resolve.ts | 18 ++++++++++++++++++ mvp/b2c/tests/budget/resolve.test.ts | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 mvp/b2c/src/budget/resolve.ts create mode 100644 mvp/b2c/tests/budget/resolve.test.ts diff --git a/mvp/b2c/src/budget/resolve.ts b/mvp/b2c/src/budget/resolve.ts new file mode 100644 index 0000000..bfc3291 --- /dev/null +++ b/mvp/b2c/src/budget/resolve.ts @@ -0,0 +1,18 @@ +import type { Calidad, CategoriaMaterial, CatalogItem } from './types'; + +export function resolvePrecioUnitario( + categoria: CategoriaMaterial, + calidad: Calidad, + catalog: CatalogItem[], + selections: Partial>, +): { item: CatalogItem | null } { + const selectedId = selections[categoria]; + if (selectedId) { + const selected = catalog.find((c) => c.id === selectedId); + if (selected) return { item: selected }; + } + const def = catalog.find( + (c) => c.categoria === categoria && c.calidad === calidad && c.esDefault, + ); + return { item: def ?? null }; +} diff --git a/mvp/b2c/tests/budget/resolve.test.ts b/mvp/b2c/tests/budget/resolve.test.ts new file mode 100644 index 0000000..a36fb41 --- /dev/null +++ b/mvp/b2c/tests/budget/resolve.test.ts @@ -0,0 +1,25 @@ +import { describe, it, expect } from 'vitest'; +import { resolvePrecioUnitario } from '@/budget/resolve'; +import type { CatalogItem } from '@/budget/types'; + +const catalog: CatalogItem[] = [ + { id: 's-media', categoria: 'suelo', nombre: 'Cerámico medio', calidad: 'media', precioUnit: 2800, unidad: 'm2', descriptorRender: 'suelo cerámico gris', esDefault: true, sku: 'SUE-M' }, + { id: 's-premium', categoria: 'suelo', nombre: 'Porcelánico roble', calidad: 'premium', precioUnit: 4500, unidad: 'm2', descriptorRender: 'porcelánico símil roble', esDefault: true, sku: 'SUE-P' }, +]; + +describe('resolvePrecioUnitario', () => { + it('devuelve el default de la calidad cuando no hay selección', () => { + const { item } = resolvePrecioUnitario('suelo', 'media', catalog, {}); + expect(item?.id).toBe('s-media'); + }); + + it('prioriza la selección exacta sobre la calidad global', () => { + const { item } = resolvePrecioUnitario('suelo', 'media', catalog, { suelo: 's-premium' }); + expect(item?.id).toBe('s-premium'); + }); + + it('devuelve null si no hay default para esa calidad ni selección', () => { + const { item } = resolvePrecioUnitario('pared', 'media', catalog, {}); + expect(item).toBeNull(); + }); +});