diff --git a/api-gateway/README.md b/api-gateway/README.md index 5284e9b..eedead6 100644 --- a/api-gateway/README.md +++ b/api-gateway/README.md @@ -27,7 +27,7 @@ npm test # vitest: fórmulas del cotizador (fixture U-101), cobranza, monday.s | Método | Ruta | Descripción | |---|---|---| | GET | `/api/health` | `{status, monday: 'up'|'down'}` | -| GET | `/api/unidades` | Inventario desde Monday (`?disponibles=true` filtra) | +| GET | `/api/unidades` | Inventario desde Monday (`?disponibles=true` filtra; `?desarrollo=` filtra por DIAGONAL/ATELIER/ESTANZA/SENDA/SOLANA) | | GET | `/api/unidades/:codigo` | Una unidad (404 si no existe) | | POST | `/api/cotizaciones` | Calcula propuesta (server-side), persiste y crea Oportunidad en Monday | | GET | `/api/cotizaciones` · `/api/cotizaciones/:id` | Consulta | diff --git a/api-gateway/knowledge/catalogo-atelier.md b/api-gateway/knowledge/catalogo-atelier.md new file mode 100644 index 0000000..7b734a0 --- /dev/null +++ b/api-gateway/knowledge/catalogo-atelier.md @@ -0,0 +1,23 @@ +# Catálogo de unidades — Desarrollo ATELIER + +ATELIER es un desarrollo de **departamentos** de Frasa Desarrollos (manzana 32, lote 1). Inventario: 34 departamentos; al 21-ago-2026 solo **4 están DISPONIBLES** (deptos 10, 17, 27 y 34), 1 vendido y 29 escriturados. Entrega inmediata: la obra ya terminó, por lo que las mensualidades son **un solo pago** en todas las propuestas. + +## Modelos + +- **SOHO** (prototipos E-A / E-B) — ~52–53 m² habitables. Precios de lista entre $2,059,713 y $2,666,831 MXN. +- **TRIBECA** (prototipos 2R-A / 2R-B / 2R-C) — ~75–83 m² habitables. Precios entre $2,985,326 y $4,451,040 MXN. Los 4 departamentos disponibles son TRIBECA ($3,750,500 a $4,250,600). + +ATELIER tiene **precio único** por departamento: no existe distinción con/sin acabados (cocina y clóset). El apartado estándar es $20,000 MXN y se descuenta del enganche. Cuota de mantenimiento: pendiente de definir. + +## Esquema de propuestas + +Las 4 propuestas de ATELIER son **CONTADO, SEMI CONTADO, COFINAVIT y BANCARIO** (no hay Infonavit). Descuentos por defecto: 0% en las cuatro (editables en el cotizador). N = 1 pago siempre. + +- **Contado** — enganche 90% del precio final menos apartado; pago final 10%. Sin mensualidades. +- **Semi contado** — enganche 50% del precio final menos apartado; mensualidades 40% en 1 pago; pago final 10%. +- **Cofinavit** — enganche 2.5% del precio final menos apartado; mensualidades 2.5% en 1 pago; pago final 95%. (Es la estructura del Infonavit de DIAGONAL pero calculada sobre precio final, sin el quirk del precio de lista.) +- **Bancario** — enganche 5% del **precio de lista** menos apartado; mensualidades = remanente (precio final − pago final − enganche − apartado) en 1 pago; pago final 90% del precio final. + +Ejemplo validado (depto 10, precio $3,997,500, apartado $20,000): Contado enganche $3,577,750 / pago final $399,750; Semi enganche $1,978,750 / mensualidad $1,599,000 / pago final $399,750; Cofinavit enganche $79,937.50 / mensualidad $99,937.50 / pago final $3,797,625; Bancario enganche $179,875 / mensualidad $199,875 / pago final $3,597,750. + +Vigencia de la cotización: 3 días naturales; 7 días para formalizar tras el apartado. No incluye gastos de escrituración. diff --git a/api-gateway/knowledge/catalogo-estanza.md b/api-gateway/knowledge/catalogo-estanza.md new file mode 100644 index 0000000..3cd861e --- /dev/null +++ b/api-gateway/knowledge/catalogo-estanza.md @@ -0,0 +1,23 @@ +# Catálogo de unidades — Desarrollo ESTANZA + +ESTANZA es un desarrollo de **casas** de Frasa Desarrollos (manzana 92, lote 32). Inventario: 64 casas; al 21-ago-2026 hay **13 DISPONIBLES** (9 DUPLO A y 4 DUPLO B), 2 apartadas, 5 vendidas y 44 escrituradas. + +## Modelos + +- **DUPLO A** — 69.06 m² de construcción (63.99 m² habitables), terreno tipo 56.17 m², 1 cajón. Precios entre $2,248,242 y $2,752,486 MXN. +- **DUPLO B** — 69.18 m² de construcción (64.05 m² habitables), terreno tipo 63.83 m², 1 cajón. Precios entre $2,241,564 y $2,653,052 MXN. + +Las casas disponibles van de $2,345,652 a $2,581,017 MXN. ESTANZA tiene **precio único** por casa (sin distinción de acabados). Apartado estándar $20,000 MXN (se descuenta del enganche). Cuota de mantenimiento: pendiente de definir. 7 casas marcadas "R.U.V." aplican a Infonavit tradicional. + +## Esquema de propuestas + +Las mismas **4 propuestas que DIAGONAL** (Bancario, Infonavit-Cofinavit, Contado, Semi contado), con descuentos por defecto 0% en las cuatro (editables). El plazo N es fijo: **1 mensualidad** (terminación de obra = 1 mes en todo el inventario). + +- **Bancario** — enganche 2.5% del precio final menos apartado; mensualidades 7.5% del precio final ÷ N; **pago final 90% del precio de LISTA** (quirk heredado: con descuento > 0 la suma no cuadra, así está en el Excel). +- **Infonavit-Cofinavit** — enganche 2.5% del precio final menos apartado; mensualidades 2.5% del precio final ÷ N; **pago final 95% del precio de LISTA** (mismo quirk). +- **Contado** — enganche 90% del precio final menos apartado; pago final 10%. Sin mensualidades. +- **Semi contado** — enganche 50% del precio final menos apartado; mensualidades 40% del precio final ÷ N; pago final 10%. + +Ejemplo validado (casa 52, precio $2,437,250, apartado $20,000, N=1): Bancario enganche $40,931.25 / mensualidad $182,793.75 / pago final $2,193,525; Infonavit enganche $40,931.25 / mensualidad $60,931.25 / pago final $2,315,387.50; Contado enganche $2,173,525 / pago final $243,725; Semi contado enganche $1,198,625 / mensualidad $974,900 / pago final $243,725. + +Vigencia de la cotización: 3 días naturales; 7 días para formalizar. No incluye gastos de escrituración. diff --git a/api-gateway/knowledge/catalogo-senda.md b/api-gateway/knowledge/catalogo-senda.md new file mode 100644 index 0000000..4bf0d65 --- /dev/null +++ b/api-gateway/knowledge/catalogo-senda.md @@ -0,0 +1,27 @@ +# Catálogo de unidades — Desarrollo SENDA + +SENDA es un desarrollo de **casas** de Frasa Desarrollos. Los códigos de unidad tienen la forma `manzana-lote-casa` (ej. 411-1-7 = manzana 411, lote 1, casa 7). Inventario: **139 casas**; al 19-ago-2026 hay **43 DISPONIBLES**, 1 apartada, 56 vendidas y 39 bloqueadas. + +## Modelos + +- **HIMALAYA** (prototipo UNE-C) — 69.33 m² de construcción. Precios $2,287,000 – $2,468,483 MXN. 14 disponibles. +- **SANTIAGO** (prototipo LAUREL) — 63.95 m². Precios $2,450,000 – $2,750,000 MXN. 6 disponibles. +- **ESMERALDA PB / PA** (prototipo CUADRUPLEX) — 55.2 / 55.78 m². Precio único $1,948,000 (PB) y $1,850,000 (PA). 23 disponibles. +- **SANTA CRUZ** (prototipo DUPLO) — 2 unidades, ambas vendidas. + +Las casas disponibles van de $1,850,000 a $2,700,000 MXN. SENDA tiene **precio único** (precio preventa; sin distinción de acabados). Apartado estándar $20,000 MXN, que se descuenta del enganche. Cuota de mantenimiento: pendiente de definir. + +## Esquema de propuestas + +Las 4 propuestas (Bancario, Infonavit-Cofinavit, Contado, Semi contado) calculan **todo sobre el precio final** (sin el quirk del precio de lista de DIAGONAL): + +- **Bancario** — enganche **5%** del precio final menos apartado; mensualidades 5% del precio final ÷ N; pago final 90%. Descuento default 0% (editable). +- **Infonavit-Cofinavit** — enganche 2.5% del precio final menos apartado; mensualidades 2.5% ÷ N; pago final 95%. Descuento default 0% (editable). +- **Contado** — descuento **2% fijo**; enganche 90% del precio final menos apartado; pago final 10%. Sin mensualidades. +- **Semi contado** — descuento **1% fijo**; esquema **50 / 45 / 5**: enganche 50% del precio final menos apartado, mensualidades 45% ÷ N, pago final **5%** (en DIAGONAL es 40% y 10%). + +N = meses aproximados a la escritura, calculado como (fecha de entrega − hoy) / 30.4 días. **Ojo:** las unidades que aún no tienen fecha de terminación de obra (todas las CUADRUPLEX 411-25-* y varias más) no permiten calcular mensualidades; el cotizador muestra "—" en ese concepto, pero enganche y pago final sí se calculan. + +Ejemplo validado (casa 411-1-7, precio $2,451,750, N=4, sin apartado registrado): Bancario enganche $122,587.50 / mensualidad $30,646.88 / pago final $2,206,575; Infonavit enganche $61,293.75 / mensualidad $15,323.44 / pago final $2,329,162.50; Contado (2% desc) precio final $2,402,715, enganche $2,162,443.50, pago final $240,271.50; Semi (1% desc) precio final $2,427,232.50, enganche $1,213,616.25, mensualidad $273,063.66, pago final $121,361.63. + +Vigencia de la cotización: 3 días naturales; 7 días para formalizar tras el apartado. No incluye gastos de escrituración. diff --git a/api-gateway/knowledge/catalogo-solana.md b/api-gateway/knowledge/catalogo-solana.md new file mode 100644 index 0000000..9f535dc --- /dev/null +++ b/api-gateway/knowledge/catalogo-solana.md @@ -0,0 +1,25 @@ +# Catálogo de unidades — Desarrollo SOLANA + +SOLANA es un desarrollo de **casas** de Frasa Desarrollos en dos etapas: SOLANA I (ubicaciones 1A–5A) y SOLANA II (ubicaciones 6–10). Inventario: **10 casas**; al 21-ago-2026 quedan **3 DISPONIBLES** (casas 6, 7 y 9, todas MIRADOR de etapa SOLANA II), 3 vendidas y 4 escrituradas. + +## Modelos + +- **PANORAMA** (prototipo UNE-C) — 69.33 m² de construcción, 2 cajones. Precios $2,767,518 – $2,990,445 MXN (etapa SOLANA I; sin disponibles). +- **MIRADOR** (prototipos LAUREL, DUPLO-A, DUPLO-B) — 56.17 – 69.33 m². Precios $2,560,000 – $3,400,000 MXN. Las 3 disponibles ($2,560,000 a $2,605,000) son MIRADOR. + +SOLANA tiene **precio único** (precio preventa; sin distinción de acabados). Apartado estándar $20,000 MXN, que se descuenta del enganche. Cuota de mantenimiento: pendiente. + +## Esquema de propuestas + +Las 4 propuestas (Bancario, Infonavit, Contado, Semi contado) con descuentos por defecto 0% (editables; cada propuesta admite dos conceptos de descuento que se suman). Todo se calcula sobre el **precio final** (sin el quirk del precio de lista de DIAGONAL): + +- **Bancario** — enganche **5%** del precio final menos apartado; mensualidades 5% del precio final ÷ N; pago final 90%. +- **Infonavit** — **enganche 0%** (no se descuenta apartado porque no hay enganche); mensualidades 5% del precio final ÷ N; pago final 95%. +- **Contado** — enganche 90% del precio final menos apartado; pago final 10%. Sin mensualidades. +- **Semi contado** — enganche 50% del precio final menos apartado; mensualidades 40% ÷ N; pago final 10%. + +N no viene de una fecha de entrega: es el dato **"meses para firma aprox"** del inventario, propio de cada casa (1 a 4 meses). + +Ejemplo validado (casa 6, precio $2,560,000, N=1, apartado $20,000): Bancario enganche $108,000 / mensualidad $128,000 / pago final $2,304,000; Infonavit enganche $0 / mensualidad $128,000 / pago final $2,432,000; Contado enganche $2,284,000 / pago final $256,000; Semi contado enganche $1,260,000 / mensualidad $1,024,000 / pago final $256,000. + +Vigencia de la cotización: 3 días naturales; 7 días para formalizar tras el apartado. No incluye gastos de escrituración. diff --git a/api-gateway/knowledge/propuestas-cotizador.md b/api-gateway/knowledge/propuestas-cotizador.md index 14e99bc..948fdc5 100644 --- a/api-gateway/knowledge/propuestas-cotizador.md +++ b/api-gateway/knowledge/propuestas-cotizador.md @@ -1,15 +1,22 @@ # Las 4 propuestas del cotizador Frasa -El cotizador genera siempre 4 propuestas comerciales para la misma unidad. Todas parten del **precio de lista** (sin o con acabados, según lo que elija el cliente) y del **apartado** (por defecto $20,000 MXN), que ya se pagó y se descuenta del enganche. +El cotizador genera 4 propuestas comerciales para la misma unidad. Todas parten del **precio de lista** (sin o con acabados, según lo que elija el cliente; en ATELIER, ESTANZA, SENDA y SOLANA hay un solo precio) y del **apartado** (por defecto $20,000 MXN), que ya se pagó y se descuenta del enganche. + +**Este documento describe el esquema de DIAGONAL.** Los demás desarrollos usan la misma estructura pero con porcentajes y reglas propios; ver el catálogo de cada desarrollo: + +- **ATELIER** — Contado / Semi contado / **Cofinavit** (no hay Infonavit) / Bancario. Bancario con enganche 5% sobre precio de lista. Entrega inmediata: las mensualidades son 1 solo pago. Descuentos default 0%. +- **ESTANZA** — las mismas 4 de DIAGONAL, pero el enganche se calcula sobre el precio final y el **pago final de Bancario/Infonavit sobre el precio de lista**; N fijo = 1 mensualidad. Descuentos default 0%. +- **SENDA** — Bancario con enganche 5%; Semi contado 50/45/**5**; Contado con 2% y Semi con 1% de descuento default; todo sobre precio final (sin quirk de lista). Unidades sin fecha de entrega: no se pueden calcular mensualidades ("—"). +- **SOLANA** — Bancario con enganche 5%; **Infonavit con enganche 0%**; N = "meses para firma" de cada casa (1–4). Descuentos default 0%. Elementos comunes a las 4 propuestas: -- **Descuento**: fracción sobre el precio de lista. Valores por defecto: Bancario 0%, Infonavit-Cofinavit 0%, Contado 2%, Semi-contado 1%. +- **Descuento**: fracción sobre el precio de lista. Valores por defecto en DIAGONAL: Bancario 0%, Infonavit-Cofinavit 0%, Contado 2%, Semi-contado 1%. - **Monto de descuento** = precio de lista × descuento. - **Precio final** = precio de lista − monto de descuento. - **Número de mensualidades (N)**: meses completos desde hoy hasta la fecha de entrega de la unidad (mínimo 1). Las mensualidades se pagan durante la construcción. -## Propuesta 1 — Crédito Bancario +## Propuesta 1 — Crédito Bancario (DIAGONAL) Para clientes que liquidarán con crédito hipotecario bancario a la entrega. @@ -17,7 +24,7 @@ Para clientes que liquidarán con crédito hipotecario bancario a la entrega. - Pago final (contra entrega, cubierto por el crédito) = 90% del precio final con descuento. - Mensualidades = precio final − apartado − enganche − pago final, dividido entre N. -## Propuesta 2 — Crédito Infonavit / Cofinavit +## Propuesta 2 — Crédito Infonavit / Cofinavit (DIAGONAL) Para clientes que liquidarán con crédito Infonavit o Cofinavit (Infonavit + banco) a la entrega. @@ -25,7 +32,7 @@ Para clientes que liquidarán con crédito Infonavit o Cofinavit (Infonavit + ba - Pago final (contra entrega, cubierto por el crédito) = 95% del precio final con descuento. - Mensualidades = precio de **lista** − pago final − apartado − enganche, dividido entre N. (Ojo: el Excel del cotizador usa aquí el precio de lista, no el precio final; así está validado y así se calcula.) -## Propuesta 3 — Contado +## Propuesta 3 — Contado (DIAGONAL) Sin mensualidades: el cliente paga casi todo de inmediato. @@ -33,7 +40,7 @@ Sin mensualidades: el cliente paga casi todo de inmediato. - Pago final = 10% del precio final con descuento. - Mensualidad: no aplica (N/A). -## Propuesta 4 — Semi-contado +## Propuesta 4 — Semi-contado (DIAGONAL) Esquema 50 / 40 / 10 sobre el precio final con descuento. diff --git a/api-gateway/scripts/importar-unidades.ts b/api-gateway/scripts/importar-unidades.ts new file mode 100644 index 0000000..b0701a7 --- /dev/null +++ b/api-gateway/scripts/importar-unidades.ts @@ -0,0 +1,189 @@ +/** + * Importa los inventarios de ATELIER, ESTANZA, SENDA y SOLANA al board + * "Unidades" de Monday, desde reference/cotizadores/{DEV}.json. + * + * - Idempotente: salta items cuyo nombre (código prefijado) ya existe. + * - Crea la columna "Meses para firma" (numbers) si falta (la usa SOLANA). + * - create_labels_if_missing: la columna status "Disponibilidad" gana las + * etiquetas nuevas (VENDIDO, ESCRITURADO, APARTADO, BLOQUEADO). + * - Delay entre mutaciones para cuidar el rate limit. + * + * Uso: npx tsx scripts/importar-unidades.ts (desde api-gateway/) + */ +import { readFile } from 'node:fs/promises' +import path from 'node:path' +import { config } from '../src/config/index.js' +import { MondayService } from '../src/services/monday.service.js' + +const REF_DIR = path.resolve(process.cwd(), '..', 'reference', 'cotizadores') +const DELAY_MS = 400 + +const sleep = (ms: number): Promise => new Promise((r) => setTimeout(r, ms)) + +interface UnidadJson { + codigo: string + modelo: string | null + fechaEntrega: string | null + supEstacionamiento: number | null + supConstruccion: number | null + supExcedente: number | null + status: string + extras: Record +} + +interface DefinicionDesarrollo { + id: string + archivo: string + codigoMonday: (u: UnidadJson) => string + precio: (u: UnidadJson) => number | null + mesesParaFirma?: (u: UnidadJson) => number | null +} + +const DESARROLLOS: DefinicionDesarrollo[] = [ + { + id: 'ATELIER', + archivo: 'ATELIER.json', + codigoMonday: (u) => `ATELIER-${u.codigo}`, + precio: (u) => (typeof u.extras['precioLista'] === 'number' ? u.extras['precioLista'] : null), + }, + { + id: 'ESTANZA', + archivo: 'ESTANZA.json', + codigoMonday: (u) => `ESTANZA-${u.codigo.replace(/^CASA-/, '')}`, + precio: (u) => (typeof u.extras['precioLista'] === 'number' ? u.extras['precioLista'] : null), + }, + { + id: 'SENDA', + archivo: 'SENDA.json', + codigoMonday: (u) => `SENDA-${u.codigo}`, + precio: (u) => (typeof u.extras['precioPreventa'] === 'number' ? u.extras['precioPreventa'] : null), + }, + { + id: 'SOLANA', + archivo: 'SOLANA.json', + codigoMonday: (u) => `SOLANA-${u.codigo}`, + precio: (u) => (typeof u.extras['precioPreventa'] === 'number' ? u.extras['precioPreventa'] : null), + mesesParaFirma: (u) => + typeof u.extras['mesesParaFirma'] === 'number' ? u.extras['mesesParaFirma'] : null, + }, +] + +const svc = new MondayService() +const boardId = config.monday.boards.unidades + +/** Columnas del board mapeadas por título */ +async function columnasBoard(): Promise> { + const data = await svc.graphql<{ boards: { columns: { id: string; title: string; type: string }[] }[] }>( + `query ($ids: [ID!]) { boards(ids: $ids) { columns { id title type } } }`, + { ids: [boardId] }, + ) + return new Map((data.boards[0]?.columns ?? []).map((c) => [c.title, c])) +} + +async function asegurarColumnaMeses(): Promise { + const cols = await columnasBoard() + if (cols.has('Meses para firma')) return + const data = await svc.graphql<{ create_column: { id: string } }>( + `mutation ($boardId: ID!, $title: String!) { + create_column(board_id: $boardId, title: $title, column_type: numbers) { id } + }`, + { boardId, title: 'Meses para firma' }, + ) + console.log(`Columna "Meses para firma" creada (${data.create_column.id})`) +} + +/** Nombres de TODOS los items del board (paginado por cursor) */ +async function itemsExistentes(): Promise> { + const nombres = new Set() + let cursor: string | null = null + do { + const data: { boards: { items_page: { cursor: string | null; items: { name: string }[] } }[] } = + cursor === null + ? await svc.graphql( + `query ($ids: [ID!]) { boards(ids: $ids) { items_page(limit: 500) { cursor items { name } } } }`, + { ids: [boardId] }, + ) + : await svc.graphql( + `query ($ids: [ID!], $cursor: String!) { + boards(ids: $ids) { items_page(limit: 500, cursor: $cursor) { cursor items { name } } } + }`, + { ids: [boardId], cursor }, + ) + const page = data.boards[0]?.items_page + if (!page) break + for (const item of page.items) nombres.add(item.name) + cursor = page.cursor + } while (cursor !== null) + return nombres +} + +function columnValuesDe( + def: DefinicionDesarrollo, + u: UnidadJson, + cols: Map, +): string { + const valores: Record = {} + const poner = (titulo: string, valor: unknown) => { + const col = cols.get(titulo) + if (!col) throw new Error(`Columna "${titulo}" no existe en el board`) + valores[col.id] = valor + } + const precio = def.precio(u) + poner('Desarrollo', def.id) + poner('Modelo', u.modelo ?? '') + if (u.fechaEntrega) poner('Fecha entrega', { date: u.fechaEntrega }) + if (u.supConstruccion !== null) poner('Sup. construcción', u.supConstruccion) + if (u.supExcedente !== null) poner('Sup. excedente', u.supExcedente) + if (u.supEstacionamiento !== null) poner('Sup. estacionamiento', u.supEstacionamiento) + if (precio !== null) { + // Desarrollos de precio único: ambos precios replican el precio de lista + poner('Precio sin acabados', precio) + poner('Precio con acabados', precio) + } + poner('Disponibilidad', { label: u.status }) + const meses = def.mesesParaFirma?.(u) + if (meses !== null && meses !== undefined) poner('Meses para firma', meses) + return JSON.stringify(valores) +} + +async function main(): Promise { + await asegurarColumnaMeses() + const cols = await columnasBoard() + const existentes = await itemsExistentes() + console.log(`Board ${boardId}: ${existentes.size} items existentes`) + + for (const def of DESARROLLOS) { + const raw = JSON.parse(await readFile(path.join(REF_DIR, def.archivo), 'utf8')) as { + unidades: UnidadJson[] + } + let creados = 0 + let saltados = 0 + for (const u of raw.unidades) { + const nombre = def.codigoMonday(u) + if (existentes.has(nombre)) { + saltados++ + continue + } + const columnValues = columnValuesDe(def, u, cols) + await svc.graphql<{ create_item: { id: string } }>( + `mutation ($boardId: ID!, $itemName: String!, $columnValues: JSON!) { + create_item(board_id: $boardId, item_name: $itemName, column_values: $columnValues, create_labels_if_missing: true) { id } + }`, + { boardId, itemName: nombre, columnValues }, + ) + existentes.add(nombre) + creados++ + if (creados % 25 === 0) console.log(` ${def.id}: ${creados} creados…`) + await sleep(DELAY_MS) + } + console.log(`${def.id}: ${creados} creados, ${saltados} ya existían (total ${raw.unidades.length})`) + } + svc.invalidarCacheUnidades() + console.log('Importación terminada.') + process.exit(0) +} + +main().catch((err) => { + console.error('Falló la importación:', err instanceof Error ? err.message : err) + process.exit(1) +}) diff --git a/api-gateway/src/controllers/cotizaciones.controller.ts b/api-gateway/src/controllers/cotizaciones.controller.ts index 91e4886..9a208c4 100644 --- a/api-gateway/src/controllers/cotizaciones.controller.ts +++ b/api-gateway/src/controllers/cotizaciones.controller.ts @@ -12,7 +12,7 @@ const crearCotizacionSchema = z.object({ clienteNombre: z.string().min(1), clienteEmail: z.string().email().optional(), clienteTelefono: z.string().min(7).optional(), - tipoPropuesta: z.enum(['BANCARIO', 'INFONAVIT', 'CONTADO', 'SEMI_CONTADO']), + tipoPropuesta: z.enum(['BANCARIO', 'INFONAVIT', 'COFINAVIT', 'CONTADO', 'SEMI_CONTADO']), descuento: z.number().min(0).max(1).optional(), // fracción (0.02 = 2%) }) @@ -47,7 +47,7 @@ export async function crearCotizacion(req: Request, res: Response): Promise plazo desconocido (sin fecha de entrega) mensualidad: propuesta.mensualidad, pagoFinal: propuesta.pagoFinal, }, diff --git a/api-gateway/src/controllers/unidades.controller.ts b/api-gateway/src/controllers/unidades.controller.ts index 20b57a7..0b90b38 100644 --- a/api-gateway/src/controllers/unidades.controller.ts +++ b/api-gateway/src/controllers/unidades.controller.ts @@ -2,11 +2,16 @@ import type { Request, Response } from 'express' import { mondayService } from '../services/monday.service.js' import { NotFoundError } from '../utils/errors.js' -/** GET /api/unidades — ?disponibles=true filtra solo DISPONIBLE */ +/** GET /api/unidades — ?disponibles=true filtra solo DISPONIBLE; ?desarrollo= filtra por desarrollo */ export async function listarUnidades(req: Request, res: Response): Promise { const unidades = await mondayService.getUnidades() const disponibles = req.query['disponibles'] === 'true' - res.json(disponibles ? unidades.filter((u) => u.status === 'DISPONIBLE') : unidades) + const desarrollo = req.query['desarrollo']?.toString().toUpperCase() + let lista = disponibles ? unidades.filter((u) => u.status === 'DISPONIBLE') : unidades + if (desarrollo) { + lista = lista.filter((u) => (u.desarrollo || 'DIAGONAL').toUpperCase() === desarrollo) + } + res.json(lista) } /** GET /api/unidades/:codigo — 404 si no existe */ diff --git a/api-gateway/src/services/cotizacion-desarrollos.service.test.ts b/api-gateway/src/services/cotizacion-desarrollos.service.test.ts new file mode 100644 index 0000000..7665234 --- /dev/null +++ b/api-gateway/src/services/cotizacion-desarrollos.service.test.ts @@ -0,0 +1,166 @@ +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) + }) +}) diff --git a/api-gateway/src/services/cotizacion.service.test.ts b/api-gateway/src/services/cotizacion.service.test.ts index c681449..b8d98bc 100644 --- a/api-gateway/src/services/cotizacion.service.test.ts +++ b/api-gateway/src/services/cotizacion.service.test.ts @@ -21,7 +21,7 @@ const U101: UnidadCotizable = { precioSinAcabados: 4415000, precioConAcabados: 4579000, status: 'DISPONIBLE', - desarrollo: 'SENDA', + desarrollo: 'DIAGONAL', } const N = 14 @@ -130,7 +130,7 @@ describe('totalPagado', () => { describe('indiceMenorTotal', () => { it('contado (2% desc) tiene el menor total pagado', () => { const propuestas = calcular() - const totales = propuestas.map((p) => totalPagado(p, APARTADO_DEFAULT)) + const totales = propuestas.map((p) => totalPagado(p, APARTADO_DEFAULT) ?? 0) expect(indiceMenorTotal(totales)).toBe(2) }) diff --git a/api-gateway/src/services/cotizacion.service.ts b/api-gateway/src/services/cotizacion.service.ts index 8939533..52eee02 100644 --- a/api-gateway/src/services/cotizacion.service.ts +++ b/api-gateway/src/services/cotizacion.service.ts @@ -1,21 +1,24 @@ /** - * Cálculos replicados fielmente del Excel de producción "COTIZADOR" de - * Frasa Desarrollos (port de apps/cotizador/src/lib/calculations.ts). - * NO "corregir" las fórmulas: algunas usan precio de lista donde - * intuitivamente iría precio final; así está en el Excel y así debe quedar. + * Cálculos replicados fielmente de los Excel de producción "COTIZADOR" de + * Frasa Desarrollos (port parametrizado de apps/cotizador/src/lib/calculations.ts + * + src/data/desarrollos.ts). NO "corregir" las fórmulas: algunas usan precio + * de lista donde intuitivamente iría precio final; así está en los Excel y + * así debe quedar. */ export interface UnidadCotizable { codigo: string modelo: string - fechaEntrega: string // ISO 'YYYY-MM-DD' (terminación de obra) + fechaEntrega: string | null // ISO 'YYYY-MM-DD'; null si no aplica o se desconoce supEstacionamiento: number supConstruccion: number supExcedente: number precioSinAcabados: number precioConAcabados: number - status: 'DISPONIBLE' | 'APARTADA' | 'VENDIDA' + status: string // DISPONIBLE | APARTADA | VENDIDA | APARTADO | VENDIDO | ESCRITURADO | BLOQUEADO desarrollo: string + /** Plazo de mensualidades cuando lo fija el inventario (SOLANA: meses para firma) */ + mesesParaFirma?: number | null } export type TipoPropuesta = @@ -33,7 +36,7 @@ export interface DescuentosPropuestas { export const APARTADO_DEFAULT = 20000 -// Descuentos por defecto de cada propuesta (fracción, no porcentaje) +// Descuentos por defecto de DIAGONAL (fracción, no porcentaje) export const DESCUENTOS_DEFAULT: DescuentosPropuestas = { creditoBancario: 0, creditoInfonavit: 0, @@ -41,24 +44,123 @@ export const DESCUENTOS_DEFAULT: DescuentosPropuestas = { semiContado: 0.01, } +// ---------- Parametrización por desarrollo ---------- + +type BasePrecio = 'lista' | 'final' +type ModoMensualidades = 'ninguna' | 'remanente' | 'remanenteLista' | 'porcentaje' + +interface PropuestaConfig { + tipo: TipoPropuesta + claveDescuento: keyof DescuentosPropuestas + titulo: string + descuentoDefault: number + pctEnganche: number + engancheBase: BasePrecio + pctPagoFinal: number + pagoFinalBase: BasePrecio + modoMensualidades: ModoMensualidades + engancheCeroSinApartado?: boolean +} + +interface ParametrosDesarrollo { + id: string + precioUnico: boolean + fuenteDeN: 'fechaEntrega' | 'fijo' | 'porUnidad' + nFijo?: number + propuestas: PropuestaConfig[] +} + +/** + * Espejo de apps/cotizador/src/data/desarrollos.ts (mantener sincronizados). + * Detalle celda por celda en reference/cotizadores/*.md. + */ +export const DESARROLLOS: Record = { + DIAGONAL: { + id: 'DIAGONAL', + precioUnico: false, + fuenteDeN: 'fechaEntrega', + propuestas: [ + { tipo: 'CREDITO_BANCARIO', claveDescuento: 'creditoBancario', titulo: 'Crédito Bancario', descuentoDefault: 0, pctEnganche: 0.025, engancheBase: 'lista', pctPagoFinal: 0.9, pagoFinalBase: 'final', modoMensualidades: 'remanente' }, + { tipo: 'CREDITO_INFONAVIT_COFINAVIT', claveDescuento: 'creditoInfonavit', titulo: 'Crédito Infonavit-Cofinavit', descuentoDefault: 0, pctEnganche: 0.025, engancheBase: 'lista', pctPagoFinal: 0.95, pagoFinalBase: 'final', modoMensualidades: 'remanenteLista' }, + { tipo: 'CONTADO', claveDescuento: 'contado', titulo: 'Contado', descuentoDefault: 0.02, pctEnganche: 0.9, engancheBase: 'final', pctPagoFinal: 0.1, pagoFinalBase: 'final', modoMensualidades: 'ninguna' }, + { tipo: 'SEMI_CONTADO', claveDescuento: 'semiContado', titulo: 'Semi Contado', descuentoDefault: 0.01, pctEnganche: 0.5, engancheBase: 'final', pctPagoFinal: 0.1, pagoFinalBase: 'final', modoMensualidades: 'porcentaje' }, + ], + }, + ATELIER: { + id: 'ATELIER', + precioUnico: true, + fuenteDeN: 'fijo', + nFijo: 1, + propuestas: [ + { tipo: 'CREDITO_BANCARIO', claveDescuento: 'creditoBancario', titulo: 'Crédito Bancario', descuentoDefault: 0, pctEnganche: 0.05, engancheBase: 'lista', pctPagoFinal: 0.9, pagoFinalBase: 'final', modoMensualidades: 'remanente' }, + { tipo: 'CREDITO_INFONAVIT_COFINAVIT', claveDescuento: 'creditoInfonavit', titulo: 'Crédito Cofinavit', descuentoDefault: 0, pctEnganche: 0.025, engancheBase: 'final', pctPagoFinal: 0.95, pagoFinalBase: 'final', modoMensualidades: 'porcentaje' }, + { tipo: 'CONTADO', claveDescuento: 'contado', titulo: 'Contado', descuentoDefault: 0, pctEnganche: 0.9, engancheBase: 'final', pctPagoFinal: 0.1, pagoFinalBase: 'final', modoMensualidades: 'ninguna' }, + { tipo: 'SEMI_CONTADO', claveDescuento: 'semiContado', titulo: 'Semi Contado', descuentoDefault: 0, pctEnganche: 0.5, engancheBase: 'final', pctPagoFinal: 0.1, pagoFinalBase: 'final', modoMensualidades: 'porcentaje' }, + ], + }, + ESTANZA: { + id: 'ESTANZA', + precioUnico: true, + fuenteDeN: 'fijo', + nFijo: 1, + propuestas: [ + { tipo: 'CREDITO_BANCARIO', claveDescuento: 'creditoBancario', titulo: 'Crédito Bancario', descuentoDefault: 0, pctEnganche: 0.025, engancheBase: 'final', pctPagoFinal: 0.9, pagoFinalBase: 'lista', modoMensualidades: 'porcentaje' }, + { tipo: 'CREDITO_INFONAVIT_COFINAVIT', claveDescuento: 'creditoInfonavit', titulo: 'Crédito Infonavit-Cofinavit', descuentoDefault: 0, pctEnganche: 0.025, engancheBase: 'final', pctPagoFinal: 0.95, pagoFinalBase: 'lista', modoMensualidades: 'porcentaje' }, + { tipo: 'CONTADO', claveDescuento: 'contado', titulo: 'Contado', descuentoDefault: 0, pctEnganche: 0.9, engancheBase: 'final', pctPagoFinal: 0.1, pagoFinalBase: 'final', modoMensualidades: 'ninguna' }, + { tipo: 'SEMI_CONTADO', claveDescuento: 'semiContado', titulo: 'Semi Contado', descuentoDefault: 0, pctEnganche: 0.5, engancheBase: 'final', pctPagoFinal: 0.1, pagoFinalBase: 'final', modoMensualidades: 'porcentaje' }, + ], + }, + SENDA: { + id: 'SENDA', + precioUnico: true, + fuenteDeN: 'fechaEntrega', + propuestas: [ + { tipo: 'CREDITO_BANCARIO', claveDescuento: 'creditoBancario', titulo: 'Crédito Bancario', descuentoDefault: 0, pctEnganche: 0.05, engancheBase: 'final', pctPagoFinal: 0.9, pagoFinalBase: 'final', modoMensualidades: 'porcentaje' }, + { tipo: 'CREDITO_INFONAVIT_COFINAVIT', claveDescuento: 'creditoInfonavit', titulo: 'Crédito Infonavit-Cofinavit', descuentoDefault: 0, pctEnganche: 0.025, engancheBase: 'final', pctPagoFinal: 0.95, pagoFinalBase: 'final', modoMensualidades: 'porcentaje' }, + { tipo: 'CONTADO', claveDescuento: 'contado', titulo: 'Contado', descuentoDefault: 0.02, pctEnganche: 0.9, engancheBase: 'final', pctPagoFinal: 0.1, pagoFinalBase: 'final', modoMensualidades: 'ninguna' }, + { tipo: 'SEMI_CONTADO', claveDescuento: 'semiContado', titulo: 'Semi Contado', descuentoDefault: 0.01, pctEnganche: 0.5, engancheBase: 'final', pctPagoFinal: 0.05, pagoFinalBase: 'final', modoMensualidades: 'porcentaje' }, + ], + }, + SOLANA: { + id: 'SOLANA', + precioUnico: true, + fuenteDeN: 'porUnidad', + propuestas: [ + { tipo: 'CREDITO_BANCARIO', claveDescuento: 'creditoBancario', titulo: 'Crédito Bancario', descuentoDefault: 0, pctEnganche: 0.05, engancheBase: 'final', pctPagoFinal: 0.9, pagoFinalBase: 'final', modoMensualidades: 'porcentaje' }, + { tipo: 'CREDITO_INFONAVIT_COFINAVIT', claveDescuento: 'creditoInfonavit', titulo: 'Crédito Infonavit-Cofinavit', descuentoDefault: 0, pctEnganche: 0, engancheBase: 'final', pctPagoFinal: 0.95, pagoFinalBase: 'final', modoMensualidades: 'porcentaje', engancheCeroSinApartado: true }, + { tipo: 'CONTADO', claveDescuento: 'contado', titulo: 'Contado', descuentoDefault: 0, pctEnganche: 0.9, engancheBase: 'final', pctPagoFinal: 0.1, pagoFinalBase: 'final', modoMensualidades: 'ninguna' }, + { tipo: 'SEMI_CONTADO', claveDescuento: 'semiContado', titulo: 'Semi Contado', descuentoDefault: 0, pctEnganche: 0.5, engancheBase: 'final', pctPagoFinal: 0.1, pagoFinalBase: 'final', modoMensualidades: 'porcentaje' }, + ], + }, +} + +const DESARROLLO_DEFAULT = 'DIAGONAL' + +function obtenerDesarrollo(id?: string | null): ParametrosDesarrollo { + return (id && DESARROLLOS[id]) || DESARROLLOS[DESARROLLO_DEFAULT]! +} + export interface PropuestaResultado { tipo: TipoPropuesta + titulo: string descuento: number // fracción (0.02 = 2%) montoDescuento: number precioFinal: number // precio lista - descuento enganche: number // NETO del apartado (el apartado ya se pagó) - numMensualidades: number - mensualidad: number | null // null => N/A (Contado) - mensualidadesTotal: number | null // null => N/A (Contado) + numMensualidades: number | null // null => plazo desconocido (sin fecha de entrega) + mensualidad: number | null // null => N/A (Contado) o plazo desconocido + mensualidadesTotal: number | null // null => N/A (Contado) o plazo desconocido pagoFinal: number + notaMensualidades?: string } export interface CotizacionInput { unidad: UnidadCotizable - acabados: boolean // true => incluye cocina y closet + acabados: boolean // true => incluye cocina y closet (ignorado en precio único) apartado: number descuentos: DescuentosPropuestas - /** Permite forzar N en tests; por defecto se calcula desde fechaEntrega */ + desarrollo?: string + /** Permite forzar N en tests; por defecto se resuelve según la fuenteDeN del desarrollo */ numMeses?: number } @@ -70,6 +172,21 @@ export function calcularNumMensualidades(fechaEntrega: string, hoy: Date = new D return Math.max(1, Math.round((entrega.getTime() - hoy.getTime()) / MS_POR_MES)) } +/** Resuelve N según la fuente del desarrollo (null si no se puede conocer) */ +export function resolverNumMensualidades( + unidad: UnidadCotizable, + params: ParametrosDesarrollo, +): number | null { + switch (params.fuenteDeN) { + case 'fijo': + return params.nFijo ?? 1 + case 'porUnidad': + return unidad.mesesParaFirma ?? null + case 'fechaEntrega': + return unidad.fechaEntrega ? calcularNumMensualidades(unidad.fechaEntrega) : null + } +} + function baseComun(precioLista: number, descuento: number) { // Excel COTIZADOR: MONTO DE DESCUENTO y PRECIO FINAL C/DESC const montoDescuento = precioLista * descuento @@ -77,89 +194,60 @@ function baseComun(precioLista: number, descuento: number) { return { montoDescuento, precioFinal } } -/** PROPUESTA 1 — CRÉDITO BANCARIO */ -function calcularBancario(precioLista: number, apartado: number, descuento: number, n: number): PropuestaResultado { +/** Calcula una propuesta comercial con los parámetros dados */ +function calcularPropuestaConfig( + cfg: PropuestaConfig, + precioLista: number, + apartado: number, + descuento: number, + n: number | null, +): PropuestaResultado { const { montoDescuento, precioFinal } = baseComun(precioLista, descuento) - // Excel COTIZADOR!M17 — enganche 2.5% sobre precio LISTA, neto del apartado - const enganche = 0.025 * precioLista - apartado - // Excel COTIZADOR!M19 — pago final 90% del precio final c/desc - const pagoFinal = 0.9 * precioFinal - // Excel COTIZADOR!M18 — remanente a mensualidades - const mensualidadesTotal = precioFinal - apartado - enganche - pagoFinal + + const baseEnganche = cfg.engancheBase === 'lista' ? precioLista : precioFinal + const enganche = + cfg.engancheCeroSinApartado && cfg.pctEnganche === 0 + ? 0 + : cfg.pctEnganche * baseEnganche - apartado + + const basePagoFinal = cfg.pagoFinalBase === 'lista' ? precioLista : precioFinal + const pagoFinal = cfg.pctPagoFinal * basePagoFinal + + let mensualidadesTotal: number | null = null + let mensualidad: number | null = null + let notaMensualidades: string | undefined + if (cfg.modoMensualidades !== 'ninguna') { + if (n === null) { + notaMensualidades = 'sin fecha de entrega' + } else { + switch (cfg.modoMensualidades) { + case 'remanente': + mensualidadesTotal = precioFinal - apartado - enganche - pagoFinal + break + case 'remanenteLista': + // Quirk DIAGONAL Infonavit: el Excel usa precioLista aquí, NO precioFinal + mensualidadesTotal = precioLista - pagoFinal - apartado - enganche + break + case 'porcentaje': + mensualidadesTotal = precioFinal * (1 - cfg.pctEnganche - cfg.pctPagoFinal) + break + } + mensualidad = mensualidadesTotal / n + } + } + return { - tipo: 'CREDITO_BANCARIO', + tipo: cfg.tipo, + titulo: cfg.titulo, descuento, montoDescuento, precioFinal, enganche, numMensualidades: n, - mensualidad: mensualidadesTotal / n, - mensualidadesTotal, - pagoFinal, - } -} - -/** PROPUESTA 2 — CRÉDITO INFONAVIT-COFINAVIT */ -function calcularInfonavit(precioLista: number, apartado: number, descuento: number, n: number): PropuestaResultado { - const { montoDescuento, precioFinal } = baseComun(precioLista, descuento) - // Excel COTIZADOR!M26 — enganche 2.5% sobre precio LISTA, neto del apartado - const enganche = 0.025 * precioLista - apartado - // Excel COTIZADOR!M28 — pago final 95% del precio final c/desc - const pagoFinal = 0.95 * precioFinal - // Excel COTIZADOR!M27 — OJO: el Excel usa precioLista aquí, NO precioFinal. Replicar tal cual. - const mensualidadesTotal = precioLista - pagoFinal - apartado - enganche - return { - tipo: 'CREDITO_INFONAVIT_COFINAVIT', - descuento, - montoDescuento, - precioFinal, - enganche, - numMensualidades: n, - mensualidad: mensualidadesTotal / n, - mensualidadesTotal, - pagoFinal, - } -} - -/** PROPUESTA 3 — CONTADO (sin mensualidades) */ -function calcularContado(precioLista: number, apartado: number, descuento: number, n: number): PropuestaResultado { - const { montoDescuento, precioFinal } = baseComun(precioLista, descuento) - // Excel COTIZADOR!M36 — enganche 90% del precio final c/desc, neto del apartado - const enganche = 0.9 * precioFinal - apartado - // Excel COTIZADOR!M37 — pago final 10% del precio final c/desc - const pagoFinal = 0.1 * precioFinal - return { - tipo: 'CONTADO', - descuento, - montoDescuento, - precioFinal, - enganche, - numMensualidades: n, - mensualidad: null, // N/A - mensualidadesTotal: null, // N/A - pagoFinal, - } -} - -/** PROPUESTA 4 — SEMI CONTADO */ -function calcularSemiContado(precioLista: number, apartado: number, descuento: number, n: number): PropuestaResultado { - const { montoDescuento, precioFinal } = baseComun(precioLista, descuento) - // Excel COTIZADOR!M45 — enganche 50% del precio final c/desc, neto del apartado - const enganche = 0.5 * precioFinal - apartado - // Excel COTIZADOR!M46 — 40% del precio final a mensualidades (= 1 - 0.5 - 0.1) - const mensualidadesTotal = precioFinal * 0.4 - // Excel COTIZADOR!M47 — pago final 10% del precio final c/desc - const pagoFinal = 0.1 * precioFinal - return { - tipo: 'SEMI_CONTADO', - descuento, - montoDescuento, - precioFinal, - enganche, - numMensualidades: n, - mensualidad: mensualidadesTotal / n, + mensualidad, mensualidadesTotal, pagoFinal, + ...(notaMensualidades !== undefined ? { notaMensualidades } : {}), } } @@ -171,8 +259,10 @@ export function obtenerPrecioLista(unidad: UnidadCotizable, acabados: boolean): /** * Total pagado por el cliente en una propuesta: * apartado + enganche (neto del apartado) + mensualidades + pago final. + * null cuando el total no se puede conocer (plazo desconocido). */ -export function totalPagado(p: PropuestaResultado, apartado: number): number { +export function totalPagado(p: PropuestaResultado, apartado: number): number | null { + if (p.notaMensualidades) return null return apartado + p.enganche + (p.mensualidadesTotal ?? 0) + p.pagoFinal } @@ -188,39 +278,32 @@ export function indiceMenorTotal(totales: number[]): number { return indice } -/** Calcula las 4 propuestas comerciales */ +/** Calcula las propuestas comerciales del desarrollo de la unidad */ export function calcularPropuestas(input: CotizacionInput): PropuestaResultado[] { const { unidad, acabados, apartado, descuentos } = input + const params = obtenerDesarrollo(input.desarrollo ?? unidad.desarrollo ?? DESARROLLO_DEFAULT) const precioLista = obtenerPrecioLista(unidad, acabados) - const n = input.numMeses ?? calcularNumMensualidades(unidad.fechaEntrega) - return [ - calcularBancario(precioLista, apartado, descuentos.creditoBancario, n), - calcularInfonavit(precioLista, apartado, descuentos.creditoInfonavit, n), - calcularContado(precioLista, apartado, descuentos.contado, n), - calcularSemiContado(precioLista, apartado, descuentos.semiContado, n), - ] + const n = input.numMeses ?? resolverNumMensualidades(unidad, params) + return params.propuestas.map((cfg) => + calcularPropuestaConfig(cfg, precioLista, apartado, descuentos[cfg.claveDescuento], n), + ) } /** Mapa entre el nombre del API (body) y el nombre interno de la propuesta */ export const TIPO_PROPUESTA_API = { BANCARIO: 'CREDITO_BANCARIO', INFONAVIT: 'CREDITO_INFONAVIT_COFINAVIT', + COFINAVIT: 'CREDITO_INFONAVIT_COFINAVIT', CONTADO: 'CONTADO', SEMI_CONTADO: 'SEMI_CONTADO', } as const export type TipoPropuestaApi = keyof typeof TIPO_PROPUESTA_API -const DESCUENTO_DEFAULT_POR_TIPO: Record = { - BANCARIO: DESCUENTOS_DEFAULT.creditoBancario, - INFONAVIT: DESCUENTOS_DEFAULT.creditoInfonavit, - CONTADO: DESCUENTOS_DEFAULT.contado, - SEMI_CONTADO: DESCUENTOS_DEFAULT.semiContado, -} - /** - * Calcula UNA propuesta (la elegida por el cliente) para persistir/cotizar. - * `descuento` opcional (fracción); si no viene, usa el default del tipo. + * Calcula UNA propuesta (la elegida por el cliente) para persistir/cotizar, + * con los parámetros del desarrollo de la unidad. + * `descuento` opcional (fracción); si no viene, usa el default de la propuesta. */ export function calcularPropuesta( unidad: UnidadCotizable, @@ -229,17 +312,14 @@ export function calcularPropuesta( apartado: number = APARTADO_DEFAULT, descuento?: number, ): PropuestaResultado { - const precioLista = obtenerPrecioLista(unidad, acabados) - const n = calcularNumMensualidades(unidad.fechaEntrega) - const d = descuento ?? DESCUENTO_DEFAULT_POR_TIPO[tipo] - switch (tipo) { - case 'BANCARIO': - return calcularBancario(precioLista, apartado, d, n) - case 'INFONAVIT': - return calcularInfonavit(precioLista, apartado, d, n) - case 'CONTADO': - return calcularContado(precioLista, apartado, d, n) - case 'SEMI_CONTADO': - return calcularSemiContado(precioLista, apartado, d, n) + const params = obtenerDesarrollo(unidad.desarrollo) + const tipoInterno = TIPO_PROPUESTA_API[tipo] + const cfg = params.propuestas.find((p) => p.tipo === tipoInterno) + if (!cfg) { + throw new Error(`La propuesta ${tipo} no aplica para el desarrollo ${params.id}`) } + const precioLista = obtenerPrecioLista(unidad, acabados) + const n = resolverNumMensualidades(unidad, params) + const d = descuento ?? cfg.descuentoDefault + return calcularPropuestaConfig(cfg, precioLista, apartado, d, n) } diff --git a/api-gateway/src/services/monday.service.test.ts b/api-gateway/src/services/monday.service.test.ts index a26e03e..327a276 100644 --- a/api-gateway/src/services/monday.service.test.ts +++ b/api-gateway/src/services/monday.service.test.ts @@ -79,6 +79,7 @@ describe('MondayService', () => { precioConAcabados: 4579000, status: 'DISPONIBLE', desarrollo: 'SENDA', + mesesParaFirma: null, }) }) diff --git a/api-gateway/src/services/monday.service.ts b/api-gateway/src/services/monday.service.ts index 5e8af81..adb3f64 100644 --- a/api-gateway/src/services/monday.service.ts +++ b/api-gateway/src/services/monday.service.ts @@ -254,17 +254,23 @@ export class MondayService { return Number.isFinite(n) ? n : 0 } const status = textoDe('Disponibilidad').toUpperCase() + // La columna "Meses para firma" es opcional (solo la usa SOLANA) + const colMeses = cols.get('Meses para firma') + const mesesTexto = colMeses ? (porId.get(colMeses.id)?.text ?? '') : '' + const mesesNum = Number(mesesTexto) return { codigo: item.name, modelo: textoDe('Modelo'), - fechaEntrega: textoDe('Fecha entrega'), + fechaEntrega: textoDe('Fecha entrega') || null, supEstacionamiento: numeroDe('Sup. estacionamiento'), supConstruccion: numeroDe('Sup. construcción'), supExcedente: numeroDe('Sup. excedente'), precioSinAcabados: numeroDe('Precio sin acabados'), precioConAcabados: numeroDe('Precio con acabados'), - status: status === 'APARTADA' || status === 'VENDIDA' ? status : 'DISPONIBLE', + // Se conserva el status real del board (VENDIDO, ESCRITURADO, BLOQUEADO, ...) + status: status || 'DISPONIBLE', desarrollo: textoDe('Desarrollo'), + mesesParaFirma: mesesTexto !== '' && Number.isFinite(mesesNum) ? mesesNum : null, } } diff --git a/apps/cotizador/src/components/ComparadorPropuestas.tsx b/apps/cotizador/src/components/ComparadorPropuestas.tsx index 9001dab..11a5415 100644 --- a/apps/cotizador/src/components/ComparadorPropuestas.tsx +++ b/apps/cotizador/src/components/ComparadorPropuestas.tsx @@ -21,7 +21,8 @@ export function ComparadorPropuestas({ precioLista, apartado, propuestas, titulo const [abierto, setAbierto] = useState(false) const totales = propuestas.map((p) => totalPagado(p, apartado)) - const ganador = indiceMenorTotal(totales) + // null (plazo desconocido) nunca gana el menor total + const ganador = indiceMenorTotal(totales.map((t) => t ?? Number.POSITIVE_INFINITY)) const filas: Fila[] = [ { etiqueta: 'Precio de lista', obtener: () => formatoMoneda(precioLista) }, @@ -34,19 +35,30 @@ export function ComparadorPropuestas({ precioLista, apartado, propuestas, titulo { etiqueta: 'Mensualidad', obtener: (p) => - p.mensualidad === null - ? 'N/A' - : `${p.numMensualidades} × ${formatoMoneda(p.mensualidad)}`, + p.notaMensualidades + ? `— (${p.notaMensualidades})` + : p.mensualidad === null + ? 'N/A' + : `${p.numMensualidades} × ${formatoMoneda(p.mensualidad)}`, }, { etiqueta: 'Total mensualidades', - obtener: (p) => (p.mensualidadesTotal === null ? 'N/A' : formatoMoneda(p.mensualidadesTotal)), + obtener: (p) => + p.notaMensualidades + ? '—' + : p.mensualidadesTotal === null + ? 'N/A' + : formatoMoneda(p.mensualidadesTotal), }, { etiqueta: 'Pago final', obtener: (p) => formatoMoneda(p.pagoFinal) }, - { etiqueta: 'Total pagado', obtener: (_p, i) => formatoMoneda(totales[i]), destacada: true }, + { + etiqueta: 'Total pagado', + obtener: (_p, i) => (totales[i] === null ? '—' : formatoMoneda(totales[i])), + destacada: true, + }, { etiqueta: 'Diferencia vs precio de lista', - obtener: (_p, i) => formatoMoneda(totales[i] - precioLista), + obtener: (_p, i) => (totales[i] === null ? '—' : formatoMoneda(totales[i] - precioLista)), }, ] diff --git a/apps/cotizador/src/components/ProposalCard.tsx b/apps/cotizador/src/components/ProposalCard.tsx index 21e3817..88e429d 100644 --- a/apps/cotizador/src/components/ProposalCard.tsx +++ b/apps/cotizador/src/components/ProposalCard.tsx @@ -67,7 +67,9 @@ export function ProposalCard({ numero, titulo, propuesta, onDescuentoChange }: P nota="incluye deducción del apartado" valor={formatoMoneda(propuesta.enganche)} /> - {propuesta.mensualidad !== null && propuesta.mensualidadesTotal !== null ? ( + {propuesta.notaMensualidades ? ( + + ) : propuesta.mensualidad !== null && propuesta.mensualidadesTotal !== null ? ( + v === null ? '—' : `${v.toLocaleString('es-MX')} m²` + /** Tarjeta con la ficha técnica de la unidad seleccionada */ export function UnitInfoCard({ unidad, numMeses }: Props) { + const extras = unidad.extras ?? {} return (

@@ -24,11 +28,19 @@ export function UnitInfoCard({ unidad, numMeses }: Props) {

- - - - - + {extras['prototipo'] != null && } + {extras['etapa'] != null && } + {extras['manzana'] != null && } + {extras['lote'] != null && } + {extras['cajones'] != null && } + + + + +
) diff --git a/apps/cotizador/src/data/desarrollos.ts b/apps/cotizador/src/data/desarrollos.ts new file mode 100644 index 0000000..33f303f --- /dev/null +++ b/apps/cotizador/src/data/desarrollos.ts @@ -0,0 +1,319 @@ +import type { DescuentosPropuestas, ParametrosDesarrollo } from '../types' + +/** + * Parámetros comerciales por desarrollo, replicados de los cotizadores Excel + * de producción (ver reference/cotizadores/*.md). NO "corregir" fórmulas: + * algunas usan precio de lista donde intuitivamente iría precio final; así + * está en los Excel y así debe quedar. + */ + +const DIAGONAL: ParametrosDesarrollo = { + id: 'DIAGONAL', + nombre: 'Diagonal', + precioUnico: false, + fuenteDeN: 'fechaEntrega', + cuotaMantenimiento: '1,500 Aprox.', + prefijoUnidadPdf: 'CASA', + propuestas: [ + { + tipo: 'CREDITO_BANCARIO', + claveDescuento: 'creditoBancario', + titulo: 'Crédito Bancario', + descuentoDefault: 0, + pctEnganche: 0.025, + engancheBase: 'lista', + pctPagoFinal: 0.9, + pagoFinalBase: 'final', + modoMensualidades: 'remanente', + }, + { + tipo: 'CREDITO_INFONAVIT_COFINAVIT', + claveDescuento: 'creditoInfonavit', + titulo: 'Crédito Infonavit-Cofinavit', + descuentoDefault: 0, + pctEnganche: 0.025, + engancheBase: 'lista', + pctPagoFinal: 0.95, + pagoFinalBase: 'final', + modoMensualidades: 'remanenteLista', + }, + { + tipo: 'CONTADO', + claveDescuento: 'contado', + titulo: 'Contado', + descuentoDefault: 0.02, + pctEnganche: 0.9, + engancheBase: 'final', + pctPagoFinal: 0.1, + pagoFinalBase: 'final', + modoMensualidades: 'ninguna', + }, + { + tipo: 'SEMI_CONTADO', + claveDescuento: 'semiContado', + titulo: 'Semi Contado', + descuentoDefault: 0.01, + pctEnganche: 0.5, + engancheBase: 'final', + pctPagoFinal: 0.1, + pagoFinalBase: 'final', + modoMensualidades: 'porcentaje', + }, + ], +} + +const ATELIER: ParametrosDesarrollo = { + id: 'ATELIER', + nombre: 'Atelier', + precioUnico: true, + fuenteDeN: 'fijo', // inventario ya entregado: las mensualidades son 1 solo pago + nFijo: 1, + cuotaMantenimiento: 'PENDIENTE', + prefijoUnidadPdf: 'DEPTO', + propuestas: [ + { + tipo: 'CREDITO_BANCARIO', + claveDescuento: 'creditoBancario', + titulo: 'Crédito Bancario', + descuentoDefault: 0, + pctEnganche: 0.05, + engancheBase: 'lista', // Excel M36: 5% sobre precio de LISTA + pctPagoFinal: 0.9, + pagoFinalBase: 'final', + modoMensualidades: 'remanente', + }, + { + // ATELIER no tiene Infonavit: COFINAVIT con la estructura del Infonavit + // de DIAGONAL (2.5% / 95%) pero sin el quirk (todo sobre precio final) + tipo: 'CREDITO_INFONAVIT_COFINAVIT', + claveDescuento: 'creditoInfonavit', + titulo: 'Crédito Cofinavit', + descuentoDefault: 0, + pctEnganche: 0.025, + engancheBase: 'final', + pctPagoFinal: 0.95, + pagoFinalBase: 'final', + modoMensualidades: 'porcentaje', + }, + { + tipo: 'CONTADO', + claveDescuento: 'contado', + titulo: 'Contado', + descuentoDefault: 0, + pctEnganche: 0.9, + engancheBase: 'final', + pctPagoFinal: 0.1, + pagoFinalBase: 'final', + modoMensualidades: 'ninguna', + }, + { + tipo: 'SEMI_CONTADO', + claveDescuento: 'semiContado', + titulo: 'Semi Contado', + descuentoDefault: 0, + pctEnganche: 0.5, + engancheBase: 'final', + pctPagoFinal: 0.1, + pagoFinalBase: 'final', + modoMensualidades: 'porcentaje', + }, + ], +} + +const ESTANZA: ParametrosDesarrollo = { + id: 'ESTANZA', + nombre: 'Estanza', + precioUnico: true, + fuenteDeN: 'fijo', // TERM. OBRA = 1 mes en todas las casas + nFijo: 1, + cuotaMantenimiento: 'PENDIENTE', + prefijoUnidadPdf: 'CASA', + propuestas: [ + { + tipo: 'CREDITO_BANCARIO', + claveDescuento: 'creditoBancario', + titulo: 'Crédito Bancario', + descuentoDefault: 0, + pctEnganche: 0.025, + engancheBase: 'final', + pctPagoFinal: 0.9, + pagoFinalBase: 'lista', // Excel M20 = C12*H20: pago final sobre LISTA (quirk) + modoMensualidades: 'porcentaje', + }, + { + tipo: 'CREDITO_INFONAVIT_COFINAVIT', + claveDescuento: 'creditoInfonavit', + titulo: 'Crédito Infonavit-Cofinavit', + descuentoDefault: 0, + pctEnganche: 0.025, + engancheBase: 'final', + pctPagoFinal: 0.95, + pagoFinalBase: 'lista', // Excel M26 = H26*C12: pago final sobre LISTA (quirk) + modoMensualidades: 'porcentaje', + }, + { + tipo: 'CONTADO', + claveDescuento: 'contado', + titulo: 'Contado', + descuentoDefault: 0, + pctEnganche: 0.9, + engancheBase: 'final', + pctPagoFinal: 0.1, + pagoFinalBase: 'final', + modoMensualidades: 'ninguna', + }, + { + tipo: 'SEMI_CONTADO', + claveDescuento: 'semiContado', + titulo: 'Semi Contado', + descuentoDefault: 0, + pctEnganche: 0.5, + engancheBase: 'final', + pctPagoFinal: 0.1, + pagoFinalBase: 'final', + modoMensualidades: 'porcentaje', + }, + ], +} + +const SENDA: ParametrosDesarrollo = { + id: 'SENDA', + nombre: 'Senda', + precioUnico: true, + fuenteDeN: 'fechaEntrega', // unidades sin fecha en TERM => N null (mensualidades "—") + cuotaMantenimiento: 'PENDIENTE DE DEFINIR', + prefijoUnidadPdf: 'CASA', + propuestas: [ + { + tipo: 'CREDITO_BANCARIO', + claveDescuento: 'creditoBancario', + titulo: 'Crédito Bancario', + descuentoDefault: 0, + pctEnganche: 0.05, + engancheBase: 'final', + pctPagoFinal: 0.9, + pagoFinalBase: 'final', + modoMensualidades: 'porcentaje', + }, + { + tipo: 'CREDITO_INFONAVIT_COFINAVIT', + claveDescuento: 'creditoInfonavit', + titulo: 'Crédito Infonavit-Cofinavit', + descuentoDefault: 0, + pctEnganche: 0.025, + engancheBase: 'final', + pctPagoFinal: 0.95, + pagoFinalBase: 'final', + modoMensualidades: 'porcentaje', + }, + { + tipo: 'CONTADO', + claveDescuento: 'contado', + titulo: 'Contado', + descuentoDefault: 0.02, + pctEnganche: 0.9, + engancheBase: 'final', + pctPagoFinal: 0.1, + pagoFinalBase: 'final', + modoMensualidades: 'ninguna', + }, + { + tipo: 'SEMI_CONTADO', + claveDescuento: 'semiContado', + titulo: 'Semi Contado', + descuentoDefault: 0.01, + pctEnganche: 0.5, + engancheBase: 'final', + pctPagoFinal: 0.05, // pago final 5% (DIAGONAL: 10%) + pagoFinalBase: 'final', + modoMensualidades: 'porcentaje', + }, + ], +} + +const SOLANA: ParametrosDesarrollo = { + id: 'SOLANA', + nombre: 'Solana', + precioUnico: true, + fuenteDeN: 'porUnidad', // MESES PARA FIRMA APROX del inventario (1–4) + cuotaMantenimiento: 'PENDIENTE', + prefijoUnidadPdf: 'CASA', + propuestas: [ + { + tipo: 'CREDITO_BANCARIO', + claveDescuento: 'creditoBancario', + titulo: 'Crédito Bancario', + descuentoDefault: 0, + pctEnganche: 0.05, + engancheBase: 'final', + pctPagoFinal: 0.9, + pagoFinalBase: 'final', + modoMensualidades: 'porcentaje', + }, + { + tipo: 'CREDITO_INFONAVIT_COFINAVIT', + claveDescuento: 'creditoInfonavit', + titulo: 'Crédito Infonavit-Cofinavit', + descuentoDefault: 0, + pctEnganche: 0, + engancheBase: 'final', + pctPagoFinal: 0.95, + pagoFinalBase: 'final', + modoMensualidades: 'porcentaje', + engancheCeroSinApartado: true, // Excel M27: IF(H27=0, 0, ...) + }, + { + tipo: 'CONTADO', + claveDescuento: 'contado', + titulo: 'Contado', + descuentoDefault: 0, + pctEnganche: 0.9, + engancheBase: 'final', + pctPagoFinal: 0.1, + pagoFinalBase: 'final', + modoMensualidades: 'ninguna', + }, + { + tipo: 'SEMI_CONTADO', + claveDescuento: 'semiContado', + titulo: 'Semi Contado', + descuentoDefault: 0, + pctEnganche: 0.5, + engancheBase: 'final', + pctPagoFinal: 0.1, + pagoFinalBase: 'final', + modoMensualidades: 'porcentaje', + }, + ], +} + +export const DESARROLLOS: Record = { + DIAGONAL, + ATELIER, + ESTANZA, + SENDA, + SOLANA, +} + +export const DESARROLLO_DEFAULT = 'DIAGONAL' + +/** Ids en orden de presentación en el selector */ +export const LISTA_DESARROLLOS: string[] = ['DIAGONAL', 'ATELIER', 'ESTANZA', 'SENDA', 'SOLANA'] + +/** Parámetros de un desarrollo; cae a DIAGONAL si el id es desconocido */ +export function obtenerDesarrollo(id?: string | null): ParametrosDesarrollo { + return (id && DESARROLLOS[id]) || DESARROLLOS[DESARROLLO_DEFAULT] +} + +/** Descuentos por defecto del desarrollo, indexados por clave de propuesta */ +export function descuentosDefault(id?: string | null): DescuentosPropuestas { + const params = obtenerDesarrollo(id) + const out: DescuentosPropuestas = { + creditoBancario: 0, + creditoInfonavit: 0, + contado: 0, + semiContado: 0, + } + for (const p of params.propuestas) out[p.claveDescuento] = p.descuentoDefault + return out +} diff --git a/apps/cotizador/src/data/unidades-atelier.ts b/apps/cotizador/src/data/unidades-atelier.ts new file mode 100644 index 0000000..5919369 --- /dev/null +++ b/apps/cotizador/src/data/unidades-atelier.ts @@ -0,0 +1,41 @@ +import type { Unidad } from '../types' + +// Inventario ATELIER — 34 unidades. Departamentos, MZA 32 LOTE 1. +// Generado desde reference/cotizadores/ATELIER.json (NO editar a mano; regenerar con scripts/generar-seeds.py). +// Precio único por unidad: precioSinAcabados/precioConAcabados replican precioLista. +export const UNIDADES_ATELIER: Unidad[] = [ + { codigo: 'ATELIER-1', modelo: 'TRIBECA', desarrollo: 'ATELIER', fechaEntrega: '2026-01-27', supEstacionamiento: null, supConstruccion: 75.005, supExcedente: 17.468, precioSinAcabados: 3554291, precioConAcabados: 3554291, precioLista: 3554291, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: '2R-A', manzana: 32, lote: 1, cajones: '1 C-EXT' } }, + { codigo: 'ATELIER-2', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-01-15', supEstacionamiento: null, supConstruccion: 52.5359, supExcedente: 4.1864, precioSinAcabados: 2397434.52, precioConAcabados: 2397434.52, precioLista: 2397434.52, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-A', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-3', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-02-07', supEstacionamiento: null, supConstruccion: 52.115, supExcedente: null, precioSinAcabados: 2666831, precioConAcabados: 2666831, precioLista: 2666831, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-4', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-01-28', supEstacionamiento: null, supConstruccion: 53.1241, supExcedente: 4.6853, precioSinAcabados: 2614540, precioConAcabados: 2614540, precioLista: 2614540, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-5', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-02-07', supEstacionamiento: null, supConstruccion: 52.827, supExcedente: 4.5684, precioSinAcabados: 2614540, precioConAcabados: 2614540, precioLista: 2614540, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-6', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-02-07', supEstacionamiento: null, supConstruccion: 52.309, supExcedente: 4.326, precioSinAcabados: 2666831, precioConAcabados: 2666831, precioLista: 2666831, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-7', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-02-07', supEstacionamiento: null, supConstruccion: 52.309, supExcedente: 4.324, precioSinAcabados: 2373713, precioConAcabados: 2373713, precioLista: 2373713, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-8', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-02-07', supEstacionamiento: null, supConstruccion: 52.059, supExcedente: 4.22, precioSinAcabados: 2327169, precioConAcabados: 2327169, precioLista: 2327169, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-9', modelo: 'TRIBECA', desarrollo: 'ATELIER', fechaEntrega: '2026-02-07', supEstacionamiento: null, supConstruccion: 82.828, supExcedente: 27.913, precioSinAcabados: 4451040, precioConAcabados: 4451040, precioLista: 4451040, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: '2R-B', manzana: 32, lote: 1, cajones: '1 C-EXT' } }, + { codigo: 'ATELIER-10', modelo: 'TRIBECA', desarrollo: 'ATELIER', fechaEntrega: '2026-01-21', supEstacionamiento: null, supConstruccion: 83.348, supExcedente: 16.065, precioSinAcabados: 3997500, precioConAcabados: 3997500, precioLista: 3997500, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: '2R-C', manzana: 32, lote: 1, cajones: '1 C-EXT' } }, + { codigo: 'ATELIER-11', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-01-15', supEstacionamiento: null, supConstruccion: 52.194, supExcedente: 4.219, precioSinAcabados: 2304339, precioConAcabados: 2304339, precioLista: 2304339, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-A', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-12', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-01-27', supEstacionamiento: null, supConstruccion: 52.526, supExcedente: 4.461, precioSinAcabados: 2281538, precioConAcabados: 2281538, precioLista: 2281538, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-13', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-01-05', supEstacionamiento: null, supConstruccion: 52.856, supExcedente: 4.575, precioSinAcabados: 2327169, precioConAcabados: 2327169, precioLista: 2327169, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-14', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-01-30', supEstacionamiento: null, supConstruccion: 52.148, supExcedente: 4.252, precioSinAcabados: 2491122, precioConAcabados: 2491122, precioLista: 2491122, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-15', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-01-31', supEstacionamiento: null, supConstruccion: 51.772, supExcedente: 4.109, precioSinAcabados: 2261538, precioConAcabados: 2261538, precioLista: 2261538, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-16', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-01-27', supEstacionamiento: null, supConstruccion: 52.371, supExcedente: 4.249, precioSinAcabados: 2327169, precioConAcabados: 2327169, precioLista: 2327169, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-17', modelo: 'TRIBECA', desarrollo: 'ATELIER', fechaEntrega: '2026-02-07', supEstacionamiento: null, supConstruccion: 83.025, supExcedente: 27.495, precioSinAcabados: 4250600, precioConAcabados: 4250600, precioLista: 4250600, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: '2R-B', manzana: 32, lote: 1, cajones: '1 C-EXT' } }, + { codigo: 'ATELIER-18', modelo: 'TRIBECA', desarrollo: 'ATELIER', fechaEntrega: '2026-03-24', supEstacionamiento: null, supConstruccion: 75.115, supExcedente: null, precioSinAcabados: 2985326, precioConAcabados: 2985326, precioLista: 2985326, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: '2R-A', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-19', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-03-21', supEstacionamiento: null, supConstruccion: 52.536, supExcedente: null, precioSinAcabados: 2191183, precioConAcabados: 2191183, precioLista: 2191183, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-A', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-20', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-03-21', supEstacionamiento: null, supConstruccion: 52.11, supExcedente: 4.2481, precioSinAcabados: 2281538, precioConAcabados: 2281538, precioLista: 2281538, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-21', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-03-14', supEstacionamiento: null, supConstruccion: 53.056, supExcedente: null, precioSinAcabados: 2506220, precioConAcabados: 2506220, precioLista: 2506220, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-22', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-03-14', supEstacionamiento: null, supConstruccion: 52.827, supExcedente: null, precioSinAcabados: 2506220, precioConAcabados: 2506220, precioLista: 2506220, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-23', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-03-09', supEstacionamiento: null, supConstruccion: 52.503, supExcedente: null, precioSinAcabados: 2216961, precioConAcabados: 2216961, precioLista: 2216961, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-24', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-03-09', supEstacionamiento: null, supConstruccion: 52.501, supExcedente: null, precioSinAcabados: 2666831, precioConAcabados: 2666831, precioLista: 2666831, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-25', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-03-04', supEstacionamiento: null, supConstruccion: 52.085, supExcedente: null, precioSinAcabados: 2173491, precioConAcabados: 2173491, precioLista: 2173491, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-26', modelo: 'TRIBECA', desarrollo: 'ATELIER', fechaEntrega: '2026-03-04', supEstacionamiento: null, supConstruccion: 82.805, supExcedente: null, precioSinAcabados: 3547406, precioConAcabados: 3547406, precioLista: 3547406, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: '2R-B', manzana: 32, lote: 1, cajones: '1 C-EXT' } }, + { codigo: 'ATELIER-27', modelo: 'TRIBECA', desarrollo: 'ATELIER', fechaEntrega: '2026-03-14', supEstacionamiento: null, supConstruccion: 82.633, supExcedente: null, precioSinAcabados: 3750500, precioConAcabados: 3750500, precioLista: 3750500, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: '2R-C', manzana: 32, lote: 1, cajones: '1 C-EXT' } }, + { codigo: 'ATELIER-28', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-02-07', supEstacionamiento: null, supConstruccion: 52.242, supExcedente: null, precioSinAcabados: 2059713, precioConAcabados: 2059713, precioLista: 2059713, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-A', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-29', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-02-28', supEstacionamiento: null, supConstruccion: 52.518, supExcedente: null, precioSinAcabados: 2579442, precioConAcabados: 2579442, precioLista: 2579442, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-30', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-02-12', supEstacionamiento: null, supConstruccion: 52.958, supExcedente: null, precioSinAcabados: 2173491, precioConAcabados: 2173491, precioLista: 2173491, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-31', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-02-17', supEstacionamiento: null, supConstruccion: 52.101, supExcedente: null, precioSinAcabados: 2216961, precioConAcabados: 2216961, precioLista: 2216961, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-32', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-02-25', supEstacionamiento: null, supConstruccion: 51.83, supExcedente: null, precioSinAcabados: 2173491, precioConAcabados: 2173491, precioLista: 2173491, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-33', modelo: 'SOHO', desarrollo: 'ATELIER', fechaEntrega: '2026-02-25', supEstacionamiento: null, supConstruccion: 52.25, supExcedente: null, precioSinAcabados: 2151756.09, precioConAcabados: 2151756.09, precioLista: 2151756.09, status: 'ESCRITURADO', mesesParaFirma: null, extras: { prototipo: 'E-B', manzana: 32, lote: 1, cajones: '1 C-INT' } }, + { codigo: 'ATELIER-34', modelo: 'TRIBECA', desarrollo: 'ATELIER', fechaEntrega: '2026-02-28', supEstacionamiento: null, supConstruccion: 83.154, supExcedente: null, precioSinAcabados: 3750500, precioConAcabados: 3750500, precioLista: 3750500, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: '2R-B', manzana: 32, lote: 1, cajones: '1 C-EXT' } }, +] diff --git a/apps/cotizador/src/data/unidades-estanza.ts b/apps/cotizador/src/data/unidades-estanza.ts new file mode 100644 index 0000000..e3acd4f --- /dev/null +++ b/apps/cotizador/src/data/unidades-estanza.ts @@ -0,0 +1,71 @@ +import type { Unidad } from '../types' + +// Inventario ESTANZA — 64 unidades. Casas DUPLO A/B, MZA 92 LOTE 32. +// Generado desde reference/cotizadores/ESTANZA.json (NO editar a mano; regenerar con scripts/generar-seeds.py). +// Precio único por unidad: precioSinAcabados/precioConAcabados replican precioLista. +export const UNIDADES_ESTANZA: Unidad[] = [ + { codigo: 'ESTANZA-1', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: 27.061, precioSinAcabados: 2617632, precioConAcabados: 2617632, precioLista: 2617632, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-2', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: 14.6227, precioSinAcabados: 2653052, precioConAcabados: 2653052, precioLista: 2653052, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-3', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: 10.3277, precioSinAcabados: 2273327, precioConAcabados: 2273327, precioLista: 2273327, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-4', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: 8.1628, precioSinAcabados: 2558590, precioConAcabados: 2558590, precioLista: 2558590, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-5', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: 4.4683, precioSinAcabados: 2292631, precioConAcabados: 2292631, precioLista: 2292631, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-6', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: 2.394, precioSinAcabados: 2536885, precioConAcabados: 2536885, precioLista: 2536885, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-7', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: 1.0732, precioSinAcabados: 2486023, precioConAcabados: 2486023, precioLista: 2486023, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-8', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: 1.4823, precioSinAcabados: 2485590, precioConAcabados: 2485590, precioLista: 2485590, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-9', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: 1.5349, precioSinAcabados: 2489942, precioConAcabados: 2489942, precioLista: 2489942, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-10', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: 1.1014, precioSinAcabados: 2340562, precioConAcabados: 2340562, precioLista: 2340562, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-11', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2501683, precioConAcabados: 2501683, precioLista: 2501683, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-12', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2512571, precioConAcabados: 2512571, precioLista: 2512571, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-13', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2472677, precioConAcabados: 2472677, precioLista: 2472677, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-14', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2508175, precioConAcabados: 2508175, precioLista: 2508175, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-15', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2361751, precioConAcabados: 2361751, precioLista: 2361751, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-16', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2589780, precioConAcabados: 2589780, precioLista: 2589780, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-17', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2388920, precioConAcabados: 2388920, precioLista: 2388920, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-18', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2407848, precioConAcabados: 2407848, precioLista: 2407848, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-19', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2388920, precioConAcabados: 2388920, precioLista: 2388920, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-20', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2563074, precioConAcabados: 2563074, precioLista: 2563074, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-21', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2528824, precioConAcabados: 2528824, precioLista: 2528824, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-22', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2241564, precioConAcabados: 2241564, precioLista: 2241564, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-23', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2410854, precioConAcabados: 2410854, precioLista: 2410854, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-24', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2345652, precioConAcabados: 2345652, precioLista: 2345652, status: 'VENDIDO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-25', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2388920, precioConAcabados: 2388920, precioLista: 2388920, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-26', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2563074, precioConAcabados: 2563074, precioLista: 2563074, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-27', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2497404, precioConAcabados: 2497404, precioLista: 2497404, status: 'VENDIDO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-28', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2512571, precioConAcabados: 2512571, precioLista: 2512571, status: 'VENDIDO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-29', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2492611, precioConAcabados: 2492611, precioLista: 2492611, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-30', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2516765, precioConAcabados: 2516765, precioLista: 2516765, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-31', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: 18.296, precioSinAcabados: 2521121, precioConAcabados: 2521121, precioLista: 2521121, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-32', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2412850, precioConAcabados: 2412850, precioLista: 2412850, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-33', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2388920, precioConAcabados: 2388920, precioLista: 2388920, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-34', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2537697, precioConAcabados: 2537697, precioLista: 2537697, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-35', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2380999, precioConAcabados: 2380999, precioLista: 2380999, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-36', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2380999, precioConAcabados: 2380999, precioLista: 2380999, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-37', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2388920, precioConAcabados: 2388920, precioLista: 2388920, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-38', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2427873, precioConAcabados: 2427873, precioLista: 2427873, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-39', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2485590, precioConAcabados: 2485590, precioLista: 2485590, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-40', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2506594, precioConAcabados: 2506594, precioLista: 2506594, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-41', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2388920, precioConAcabados: 2388920, precioLista: 2388920, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-42', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2515999, precioConAcabados: 2515999, precioLista: 2515999, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-43', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2443465, precioConAcabados: 2443465, precioLista: 2443465, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-44', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2287855, precioConAcabados: 2287855, precioLista: 2287855, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-45', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2269258, precioConAcabados: 2269258, precioLista: 2269258, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-46', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: 6.3751, precioSinAcabados: 2581017, precioConAcabados: 2581017, precioLista: 2581017, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-47', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: 27.7262, precioSinAcabados: 2752486, precioConAcabados: 2752486, precioLista: 2752486, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-48', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2581017, precioConAcabados: 2581017, precioLista: 2581017, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-49', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2516765, precioConAcabados: 2516765, precioLista: 2516765, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-50', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2529223, precioConAcabados: 2529223, precioLista: 2529223, status: 'VENDIDO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-51', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2490390, precioConAcabados: 2490390, precioLista: 2490390, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-52', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2437250, precioConAcabados: 2437250, precioLista: 2437250, status: 'APARTADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-53', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2345652, precioConAcabados: 2345652, precioLista: 2345652, status: 'APARTADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-54', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2556059, precioConAcabados: 2556059, precioLista: 2556059, status: 'VENDIDO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-55', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2492920, precioConAcabados: 2492920, precioLista: 2492920, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-56', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2533257, precioConAcabados: 2533257, precioLista: 2533257, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-57', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2492920, precioConAcabados: 2492920, precioLista: 2492920, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-58', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2529223, precioConAcabados: 2529223, precioLista: 2529223, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-59', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2388920, precioConAcabados: 2388920, precioLista: 2388920, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-60', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2558590, precioConAcabados: 2558590, precioLista: 2558590, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-61', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2345652, precioConAcabados: 2345652, precioLista: 2345652, status: 'DISPONIBLE', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-62', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2537697, precioConAcabados: 2537697, precioLista: 2537697, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-63', modelo: 'DUPLO A', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.06, supExcedente: null, precioSinAcabados: 2248242, precioConAcabados: 2248242, precioLista: 2248242, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, + { codigo: 'ESTANZA-64', modelo: 'DUPLO B', desarrollo: 'ESTANZA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.18, supExcedente: null, precioSinAcabados: 2457269, precioConAcabados: 2457269, precioLista: 2457269, status: 'ESCRITURADO', mesesParaFirma: null, extras: { manzana: 92, lote: 32, cajones: 1 } }, +] diff --git a/apps/cotizador/src/data/unidades-senda.ts b/apps/cotizador/src/data/unidades-senda.ts new file mode 100644 index 0000000..a99b74b --- /dev/null +++ b/apps/cotizador/src/data/unidades-senda.ts @@ -0,0 +1,146 @@ +import type { Unidad } from '../types' + +// Inventario SENDA — 139 unidades. Casas; fechaEntrega null cuando la unidad no está en la tabla TERM (mensualidades no calculables). +// Generado desde reference/cotizadores/SENDA.json (NO editar a mano; regenerar con scripts/generar-seeds.py). +// Precio único por unidad: precioSinAcabados/precioConAcabados replican precioLista. +export const UNIDADES_SENDA: Unidad[] = [ + { codigo: 'SENDA-411-1-2', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-11-07', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2287000, precioConAcabados: 2287000, precioLista: 2287000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-401-26-10', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-01-02', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-26-11', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-01-09', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-26-12', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-01-16', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2468483, precioConAcabados: 2468483, precioLista: 2468483, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-26-13', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-01-23', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-26-14', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-01-30', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-26-2', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-11-07', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-26-3', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-11-14', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2468483, precioConAcabados: 2468483, precioLista: 2468483, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-26-4', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-11-21', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-26-5', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-11-28', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-26-6', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-12-05', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2468483, precioConAcabados: 2468483, precioLista: 2468483, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-26-7', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-12-12', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2450000, precioConAcabados: 2450000, precioLista: 2450000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-3-5', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 401, lote: 3, cajones: 2 } }, + { codigo: 'SENDA-401-3-6', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 401, lote: 3, cajones: 2 } }, + { codigo: 'SENDA-401-3-2', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 401, lote: 3, cajones: 2 } }, + { codigo: 'SENDA-411-2-2', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2750000, precioConAcabados: 2750000, precioLista: 2750000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 411, lote: 2, cajones: 2 } }, + { codigo: 'SENDA-401-26-1', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-10-24', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-3-7', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 401, lote: 3, cajones: 2 } }, + { codigo: 'SENDA-401-26-9', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-12-26', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2450000, precioConAcabados: 2450000, precioLista: 2450000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-401-3-3', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 401, lote: 3, cajones: 2 } }, + { codigo: 'SENDA-401-3-8', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2750000, precioConAcabados: 2750000, precioLista: 2750000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 401, lote: 3, cajones: 2 } }, + { codigo: 'SENDA-401-26-8', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-12-19', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 401, lote: 26, cajones: 1 } }, + { codigo: 'SENDA-411-1-4', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-11-21', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2287000, precioConAcabados: 2287000, precioLista: 2287000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-2', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-09-04', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-409-1-3', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-09-11', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-409-1-4', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-09-18', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-409-1-5', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-09-25', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-409-1-6', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-10-02', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-409-1-7', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-10-09', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-409-1-8', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-10-16', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-411-1-13', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-01-23', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-10', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-10-30', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-409-1-11', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-11-06', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-409-2-1', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-12-04', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 2, cajones: 2 } }, + { codigo: 'SENDA-409-1-13', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-04-09', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-14', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-04-02', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-15', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-03-26', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-16', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-03-19', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-17', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-03-12', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-18', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-03-05', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-19', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-02-26', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-20', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-02-19', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-21', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-02-12', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-22', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-02-05', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-23', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-01-29', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-24', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-01-22', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-25', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-01-15', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-26', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-01-08', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-27', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2029-01-01', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-28', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-12-25', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-29', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-12-18', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-30', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-12-04', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 409, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-3-1', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-12-11', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2750000, precioConAcabados: 2750000, precioLista: 2750000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 3, cajones: 2 } }, + { codigo: 'SENDA-410-2-1', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-07-17', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 410, lote: 2, cajones: 2 } }, + { codigo: 'SENDA-410-3-1', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-07-24', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2750000, precioConAcabados: 2750000, precioLista: 2750000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 410, lote: 3, cajones: 2 } }, + { codigo: 'SENDA-410-1-11', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2450000, precioConAcabados: 2450000, precioLista: 2450000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-410-1-8', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-9', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-10', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-12', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-01-16', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2318246, precioConAcabados: 2318246, precioLista: 2318246, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-12', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-13', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-14', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-15', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-16', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-17', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-02-13', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-18', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-02-20', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-19', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-10-09', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-20', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-10-02', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-21', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-09-25', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-22', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-09-18', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-23', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-09-11', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-24', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-09-04', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-25', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-08-28', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-26', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-08-21', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-27', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-08-14', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-28', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-08-07', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-29', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-07-31', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-30', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-07-24', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-31', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2028-07-17', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'BLOQUEADO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-7A', modelo: 'SANTA CRUZ', desarrollo: 'SENDA', fechaEntrega: '2027-03-20', supEstacionamiento: null, supConstruccion: null, supExcedente: null, precioSinAcabados: 2063666.75, precioConAcabados: 2063666.75, precioLista: 2063666.75, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'DUPLO', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-A102', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.2, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-A101', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.2, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-A201', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.2, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-B101', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.2, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-B102', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.2, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-5', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-11-28', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-B202', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.2, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-C101', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.2, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-C102', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.2, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-C201', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-C202', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-D101', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-D102', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-D201', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-D202', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-E101', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-E102', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-E201', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-E202', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-F101', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-F102', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-F201', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-F202', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-G101', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-G202', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-5-1', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-11-20', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 5, cajones: 2 } }, + { codigo: 'SENDA-409-4-1', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-11-27', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2750000, precioConAcabados: 2750000, precioLista: 2750000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 4, cajones: 2 } }, + { codigo: 'SENDA-409-1-1', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-08-28', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2750000, precioConAcabados: 2750000, precioLista: 2750000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-411-1-1', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-10-24', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2326593, precioConAcabados: 2326593, precioLista: 2326593, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-409-1-12', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-11-13', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-410-1-15A', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: '2027-05-15', supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 0, precioConAcabados: 0, precioLista: null, status: 'APARTADO', mesesParaFirma: null, extras: { prototipo: 'DUPLO', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-3', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-11-14', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-11', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-01-09', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-6', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-12-05', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-7', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-12-12', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-8', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-12-19', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-9', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2026-12-26', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-401-3-4', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 401, lote: 3, cajones: 2 } }, + { codigo: 'SENDA-409-1-9', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2027-10-23', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2700000, precioConAcabados: 2700000, precioLista: 2700000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 409, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-411-1-10', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-01-02', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-14', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-04-17', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-23', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-02-13', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-16', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-04-03', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-15', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-04-10', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-17', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-03-27', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-18', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-03-20', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-19', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-03-13', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-20', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-03-06', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-21', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-02-27', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-22', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-02-20', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-410-1-16B', modelo: 'SANTA CRUZ', desarrollo: 'SENDA', fechaEntrega: '2027-05-15', supEstacionamiento: null, supConstruccion: 55.2, supExcedente: null, precioSinAcabados: 2112663.75, precioConAcabados: 2112663.75, precioLista: 2112663.75, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'DUPLO', manzana: 410, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-1-24', modelo: 'HIMALAYA', desarrollo: 'SENDA', fechaEntrega: '2027-02-06', supEstacionamiento: null, supConstruccion: 69.33, supExcedente: null, precioSinAcabados: 2451750, precioConAcabados: 2451750, precioLista: 2451750, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'UNE-C', manzana: 411, lote: 1, cajones: 1 } }, + { codigo: 'SENDA-411-25-A201', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 411, lote: 25, cajones: 1 } }, + { codigo: 'SENDA-411-25-A202', modelo: 'ESMERALDA PA', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.78, supExcedente: null, precioSinAcabados: 1850000, precioConAcabados: 1850000, precioLista: 1850000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 411, lote: 25, cajones: 1 } }, + { codigo: 'SENDA-411-25-A101', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.2, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 411, lote: 25, cajones: 1 } }, + { codigo: 'SENDA-411-25-A102', modelo: 'ESMERALDA PB', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 55.2, supExcedente: null, precioSinAcabados: 1948000, precioConAcabados: 1948000, precioLista: 1948000, status: 'DISPONIBLE', mesesParaFirma: null, extras: { prototipo: 'CUADRUPLEX', manzana: 411, lote: 25, cajones: 1 } }, + { codigo: 'SENDA-411-26-1', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2750000, precioConAcabados: 2750000, precioLista: 2750000, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 411, lote: 1, cajones: 2 } }, + { codigo: 'SENDA-411-3-1', modelo: 'SANTIAGO', desarrollo: 'SENDA', fechaEntrega: '2026-12-05', supEstacionamiento: null, supConstruccion: 63.95, supExcedente: null, precioSinAcabados: 2524241, precioConAcabados: 2524241, precioLista: 2524241, status: 'VENDIDO', mesesParaFirma: null, extras: { prototipo: 'LAUREL', manzana: 411, lote: 3, cajones: 2 } }, +] diff --git a/apps/cotizador/src/data/unidades-solana.ts b/apps/cotizador/src/data/unidades-solana.ts new file mode 100644 index 0000000..352c682 --- /dev/null +++ b/apps/cotizador/src/data/unidades-solana.ts @@ -0,0 +1,17 @@ +import type { Unidad } from '../types' + +// Inventario SOLANA — 10 unidades. Casas etapas SOLANA I/II; mesesParaFirma (1-4) es el plazo de mensualidades. +// Generado desde reference/cotizadores/SOLANA.json (NO editar a mano; regenerar con scripts/generar-seeds.py). +// Precio único por unidad: precioSinAcabados/precioConAcabados replican precioLista. +export const UNIDADES_SOLANA: Unidad[] = [ + { codigo: 'SOLANA-1A', modelo: 'PANORAMA', desarrollo: 'SOLANA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: 33.8094, precioSinAcabados: 2990445, precioConAcabados: 2990445, precioLista: 2990445, status: 'ESCRITURADO', mesesParaFirma: 3, extras: { prototipo: 'UNE-C', etapa: 'SOLANA I', cajones: 2 } }, + { codigo: 'SOLANA-2A', modelo: 'PANORAMA', desarrollo: 'SOLANA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: 4.95, precioSinAcabados: 2767518, precioConAcabados: 2767518, precioLista: 2767518, status: 'VENDIDO', mesesParaFirma: 2, extras: { prototipo: 'UNE-C', etapa: 'SOLANA I', cajones: 2 } }, + { codigo: 'SOLANA-3A', modelo: 'MIRADOR', desarrollo: 'SOLANA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: 26.647, precioSinAcabados: 2922847, precioConAcabados: 2922847, precioLista: 2922847, status: 'ESCRITURADO', mesesParaFirma: 3, extras: { prototipo: 'UNE-C', etapa: 'SOLANA I', cajones: 1 } }, + { codigo: 'SOLANA-4A', modelo: 'MIRADOR', desarrollo: 'SOLANA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: 27.815, precioSinAcabados: 2952847, precioConAcabados: 2952847, precioLista: 2952847, status: 'ESCRITURADO', mesesParaFirma: 4, extras: { prototipo: 'UNE-C', etapa: 'SOLANA I', cajones: 1 } }, + { codigo: 'SOLANA-5A', modelo: 'MIRADOR', desarrollo: 'SOLANA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.83, supExcedente: 22.067, precioSinAcabados: 3400000, precioConAcabados: 3400000, precioLista: 3400000, status: 'ESCRITURADO', mesesParaFirma: 4, extras: { prototipo: 'LAUREL', etapa: 'SOLANA I', cajones: 1 } }, + { codigo: 'SOLANA-6', modelo: 'MIRADOR', desarrollo: 'SOLANA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 69.33, supExcedente: 0, precioSinAcabados: 2560000, precioConAcabados: 2560000, precioLista: 2560000, status: 'DISPONIBLE', mesesParaFirma: 1, extras: { prototipo: 'UNE-C', etapa: 'SOLANA II', cajones: 1 } }, + { codigo: 'SOLANA-7', modelo: 'MIRADOR', desarrollo: 'SOLANA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.83, supExcedente: 0, precioSinAcabados: 2595000, precioConAcabados: 2595000, precioLista: 2595000, status: 'DISPONIBLE', mesesParaFirma: 1, extras: { prototipo: 'DUPLO-B', etapa: 'SOLANA II', cajones: 2 } }, + { codigo: 'SOLANA-8', modelo: 'MIRADOR', desarrollo: 'SOLANA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 56.17, supExcedente: 0, precioSinAcabados: 2575000, precioConAcabados: 2575000, precioLista: 2575000, status: 'VENDIDO', mesesParaFirma: 1, extras: { prototipo: 'DUPLO-A', etapa: 'SOLANA II', cajones: 2 } }, + { codigo: 'SOLANA-9', modelo: 'MIRADOR', desarrollo: 'SOLANA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 63.83, supExcedente: 0, precioSinAcabados: 2605000, precioConAcabados: 2605000, precioLista: 2605000, status: 'DISPONIBLE', mesesParaFirma: 1, extras: { prototipo: 'DUPLO-B', etapa: 'SOLANA II', cajones: 2 } }, + { codigo: 'SOLANA-10', modelo: 'MIRADOR', desarrollo: 'SOLANA', fechaEntrega: null, supEstacionamiento: null, supConstruccion: 56.17, supExcedente: 0, precioSinAcabados: 2730000, precioConAcabados: 2730000, precioLista: 2730000, status: 'VENDIDO', mesesParaFirma: 1, extras: { prototipo: 'DUPLO-A', etapa: 'SOLANA II', cajones: 2 } }, +] diff --git a/apps/cotizador/src/data/unidades.ts b/apps/cotizador/src/data/unidades.ts index 5aa2887..f91ec7b 100644 --- a/apps/cotizador/src/data/unidades.ts +++ b/apps/cotizador/src/data/unidades.ts @@ -1,43 +1,57 @@ import type { Unidad } from '../types' // Inventario real — 38 unidades, todas DISPONIBLE +import { UNIDADES_ATELIER } from './unidades-atelier' +import { UNIDADES_ESTANZA } from './unidades-estanza' +import { UNIDADES_SENDA } from './unidades-senda' +import { UNIDADES_SOLANA } from './unidades-solana' + export const UNIDADES_SEED: Unidad[] = [ - { codigo: 'U-101', modelo: '2RA', fechaEntrega: '2027-10-08', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 21.336, precioSinAcabados: 4415000, precioConAcabados: 4579000, status: 'DISPONIBLE' }, - { codigo: 'U-102', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.132, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE' }, - { codigo: 'U-103', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.132, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE' }, - { codigo: 'U-104', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.132, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE' }, - { codigo: 'U-105', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.132, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE' }, - { codigo: 'U-106', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.132, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE' }, - { codigo: 'U-107', modelo: '2RA', fechaEntrega: '2027-10-08', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 20.842, precioSinAcabados: 4415000, precioConAcabados: 4579000, status: 'DISPONIBLE' }, - { codigo: 'U-108', modelo: '2RA', fechaEntrega: '2027-10-08', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 18.97, precioSinAcabados: 4415000, precioConAcabados: 4579000, status: 'DISPONIBLE' }, - { codigo: 'U-109', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.13, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE' }, - { codigo: 'U-110', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.13, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE' }, - { codigo: 'U-111', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.13, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE' }, - { codigo: 'U-112', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.13, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE' }, - { codigo: 'U-113', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.13, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE' }, - { codigo: 'U-114', modelo: '2RA', fechaEntrega: '2027-10-08', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 18.476, precioSinAcabados: 4415000, precioConAcabados: 4579000, status: 'DISPONIBLE' }, - { codigo: 'U-201', modelo: '2RA', fechaEntrega: '2027-10-15', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 3960000, precioConAcabados: 4127000, status: 'DISPONIBLE' }, - { codigo: 'U-202', modelo: '1R', fechaEntrega: '2027-10-15', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE' }, - { codigo: 'U-203', modelo: '1R', fechaEntrega: '2027-10-22', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE' }, - { codigo: 'U-204', modelo: '1R', fechaEntrega: '2027-10-22', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE' }, - { codigo: 'U-205', modelo: '1R', fechaEntrega: '2027-10-22', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE' }, - { codigo: 'U-206', modelo: '1R', fechaEntrega: '2027-10-29', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE' }, - { codigo: 'U-207', modelo: '2RA', fechaEntrega: '2027-10-29', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 3960000, precioConAcabados: 4127000, status: 'DISPONIBLE' }, - { codigo: 'U-208', modelo: '2RA', fechaEntrega: '2027-10-29', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 3960000, precioConAcabados: 4127000, status: 'DISPONIBLE' }, - { codigo: 'U-209', modelo: '1R', fechaEntrega: '2027-10-29', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE' }, - { codigo: 'U-210', modelo: '1R', fechaEntrega: '2027-10-29', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE' }, - { codigo: 'U-211', modelo: '1R', fechaEntrega: '2027-11-05', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE' }, - { codigo: 'U-212', modelo: '1R', fechaEntrega: '2027-11-05', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE' }, - { codigo: 'U-213', modelo: '1R', fechaEntrega: '2027-11-12', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE' }, - { codigo: 'U-214', modelo: '2RA', fechaEntrega: '2027-11-12', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 3960000, precioConAcabados: 4127000, status: 'DISPONIBLE' }, - { codigo: 'U-301', modelo: '2RA', fechaEntrega: '2027-11-12', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 4150000, precioConAcabados: 4315000, status: 'DISPONIBLE' }, - { codigo: 'U-302', modelo: '2RB', fechaEntrega: '2027-11-12', supEstacionamiento: 28.62, supConstruccion: 68.757, supExcedente: 0, precioSinAcabados: 4120000, precioConAcabados: 4280000, status: 'DISPONIBLE' }, - { codigo: 'U-303', modelo: '2RC', fechaEntrega: '2027-11-19', supEstacionamiento: 28.62, supConstruccion: 72.328, supExcedente: 0, precioSinAcabados: 4300000, precioConAcabados: 4450000, status: 'DISPONIBLE' }, - { codigo: 'U-304', modelo: '2RC', fechaEntrega: '2027-11-19', supEstacionamiento: 28.62, supConstruccion: 72.328, supExcedente: 0, precioSinAcabados: 4300000, precioConAcabados: 4450000, status: 'DISPONIBLE' }, - { codigo: 'U-305', modelo: '2RA', fechaEntrega: '2027-11-19', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 4150000, precioConAcabados: 4315000, status: 'DISPONIBLE' }, - { codigo: 'U-306', modelo: '2RA', fechaEntrega: '2027-11-19', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 4150000, precioConAcabados: 4315000, status: 'DISPONIBLE' }, - { codigo: 'U-307', modelo: '2RC', fechaEntrega: '2027-11-26', supEstacionamiento: 28.62, supConstruccion: 72.328, supExcedente: 0, precioSinAcabados: 4300000, precioConAcabados: 4450000, status: 'DISPONIBLE' }, - { codigo: 'U-308', modelo: '2RC', fechaEntrega: '2027-11-26', supEstacionamiento: 28.62, supConstruccion: 72.328, supExcedente: 0, precioSinAcabados: 4300000, precioConAcabados: 4450000, status: 'DISPONIBLE' }, - { codigo: 'U-309', modelo: '2RB', fechaEntrega: '2027-11-26', supEstacionamiento: 28.62, supConstruccion: 68.757, supExcedente: 0, precioSinAcabados: 4120000, precioConAcabados: 4280000, status: 'DISPONIBLE' }, - { codigo: 'U-310', modelo: '2RA', fechaEntrega: '2027-11-26', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 4150000, precioConAcabados: 4315000, status: 'DISPONIBLE' }, + { codigo: 'U-101', modelo: '2RA', fechaEntrega: '2027-10-08', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 21.336, precioSinAcabados: 4415000, precioConAcabados: 4579000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-102', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.132, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-103', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.132, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-104', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.132, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-105', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.132, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-106', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.132, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-107', modelo: '2RA', fechaEntrega: '2027-10-08', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 20.842, precioSinAcabados: 4415000, precioConAcabados: 4579000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-108', modelo: '2RA', fechaEntrega: '2027-10-08', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 18.97, precioSinAcabados: 4415000, precioConAcabados: 4579000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-109', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.13, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-110', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.13, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-111', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.13, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-112', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.13, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-113', modelo: '1R', fechaEntrega: '2027-10-08', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 9.13, precioSinAcabados: 3107108.39, precioConAcabados: 3269000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-114', modelo: '2RA', fechaEntrega: '2027-10-08', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 18.476, precioSinAcabados: 4415000, precioConAcabados: 4579000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-201', modelo: '2RA', fechaEntrega: '2027-10-15', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 3960000, precioConAcabados: 4127000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-202', modelo: '1R', fechaEntrega: '2027-10-15', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-203', modelo: '1R', fechaEntrega: '2027-10-22', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-204', modelo: '1R', fechaEntrega: '2027-10-22', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-205', modelo: '1R', fechaEntrega: '2027-10-22', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-206', modelo: '1R', fechaEntrega: '2027-10-29', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-207', modelo: '2RA', fechaEntrega: '2027-10-29', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 3960000, precioConAcabados: 4127000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-208', modelo: '2RA', fechaEntrega: '2027-10-29', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 3960000, precioConAcabados: 4127000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-209', modelo: '1R', fechaEntrega: '2027-10-29', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-210', modelo: '1R', fechaEntrega: '2027-10-29', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-211', modelo: '1R', fechaEntrega: '2027-11-05', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-212', modelo: '1R', fechaEntrega: '2027-11-05', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-213', modelo: '1R', fechaEntrega: '2027-11-12', supEstacionamiento: 14.31, supConstruccion: 47.87, supExcedente: 0, precioSinAcabados: 2850000, precioConAcabados: 2950000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-214', modelo: '2RA', fechaEntrega: '2027-11-12', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 3960000, precioConAcabados: 4127000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-301', modelo: '2RA', fechaEntrega: '2027-11-12', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 4150000, precioConAcabados: 4315000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-302', modelo: '2RB', fechaEntrega: '2027-11-12', supEstacionamiento: 28.62, supConstruccion: 68.757, supExcedente: 0, precioSinAcabados: 4120000, precioConAcabados: 4280000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-303', modelo: '2RC', fechaEntrega: '2027-11-19', supEstacionamiento: 28.62, supConstruccion: 72.328, supExcedente: 0, precioSinAcabados: 4300000, precioConAcabados: 4450000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-304', modelo: '2RC', fechaEntrega: '2027-11-19', supEstacionamiento: 28.62, supConstruccion: 72.328, supExcedente: 0, precioSinAcabados: 4300000, precioConAcabados: 4450000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-305', modelo: '2RA', fechaEntrega: '2027-11-19', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 4150000, precioConAcabados: 4315000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-306', modelo: '2RA', fechaEntrega: '2027-11-19', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 4150000, precioConAcabados: 4315000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-307', modelo: '2RC', fechaEntrega: '2027-11-26', supEstacionamiento: 28.62, supConstruccion: 72.328, supExcedente: 0, precioSinAcabados: 4300000, precioConAcabados: 4450000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-308', modelo: '2RC', fechaEntrega: '2027-11-26', supEstacionamiento: 28.62, supConstruccion: 72.328, supExcedente: 0, precioSinAcabados: 4300000, precioConAcabados: 4450000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-309', modelo: '2RB', fechaEntrega: '2027-11-26', supEstacionamiento: 28.62, supConstruccion: 68.757, supExcedente: 0, precioSinAcabados: 4120000, precioConAcabados: 4280000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, + { codigo: 'U-310', modelo: '2RA', fechaEntrega: '2027-11-26', supEstacionamiento: 28.62, supConstruccion: 69.485, supExcedente: 0, precioSinAcabados: 4150000, precioConAcabados: 4315000, status: 'DISPONIBLE', desarrollo: 'DIAGONAL' }, +] + +/** Inventario local completo (fallback cuando el API no responde) */ +export const INVENTARIO_SEED: Unidad[] = [ + ...UNIDADES_SEED, + ...UNIDADES_ATELIER, + ...UNIDADES_ESTANZA, + ...UNIDADES_SENDA, + ...UNIDADES_SOLANA, ] diff --git a/apps/cotizador/src/hooks/useCotizacion.ts b/apps/cotizador/src/hooks/useCotizacion.ts index eee6b03..d188b6c 100644 --- a/apps/cotizador/src/hooks/useCotizacion.ts +++ b/apps/cotizador/src/hooks/useCotizacion.ts @@ -1,15 +1,19 @@ import { useMemo, useState } from 'react' -import type { Unidad, DescuentosPropuestas } from '../types' +import type { Unidad, DescuentosPropuestas, ParametrosDesarrollo } from '../types' import { APARTADO_DEFAULT, - DESCUENTOS_DEFAULT, - calcularNumMensualidades, calcularPropuestas, obtenerPrecioLista, + resolverNumMensualidades, type PropuestaResultado, } from '../lib/calculations' +import { DESARROLLO_DEFAULT, descuentosDefault, obtenerDesarrollo } from '../data/desarrollos' interface UseCotizacionResult { + desarrollo: string + /** Cambia de desarrollo: limpia la unidad elegida y reinicia los descuentos */ + setDesarrollo: (id: string) => void + params: ParametrosDesarrollo unidad: Unidad | null setUnidad: (u: Unidad | null) => void acabados: boolean @@ -19,24 +23,34 @@ interface UseCotizacionResult { descuentos: DescuentosPropuestas setDescuento: (clave: keyof DescuentosPropuestas, valor: number) => void precioLista: number - numMeses: number + numMeses: number | null propuestas: PropuestaResultado[] } -/** Estado central de la cotización: unidad, acabados, apartado y descuentos */ +/** Estado central de la cotización: desarrollo, unidad, acabados, apartado y descuentos */ export function useCotizacion(): UseCotizacionResult { + const [desarrollo, setDesarrolloState] = useState(DESARROLLO_DEFAULT) const [unidad, setUnidad] = useState(null) const [acabados, setAcabados] = useState(false) const [apartado, setApartado] = useState(APARTADO_DEFAULT) - const [descuentos, setDescuentos] = useState({ ...DESCUENTOS_DEFAULT }) + const [descuentos, setDescuentos] = useState(descuentosDefault(DESARROLLO_DEFAULT)) + + const params = obtenerDesarrollo(desarrollo) + + const setDesarrollo = (id: string) => { + setDesarrolloState(id) + setUnidad(null) + setAcabados(false) + setDescuentos(descuentosDefault(id)) + } const setDescuento = (clave: keyof DescuentosPropuestas, valor: number) => { setDescuentos((prev) => ({ ...prev, [clave]: valor })) } const numMeses = useMemo( - () => (unidad ? calcularNumMensualidades(unidad.fechaEntrega) : 0), - [unidad], + () => (unidad ? resolverNumMensualidades(unidad, params) : null), + [unidad, params], ) const precioLista = useMemo( @@ -46,10 +60,13 @@ export function useCotizacion(): UseCotizacionResult { const propuestas = useMemo(() => { if (!unidad) return [] - return calcularPropuestas({ unidad, acabados, apartado, descuentos }) - }, [unidad, acabados, apartado, descuentos]) + return calcularPropuestas({ unidad, acabados, apartado, descuentos, desarrollo }) + }, [unidad, acabados, apartado, descuentos, desarrollo]) return { + desarrollo, + setDesarrollo, + params, unidad, setUnidad, acabados, diff --git a/apps/cotizador/src/lib/calculations-desarrollos.test.ts b/apps/cotizador/src/lib/calculations-desarrollos.test.ts new file mode 100644 index 0000000..3a5bad9 --- /dev/null +++ b/apps/cotizador/src/lib/calculations-desarrollos.test.ts @@ -0,0 +1,291 @@ +import { describe, it, expect } from 'vitest' +import { + calcularPropuestas, + obtenerPrecioLista, + resolverNumMensualidades, + totalPagado, + APARTADO_DEFAULT, +} from './calculations' +import { DESARROLLOS, descuentosDefault, obtenerDesarrollo } from '../data/desarrollos' +import { UNIDADES_ATELIER } from '../data/unidades-atelier' +import { UNIDADES_ESTANZA } from '../data/unidades-estanza' +import { UNIDADES_SENDA } from '../data/unidades-senda' +import { UNIDADES_SOLANA } from '../data/unidades-solana' +import type { Unidad } from '../types' + +/** + * Fixtures validados al centavo contra los Excel de producción + * (ver reference/cotizadores/{ATELIER,ESTANZA,SENDA,SOLANA}.md). + * Orden de propuestas en la config: [bancario, infonavit/cofinavit, contado, semi]. + */ + +function porCodigo(unidades: Unidad[], codigo: string): Unidad { + const u = unidades.find((x) => x.codigo === codigo) + if (!u) throw new Error(`Fixture ${codigo} no existe en el seed`) + return u +} + +describe('ATELIER — depto 10 (L = 3,997,500, apartado 20,000, N fijo = 1)', () => { + const unidad = porCodigo(UNIDADES_ATELIER, 'ATELIER-10') + const calcular = () => + calcularPropuestas({ + unidad, + acabados: false, + apartado: APARTADO_DEFAULT, + descuentos: descuentosDefault('ATELIER'), + }) + + it('precio único: no depende de acabados', () => { + expect(obtenerPrecioLista(unidad, true)).toBe(3997500) + expect(obtenerPrecioLista(unidad, false)).toBe(3997500) + }) + + it('resuelve N = 1 fijo (entrega inmediata)', () => { + expect(resolverNumMensualidades(unidad, DESARROLLOS['ATELIER'])).toBe(1) + }) + + it('BANCARIO: enganche 5% sobre LISTA; mensualidad = remanente en 1 pago', () => { + const [p] = calcular() + expect(p.enganche).toBeCloseTo(179875, 2) + expect(p.mensualidadesTotal).toBeCloseTo(199875, 2) + expect(p.mensualidad).toBeCloseTo(199875, 2) + expect(p.numMensualidades).toBe(1) + expect(p.pagoFinal).toBeCloseTo(3597750, 2) + }) + + it('COFINAVIT: estructura Infonavit sin quirk (todo sobre precio final)', () => { + const [, p] = calcular() + expect(p.titulo).toBe('Crédito Cofinavit') + expect(p.enganche).toBeCloseTo(79937.5, 2) + expect(p.mensualidadesTotal).toBeCloseTo(99937.5, 2) + expect(p.mensualidad).toBeCloseTo(99937.5, 2) + expect(p.pagoFinal).toBeCloseTo(3797625, 2) + expect(totalPagado(p, APARTADO_DEFAULT)).toBeCloseTo(3997500, 2) + }) + + it('CONTADO', () => { + const [, , p] = calcular() + expect(p.enganche).toBeCloseTo(3577750, 2) + expect(p.pagoFinal).toBeCloseTo(399750, 2) + expect(p.mensualidad).toBeNull() + }) + + it('SEMI CONTADO', () => { + const [, , , p] = calcular() + expect(p.enganche).toBeCloseTo(1978750, 2) + expect(p.mensualidadesTotal).toBeCloseTo(1599000, 2) + expect(p.pagoFinal).toBeCloseTo(399750, 2) + }) + + it('bancario cuadra: apartado + enganche + mensualidades + pago final = precio final', () => { + const [p] = calcular() + expect(totalPagado(p, APARTADO_DEFAULT)).toBeCloseTo(3997500, 2) + }) +}) + +describe('ESTANZA — casa 52 (L = 2,437,250, apartado 20,000, N fijo = 1, desc 0)', () => { + const unidad = porCodigo(UNIDADES_ESTANZA, 'ESTANZA-52') + const calcular = () => + calcularPropuestas({ + unidad, + acabados: false, + apartado: APARTADO_DEFAULT, + descuentos: descuentosDefault('ESTANZA'), + }) + + it('BANCARIO: enganche 2.5% sobre final; pago final 90% sobre LISTA (quirk)', () => { + const [p] = calcular() + expect(p.enganche).toBeCloseTo(40931.25, 2) + expect(p.mensualidadesTotal).toBeCloseTo(182793.75, 2) + expect(p.mensualidad).toBeCloseTo(182793.75, 2) + expect(p.pagoFinal).toBeCloseTo(2193525, 2) + }) + + it('INFONAVIT: pago final 95% sobre LISTA (quirk)', () => { + const [, p] = calcular() + expect(p.enganche).toBeCloseTo(40931.25, 2) + expect(p.mensualidadesTotal).toBeCloseTo(60931.25, 2) + expect(p.pagoFinal).toBeCloseTo(2315387.5, 2) + }) + + it('CONTADO', () => { + const [, , p] = calcular() + expect(p.enganche).toBeCloseTo(2173525, 2) + expect(p.pagoFinal).toBeCloseTo(243725, 2) + }) + + it('SEMI CONTADO', () => { + const [, , , p] = calcular() + expect(p.enganche).toBeCloseTo(1198625, 2) + expect(p.mensualidadesTotal).toBeCloseTo(974900, 2) + expect(p.pagoFinal).toBeCloseTo(243725, 2) + }) +}) + +describe('SENDA — 411-1-7 (L = 2,451,750, apartado 0 en el Excel, N = 4)', () => { + const unidad = porCodigo(UNIDADES_SENDA, 'SENDA-411-1-7') + const calcular = () => + calcularPropuestas({ + unidad, + acabados: false, + apartado: 0, // el Excel de SENDA traía MONTO APARTADO (C15) en 0 + descuentos: descuentosDefault('SENDA'), + numMeses: 4, + }) + + it('BANCARIO: enganche 5%, mensualidades 5% ÷ N, pago final 90%', () => { + const [p] = calcular() + expect(p.enganche).toBeCloseTo(122587.5, 2) + expect(p.mensualidadesTotal).toBeCloseTo(122587.5, 2) + expect(p.mensualidad).toBeCloseTo(30646.875, 3) + expect(p.pagoFinal).toBeCloseTo(2206575, 2) + }) + + it('INFONAVIT: sin quirk de lista — mensualidades sobre precio final', () => { + const [, p] = calcular() + expect(p.enganche).toBeCloseTo(61293.75, 2) + expect(p.mensualidadesTotal).toBeCloseTo(61293.75, 2) + expect(p.mensualidad).toBeCloseTo(15323.4375, 4) + expect(p.pagoFinal).toBeCloseTo(2329162.5, 2) + }) + + it('CONTADO (desc default 2%)', () => { + const [, , p] = calcular() + expect(p.precioFinal).toBeCloseTo(2402715, 2) + expect(p.enganche).toBeCloseTo(2162443.5, 2) + expect(p.pagoFinal).toBeCloseTo(240271.5, 2) + }) + + it('SEMI CONTADO (desc default 1%, split 50/45/5)', () => { + const [, , , p] = calcular() + expect(p.precioFinal).toBeCloseTo(2427232.5, 2) + expect(p.enganche).toBeCloseTo(1213616.25, 2) + expect(p.mensualidadesTotal).toBeCloseTo(1092254.625, 3) + expect(p.mensualidad).toBeCloseTo(273063.65625, 5) + expect(p.pagoFinal).toBeCloseTo(121361.625, 3) + }) +}) + +describe('SENDA — unidad sin fecha de entrega (411-25-A101)', () => { + const unidad = porCodigo(UNIDADES_SENDA, 'SENDA-411-25-A101') + + it('N = null y mensualidades "—" con nota; enganche y pago final sí se calculan', () => { + expect(unidad.fechaEntrega).toBeNull() + const [bancario] = calcularPropuestas({ + unidad, + acabados: false, + apartado: APARTADO_DEFAULT, + descuentos: descuentosDefault('SENDA'), + }) + expect(bancario.numMensualidades).toBeNull() + expect(bancario.mensualidad).toBeNull() + expect(bancario.mensualidadesTotal).toBeNull() + expect(bancario.notaMensualidades).toBe('sin fecha de entrega') + expect(bancario.enganche).toBeCloseTo(0.05 * 1948000 - APARTADO_DEFAULT, 2) + expect(bancario.pagoFinal).toBeCloseTo(0.9 * 1948000, 2) + expect(totalPagado(bancario, APARTADO_DEFAULT)).toBeNull() + }) +}) + +describe('SOLANA — casa 6 (L = 2,560,000, apartado 20,000, mesesParaFirma = 1)', () => { + const unidad = porCodigo(UNIDADES_SOLANA, 'SOLANA-6') + const calcular = () => + calcularPropuestas({ + unidad, + acabados: false, + apartado: APARTADO_DEFAULT, + descuentos: descuentosDefault('SOLANA'), + }) + + it('N viene de mesesParaFirma de la unidad', () => { + expect(unidad.mesesParaFirma).toBe(1) + expect(resolverNumMensualidades(unidad, DESARROLLOS['SOLANA'])).toBe(1) + }) + + it('BANCARIO: enganche 5% sobre final', () => { + const [p] = calcular() + expect(p.enganche).toBeCloseTo(108000, 2) + expect(p.mensualidadesTotal).toBeCloseTo(128000, 2) + expect(p.pagoFinal).toBeCloseTo(2304000, 2) + }) + + it('INFONAVIT: enganche 0% con guarda (no resta el apartado)', () => { + const [, p] = calcular() + expect(p.enganche).toBe(0) + expect(p.mensualidadesTotal).toBeCloseTo(128000, 2) + expect(p.pagoFinal).toBeCloseTo(2432000, 2) + }) + + it('CONTADO', () => { + const [, , p] = calcular() + expect(p.enganche).toBeCloseTo(2284000, 2) + expect(p.pagoFinal).toBeCloseTo(256000, 2) + }) + + it('SEMI CONTADO', () => { + const [, , , p] = calcular() + expect(p.enganche).toBeCloseTo(1260000, 2) + expect(p.mensualidadesTotal).toBeCloseTo(1024000, 2) + expect(p.pagoFinal).toBeCloseTo(256000, 2) + }) +}) + +describe('SOLANA — casa 9 (L = 2,605,000, mesesParaFirma = 1)', () => { + const unidad = porCodigo(UNIDADES_SOLANA, 'SOLANA-9') + const calcular = () => + calcularPropuestas({ + unidad, + acabados: false, + apartado: APARTADO_DEFAULT, + descuentos: descuentosDefault('SOLANA'), + }) + + it('las 4 propuestas al centavo', () => { + const [bancario, infonavit, contado, semi] = calcular() + expect(bancario.enganche).toBeCloseTo(110250, 2) + expect(bancario.mensualidadesTotal).toBeCloseTo(130250, 2) + expect(bancario.pagoFinal).toBeCloseTo(2344500, 2) + expect(infonavit.enganche).toBe(0) + expect(infonavit.mensualidadesTotal).toBeCloseTo(130250, 2) + expect(infonavit.pagoFinal).toBeCloseTo(2474750, 2) + expect(contado.enganche).toBeCloseTo(2324500, 2) + expect(contado.pagoFinal).toBeCloseTo(260500, 2) + expect(semi.enganche).toBeCloseTo(1282500, 2) + expect(semi.mensualidadesTotal).toBeCloseTo(1042000, 2) + expect(semi.pagoFinal).toBeCloseTo(260500, 2) + }) +}) + +describe('configuración de desarrollos', () => { + it('los 5 desarrollos tienen 4 propuestas', () => { + for (const id of ['DIAGONAL', 'ATELIER', 'ESTANZA', 'SENDA', 'SOLANA']) { + expect(DESARROLLOS[id].propuestas).toHaveLength(4) + } + }) + + it('id desconocido cae a DIAGONAL', () => { + expect(obtenerDesarrollo('NO_EXISTE').id).toBe('DIAGONAL') + expect(obtenerDesarrollo(undefined).id).toBe('DIAGONAL') + }) + + it('descuentos default por desarrollo', () => { + expect(descuentosDefault('DIAGONAL')).toEqual({ + creditoBancario: 0, + creditoInfonavit: 0, + contado: 0.02, + semiContado: 0.01, + }) + expect(descuentosDefault('SENDA')).toEqual({ + creditoBancario: 0, + creditoInfonavit: 0, + contado: 0.02, + semiContado: 0.01, + }) + expect(descuentosDefault('ATELIER')).toEqual({ + creditoBancario: 0, + creditoInfonavit: 0, + contado: 0, + semiContado: 0, + }) + }) +}) diff --git a/apps/cotizador/src/lib/calculations.test.ts b/apps/cotizador/src/lib/calculations.test.ts index 49ed950..a47eaf0 100644 --- a/apps/cotizador/src/lib/calculations.test.ts +++ b/apps/cotizador/src/lib/calculations.test.ts @@ -129,7 +129,7 @@ describe('totalPagado', () => { describe('indiceMenorTotal', () => { it('devuelve el índice de la propuesta con menor total pagado', () => { const propuestas = calcular() - const totales = propuestas.map((p) => totalPagado(p, APARTADO_DEFAULT)) + const totales = propuestas.map((p) => totalPagado(p, APARTADO_DEFAULT) ?? 0) // Contado (2% desc) tiene el menor total pagado expect(indiceMenorTotal(totales)).toBe(2) }) diff --git a/apps/cotizador/src/lib/calculations.ts b/apps/cotizador/src/lib/calculations.ts index 23502e7..d8404a7 100644 --- a/apps/cotizador/src/lib/calculations.ts +++ b/apps/cotizador/src/lib/calculations.ts @@ -1,15 +1,27 @@ -import type { Unidad, TipoPropuesta, DescuentosPropuestas } from '../types' +import type { + Unidad, + TipoPropuesta, + DescuentosPropuestas, + ParametrosDesarrollo, + PropuestaConfig, +} from '../types' +import { DESARROLLO_DEFAULT, obtenerDesarrollo } from '../data/desarrollos' /** - * Cálculos replicados fielmente del Excel de producción "COTIZADOR" de - * Frasa Desarrollos (ya validado). NO "corregir" las fórmulas: algunas + * Cálculos replicados fielmente de los Excel de producción "COTIZADOR" de + * Frasa Desarrollos (ya validados). NO "corregir" las fórmulas: algunas * usan precio de lista donde intuitivamente iría precio final; así está - * en el Excel y así debe quedar. + * en los Excel y así debe quedar. + * + * El motor está parametrizado por desarrollo (ver src/data/desarrollos.ts); + * con los parámetros de DIAGONAL reproduce byte a byte las fórmulas + * originales de este archivo. */ export const APARTADO_DEFAULT = 20000 -// Descuentos por defecto de cada propuesta (fracción, no porcentaje) +// Descuentos por defecto de DIAGONAL (fracción, no porcentaje). +// Para otros desarrollos usar descuentosDefault(desarrollo) de data/desarrollos. export const DESCUENTOS_DEFAULT: DescuentosPropuestas = { creditoBancario: 0, creditoInfonavit: 0, @@ -19,22 +31,28 @@ export const DESCUENTOS_DEFAULT: DescuentosPropuestas = { export interface PropuestaResultado { tipo: TipoPropuesta + /** Título para mostrar, viene de la config del desarrollo (p. ej. 'Crédito Cofinavit') */ + titulo: string descuento: number // fracción (0.02 = 2%) montoDescuento: number precioFinal: number // precio lista - descuento enganche: number // NETO del apartado (el apartado ya se pagó) - numMensualidades: number - mensualidad: number | null // null => N/A (Contado) - mensualidadesTotal: number | null // null => N/A (Contado) + numMensualidades: number | null // null => sin fecha de entrega / plazo desconocido + mensualidad: number | null // null => N/A (Contado) o plazo desconocido + mensualidadesTotal: number | null // null => N/A (Contado) o plazo desconocido pagoFinal: number + /** Nota explicativa cuando no se pueden calcular mensualidades ('sin fecha de entrega') */ + notaMensualidades?: string } export interface CotizacionInput { unidad: Unidad - acabados: boolean // true => incluye cocina y closet + acabados: boolean // true => incluye cocina y closet (ignorado en desarrollos de precio único) apartado: number descuentos: DescuentosPropuestas - /** Permite forzar N en tests; por defecto se calcula desde fechaEntrega */ + /** Desarrollo cuyos parámetros aplican; por defecto unidad.desarrollo ?? 'DIAGONAL' */ + desarrollo?: string + /** Permite forzar N en tests; por defecto se resuelve según la fuenteDeN del desarrollo */ numMeses?: number } @@ -46,6 +64,26 @@ export function calcularNumMensualidades(fechaEntrega: string, hoy: Date = new D return Math.max(1, Math.round((entrega.getTime() - hoy.getTime()) / MS_POR_MES)) } +/** + * Resuelve N según la fuente del desarrollo: + * - fijo: nFijo (entrega inmediata) + * - porUnidad: mesesParaFirma del inventario (null si falta) + * - fechaEntrega: meses desde hoy (null si la unidad no tiene fecha) + */ +export function resolverNumMensualidades( + unidad: Unidad, + params: ParametrosDesarrollo, +): number | null { + switch (params.fuenteDeN) { + case 'fijo': + return params.nFijo ?? 1 + case 'porUnidad': + return unidad.mesesParaFirma ?? null + case 'fechaEntrega': + return unidad.fechaEntrega ? calcularNumMensualidades(unidad.fechaEntrega) : null + } +} + function baseComun(precioLista: number, descuento: number) { // Excel COTIZADOR: MONTO DE DESCUENTO y PRECIO FINAL C/DESC const montoDescuento = precioLista * descuento @@ -53,102 +91,82 @@ function baseComun(precioLista: number, descuento: number) { return { montoDescuento, precioFinal } } -/** PROPUESTA 1 — CRÉDITO BANCARIO */ -function calcularBancario(precioLista: number, apartado: number, descuento: number, n: number): PropuestaResultado { +/** Calcula una propuesta comercial con los parámetros dados */ +function calcularPropuestaConfig( + cfg: PropuestaConfig, + precioLista: number, + apartado: number, + descuento: number, + n: number | null, +): PropuestaResultado { const { montoDescuento, precioFinal } = baseComun(precioLista, descuento) - // Excel COTIZADOR!M17 — enganche 2.5% sobre precio LISTA, neto del apartado - const enganche = 0.025 * precioLista - apartado - // Excel COTIZADOR!M19 — pago final 90% del precio final c/desc - const pagoFinal = 0.9 * precioFinal - // Excel COTIZADOR!M18 — remanente a mensualidades - const mensualidadesTotal = precioFinal - apartado - enganche - pagoFinal + + // Enganche: pct sobre su base, neto del apartado (salvo la guarda de SOLANA) + const baseEnganche = cfg.engancheBase === 'lista' ? precioLista : precioFinal + const enganche = + cfg.engancheCeroSinApartado && cfg.pctEnganche === 0 + ? 0 + : cfg.pctEnganche * baseEnganche - apartado + + const basePagoFinal = cfg.pagoFinalBase === 'lista' ? precioLista : precioFinal + const pagoFinal = cfg.pctPagoFinal * basePagoFinal + + let mensualidadesTotal: number | null = null + let mensualidad: number | null = null + let notaMensualidades: string | undefined + if (cfg.modoMensualidades !== 'ninguna') { + if (n === null) { + // Unidad sin fecha de entrega / plazo: el Excel mostraría #N/A + notaMensualidades = 'sin fecha de entrega' + } else { + switch (cfg.modoMensualidades) { + case 'remanente': + mensualidadesTotal = precioFinal - apartado - enganche - pagoFinal + break + case 'remanenteLista': + // Quirk DIAGONAL Infonavit: el Excel usa precioLista aquí, NO precioFinal + mensualidadesTotal = precioLista - pagoFinal - apartado - enganche + break + case 'porcentaje': + mensualidadesTotal = precioFinal * (1 - cfg.pctEnganche - cfg.pctPagoFinal) + break + } + mensualidad = mensualidadesTotal / n + } + } + return { - tipo: 'CREDITO_BANCARIO', + tipo: cfg.tipo, + titulo: cfg.titulo, descuento, montoDescuento, precioFinal, enganche, numMensualidades: n, - mensualidad: mensualidadesTotal / n, + mensualidad, mensualidadesTotal, pagoFinal, + ...(notaMensualidades !== undefined ? { notaMensualidades } : {}), } } -/** PROPUESTA 2 — CRÉDITO INFONAVIT-COFINAVIT */ -function calcularInfonavit(precioLista: number, apartado: number, descuento: number, n: number): PropuestaResultado { - const { montoDescuento, precioFinal } = baseComun(precioLista, descuento) - // Excel COTIZADOR!M26 — enganche 2.5% sobre precio LISTA, neto del apartado - const enganche = 0.025 * precioLista - apartado - // Excel COTIZADOR!M28 — pago final 95% del precio final c/desc - const pagoFinal = 0.95 * precioFinal - // Excel COTIZADOR!M27 — OJO: el Excel usa precioLista aquí, NO precioFinal. Replicar tal cual. - const mensualidadesTotal = precioLista - pagoFinal - apartado - enganche - return { - tipo: 'CREDITO_INFONAVIT_COFINAVIT', - descuento, - montoDescuento, - precioFinal, - enganche, - numMensualidades: n, - mensualidad: mensualidadesTotal / n, - mensualidadesTotal, - pagoFinal, - } -} - -/** PROPUESTA 3 — CONTADO (sin mensualidades) */ -function calcularContado(precioLista: number, apartado: number, descuento: number, n: number): PropuestaResultado { - const { montoDescuento, precioFinal } = baseComun(precioLista, descuento) - // Excel COTIZADOR!M36 — enganche 90% del precio final c/desc, neto del apartado - const enganche = 0.9 * precioFinal - apartado - // Excel COTIZADOR!M37 — pago final 10% del precio final c/desc - const pagoFinal = 0.1 * precioFinal - return { - tipo: 'CONTADO', - descuento, - montoDescuento, - precioFinal, - enganche, - numMensualidades: n, - mensualidad: null, // N/A - mensualidadesTotal: null, // N/A - pagoFinal, - } -} - -/** PROPUESTA 4 — SEMI CONTADO */ -function calcularSemiContado(precioLista: number, apartado: number, descuento: number, n: number): PropuestaResultado { - const { montoDescuento, precioFinal } = baseComun(precioLista, descuento) - // Excel COTIZADOR!M45 — enganche 50% del precio final c/desc, neto del apartado - const enganche = 0.5 * precioFinal - apartado - // Excel COTIZADOR!M46 — 40% del precio final a mensualidades (= 1 - 0.5 - 0.1) - const mensualidadesTotal = precioFinal * 0.4 - // Excel COTIZADOR!M47 — pago final 10% del precio final c/desc - const pagoFinal = 0.1 * precioFinal - return { - tipo: 'SEMI_CONTADO', - descuento, - montoDescuento, - precioFinal, - enganche, - numMensualidades: n, - mensualidad: mensualidadesTotal / n, - mensualidadesTotal, - pagoFinal, - } -} - -/** Precio de lista según si incluye cocina y closet (acabados) */ +/** + * Precio de lista según si incluye cocina y closet (acabados). + * En desarrollos de precio único la unidad trae `precioLista` y no hay + * distinción de acabados. + */ export function obtenerPrecioLista(unidad: Unidad, acabados: boolean): number { + if (unidad.precioLista != null) return unidad.precioLista return acabados ? unidad.precioConAcabados : unidad.precioSinAcabados } /** * Total pagado por el cliente en una propuesta: * apartado + enganche (neto del apartado) + mensualidades + pago final. + * Devuelve null cuando el total no se puede conocer (plazo desconocido). */ -export function totalPagado(p: PropuestaResultado, apartado: number): number { +export function totalPagado(p: PropuestaResultado, apartado: number): number | null { + if (p.notaMensualidades) return null return apartado + p.enganche + (p.mensualidadesTotal ?? 0) + p.pagoFinal } @@ -162,15 +180,13 @@ export function indiceMenorTotal(totales: number[]): number { return indice } -/** Calcula las 4 propuestas comerciales */ +/** Calcula las propuestas comerciales del desarrollo de la unidad */ export function calcularPropuestas(input: CotizacionInput): PropuestaResultado[] { const { unidad, acabados, apartado, descuentos } = input + const params = obtenerDesarrollo(input.desarrollo ?? unidad.desarrollo ?? DESARROLLO_DEFAULT) const precioLista = obtenerPrecioLista(unidad, acabados) - const n = input.numMeses ?? calcularNumMensualidades(unidad.fechaEntrega) - return [ - calcularBancario(precioLista, apartado, descuentos.creditoBancario, n), - calcularInfonavit(precioLista, apartado, descuentos.creditoInfonavit, n), - calcularContado(precioLista, apartado, descuentos.contado, n), - calcularSemiContado(precioLista, apartado, descuentos.semiContado, n), - ] + const n = input.numMeses ?? resolverNumMensualidades(unidad, params) + return params.propuestas.map((cfg) => + calcularPropuestaConfig(cfg, precioLista, apartado, descuentos[cfg.claveDescuento], n), + ) } diff --git a/apps/cotizador/src/lib/pdf.ts b/apps/cotizador/src/lib/pdf.ts index c47bab6..26cf1fa 100644 --- a/apps/cotizador/src/lib/pdf.ts +++ b/apps/cotizador/src/lib/pdf.ts @@ -1,8 +1,9 @@ import { jsPDF } from 'jspdf' import autoTable from 'jspdf-autotable' -import type { Unidad, Cliente } from '../types' +import type { Unidad, Cliente, ParametrosDesarrollo } from '../types' import type { PropuestaResultado } from './calculations' import { formatoMoneda, formatoPorcentaje, formatoFechaLarga, fechaISOHoy } from './formatters' +import { DESARROLLOS, DESARROLLO_DEFAULT } from '../data/desarrollos' export interface PdfInput { unidad: Unidad @@ -11,14 +12,11 @@ export interface PdfInput { precioLista: number propuestas: PropuestaResultado[] cliente: Cliente + /** Parámetros del desarrollo; por defecto DIAGONAL */ + desarrollo?: ParametrosDesarrollo } -const TITULOS: Record = { - CREDITO_BANCARIO: 'CREDITO BANCARIO', - CREDITO_INFONAVIT_COFINAVIT: 'CREDITO INFONAVIT-COFINAVIT', - CONTADO: 'CONTADO', - SEMI_CONTADO: 'SEMI CONTADO', -} +const formatoM2Pdf = (v: number | null): string => (v === null ? '—' : `${v} m2`) const DISCLAIMER = 'ESTA COTIZACION NO INCLUYE GASTOS DE ESCRITURACION. LOS VALORES ANTERIORES SON EN PESOS, MERAMENTE INFORMATIVOS, POR LO QUE ESTE DOCUMENTO NO CONSTITUYE PREAPROBACIÓN Y POR LO TANTO NO COMPROMETE AL DESARROLLADOR.\n\nMONTOS, PORCENTAJES Y PRECIOS EN LA PRESENTE COTIZACIÓN PUEDEN CAMBIAR SIN PREVIO AVISO.\n\nEL MONTO TOTAL DE ENGANCHE INCLUYE LA DEDUCCIÓN DEL MONTO DE APARTADO.\n\nLA PRESENTE COTIZACIÓN TIENE UNA VIGENCIA DE 3 DÍAS NATURALES A PARTIR DE LA FECHA DE EMISIÓN, PARA CONGELAR EL PRECIO DE VENTA ES NECESARIO REALIZAR EL PAGO DE SU APARTADO COMPLETO Y TENDRÁ 7 DÍAS PARA FORMALIZAR Y NO PERDER LA UBICACIÓN.' @@ -41,7 +39,8 @@ export interface DocumentoPdf { } /** Construye el documento PDF "ANEXO A" (sin descargarlo) */ -export function crearDocumentoPdf({ unidad, acabados, apartado, precioLista, propuestas, cliente }: PdfInput): DocumentoPdf { +export function crearDocumentoPdf({ unidad, acabados, apartado, precioLista, propuestas, cliente, desarrollo }: PdfInput): DocumentoPdf { + const params = desarrollo ?? DESARROLLOS[DESARROLLO_DEFAULT] const doc = new jsPDF() const hoy = new Date() const limiteDocumentos = new Date(hoy.getTime() + 7 * 86400000) @@ -69,6 +68,9 @@ export function crearDocumentoPdf({ unidad, acabados, apartado, precioLista, pro doc.setFontSize(10) doc.setFont('helvetica', 'normal') doc.text(formatoFechaLarga(hoy), 105, y, { align: 'center' }) + y += 5 + doc.setFont('helvetica', 'bold') + doc.text(`DESARROLLO ${params.nombre.toUpperCase()}`, 105, y, { align: 'center' }) y += 8 // 2. Tabla de unidad @@ -82,16 +84,16 @@ export function crearDocumentoPdf({ unidad, acabados, apartado, precioLista, pro 'MONTO APARTADO $ MN', 'CUOTA MANTENIMIENTO', ]], body: [[ - `CASA ${unidad.codigo}`, + `${params.prefijoUnidadPdf} ${unidad.codigo}`, unidad.modelo, - acabados ? 'SI' : 'NO', + params.precioUnico ? 'N/A' : acabados ? 'SI' : 'NO', formatoMoneda(precioLista), unidad.status, - `${unidad.supConstruccion} m2`, - `${unidad.supExcedente} m2`, - `${unidad.supEstacionamiento} m2`, + formatoM2Pdf(unidad.supConstruccion), + formatoM2Pdf(unidad.supExcedente), + formatoM2Pdf(unidad.supEstacionamiento), formatoMoneda(apartado), - '1,500 Aprox.', + params.cuotaMantenimiento, ]], }) y = finalY(doc) + 8 @@ -103,8 +105,9 @@ export function crearDocumentoPdf({ unidad, acabados, apartado, precioLista, pro y += 4 propuestas.forEach((p, i) => { - const mensualidades = - p.mensualidad !== null && p.mensualidadesTotal !== null + const mensualidades = p.notaMensualidades + ? { pagos: `— (${p.notaMensualidades})`, total: '—' } + : p.mensualidad !== null && p.mensualidadesTotal !== null ? { pagos: `${p.numMensualidades} × ${formatoMoneda(p.mensualidad)}`, total: formatoMoneda(p.mensualidadesTotal), @@ -116,7 +119,7 @@ export function crearDocumentoPdf({ unidad, acabados, apartado, precioLista, pro margin: { left: MARGEN, right: MARGEN }, styles: { fontSize: 8 }, headStyles: { fillColor: [4, 120, 87] }, - head: [[`PROPUESTA COMERCIAL #${i + 1} — ${TITULOS[p.tipo]}`, '%', 'PAGOS MENSUALES', 'TOTAL']], + head: [[`PROPUESTA COMERCIAL #${i + 1} — ${p.titulo.toUpperCase()}`, '%', 'PAGOS MENSUALES', 'TOTAL']], body: [ ['DESCUENTO', formatoPorcentaje(p.descuento), '', ''], ['MONTO DE DESCUENTO', '', '', formatoMoneda(p.montoDescuento)], diff --git a/apps/cotizador/src/pages/CotizadorPage.tsx b/apps/cotizador/src/pages/CotizadorPage.tsx index 58c56f2..ae33fbb 100644 --- a/apps/cotizador/src/pages/CotizadorPage.tsx +++ b/apps/cotizador/src/pages/CotizadorPage.tsx @@ -10,6 +10,7 @@ import { ComparadorPropuestas } from '../components/ComparadorPropuestas' import { generarPdf } from '../lib/pdf' import { formatoMoneda, formatoFechaLarga } from '../lib/formatters' import { calcularVigencia } from '../lib/vigencia' +import { LISTA_DESARROLLOS, obtenerDesarrollo } from '../data/desarrollos' import type { Cliente } from '../types' const esquemaCliente = z.object({ @@ -22,13 +23,6 @@ const esquemaCliente = z.object({ type FormularioCliente = z.infer -const TITULOS_PROPUESTAS: [string, keyof import('../types').DescuentosPropuestas][] = [ - ['Crédito Bancario', 'creditoBancario'], - ['Crédito Infonavit-Cofinavit', 'creditoInfonavit'], - ['Contado', 'contado'], - ['Semi Contado', 'semiContado'], -] - const campoInputBase = 'w-full border bg-white px-3 py-2.5 text-sm transition-colors duration-150 focus:border-neutral-900 focus:outline-none' const campoInput = `${campoInputBase} border-neutral-300` @@ -40,6 +34,9 @@ export default function CotizadorPage() { const { unidades, cargando, error, usandoSeed } = useUnidades() const cotizacion = useCotizacion() const { + desarrollo, + setDesarrollo, + params, unidad, setUnidad, acabados, @@ -52,6 +49,9 @@ export default function CotizadorPage() { propuestas, } = cotizacion + const unidadesDesarrollo = unidades.filter((u) => (u.desarrollo ?? 'DIAGONAL') === desarrollo) + const disponiblesDesarrollo = unidadesDesarrollo.filter((u) => u.status === 'DISPONIBLE').length + const { register, watch, @@ -72,7 +72,7 @@ export default function CotizadorPage() { email: watch('email') || undefined, telefono: watch('telefono') || undefined, } - generarPdf({ unidad, acabados, apartado, precioLista, propuestas, cliente }) + generarPdf({ unidad, acabados, apartado, precioLista, propuestas, cliente, desarrollo: params }) } return ( @@ -105,15 +105,51 @@ export default function CotizadorPage() { {cargando ? (

Cargando unidades…

) : ( - - setUnidad(unidades.find((u) => u.codigo === codigo) ?? null) - } - /> + <> +
+ Desarrollo +
+ {LISTA_DESARROLLOS.map((id) => { + const activo = id === desarrollo + const total = unidades.filter((u) => (u.desarrollo ?? 'DIAGONAL') === id).length + const disponibles = unidades.filter( + (u) => (u.desarrollo ?? 'DIAGONAL') === id && u.status === 'DISPONIBLE', + ).length + return ( + + ) + })} +
+

+ {unidadesDesarrollo.length} unidades · {disponiblesDesarrollo} disponibles +

+
+ + setUnidad(unidadesDesarrollo.find((u) => u.codigo === codigo) ?? null) + } + /> + )} + {!params.precioUnico && (
Cocina y closet
@@ -137,6 +173,7 @@ export default function CotizadorPage() { })}
+ )}
@@ -237,7 +274,7 @@ export default function CotizadorPage() { precioLista={precioLista} apartado={apartado} propuestas={propuestas} - titulos={TITULOS_PROPUESTAS.map(([titulo]) => titulo)} + titulos={propuestas.map((p) => p.titulo)} />