# Tutorial · Presupuesto al cliente

Tutorial paso a paso sobre el módulo **Presupuesto al cliente** en
Despiece: armar un PDF branded con datos del cliente, cinco secciones de
costos (Mueble · Materiales · Herraje · Mano de obra · Extras) y el
total con margen y multiplicador de obra.

El modelo de ejemplo es `CocinaV9.despiece`. El módulo está separado
del flujo 2D (BOM/nest) y vive en un panel propio que se abre con
`Shift+Q` o el botón `[Q]` del topbar.

---

## 1. Cargar el modelo y abrir el panel

![Vista general al cargar el modelo](img/01-overview-presupuesto.jpg)

Al importar el STEP/IGES/BREP/glTF/GLB, la sidebar izquierda muestra el
**árbol jerárquico** (assemblies / piezas) y la cámara queda en vista
isométrica con `Home`. Para abrir el módulo Presupuesto hay dos formas:

- **Atajo `Shift+Q`** (también `⇧Q` en Mac).
- **Botón `[Q]`** del topbar entre Herraje y Lineales.

Se abre un **panel sheet full-screen** con header `Presupuesto` y
número correlativo. La primera sección visible es **Cliente** con los
datos cargados del catálogo del taller o del proyecto activo.

> **Módulo gated.** Si `Configuración → Presupuesto → Activar módulo
> Presupuesto` está apagado, el botón `[Q]` del topbar y `Shift+Q` no
> hacen nada — aparece el toast *"Módulo Presupuesto desactivado"*.
> Lo que sigue asume módulo **on** (default off — hay que activarlo).

## 2. Header y datos del cliente

![Header del panel + sección Cliente](img/02-header-cliente.jpg)

El header del panel expone:

- **Kicker** `Presupuesto` + **número** correlativo (default `Sin número`)
- **Fecha de emisión** y **fecha de vencimiento** (auto-calculada como
  `emisión + validez días`)
- **Botón Exportar PDF** (en la barra inferior; el header solo tiene el
  cierre)
- **Botón ×** para cerrar el panel

La sección **Cliente** debajo del header carga desde
`state.project.client` (catálogo per-proyecto o workshop-global
linkeado) y muestra: nombre, teléfono, email, dirección, CUIT y notas.
Click en el chevron colapsa/expande la sección (persiste en
`localStorage`).

> **Sin cliente configurado** la sección queda vacía con un botón
> **Abrir panel Cliente** que dispara `openClientPanel()` desde la
> topbar. Editar el cliente actualiza el presupuesto en vivo — sin
> recargar ni perder foco.

## 3. Sección Mueble

![Sección Mueble](img/03-seccion-mueble.jpg)

La sección **Mueble** resume el modelo cargado en una sola línea:

| Campo | Significado |
|-------|-------------|
| Descripción | Nombre del proyecto activo (editable vía `state.project.activeName`) |
| Cantidad | `state.settings.projectMultiplier` (default `1`) |
| Unidad | `unidad` |
| Unit price | Subtotal / ×N (sin margen) |
| Subtotal | Subtotal calculado sin multiplicar por ×N |

Si no hay modelo cargado, la sección queda con `warning: "Sin modelo
cargado"` y `computable: false`. Las notas del cliente se linkean al
PDF como descripción extendida de la línea Mueble.

## 4. Sección Materiales (overrides)

![Sección Materiales con overrides](img/04-seccion-materiales.jpg)

La sección **Materiales** agrega una línea por cada combinación
`(material × thickness)` del BOM actual. Las columnas son descripción
(auto-gen del catálogo), cantidad (m² o ml según tipo), unidad,
unit price (del `costPerM2` o `costPerMl` del catálogo), y subtotal.

> **Overrides por fila.** Cuando el catálogo del taller no tiene
> `costPerM2` para un material, la fila se renderiza con borde sutil
> (`is-override`) y permite tipear **Cant., $ U. y Descripción**
> directamente desde el panel. La multiplicación `cant × unitPrice` corre
> en vivo y los totales reflejan al instante. La key del override es
> `bucketKey(material)|thicknessMm` — el mismo que usa
> `src/export-purchase/row-builders.ts:27`.

El override vive en `parts.quoteDraft.materialesOverrides` (campo soft
en el `.despiece`). Si después cargás `costPerM2` en el catálogo, el
override sigue ganando hasta que se borre manualmente.

## 5. Sección Herraje

![Sección Herraje](img/05-seccion-herraje.jpg)

La sección **Herraje** agrega una línea por cada SKU del catálogo de
herraje del proyecto (`state.project.hardwareList[]`). Las columnas
son descripción, cantidad (`×N` global), unidad, unit price (del
`costPerUnit` del SKU), y subtotal.

Si el proyecto no tiene herraje configurado (`hardwareList.length === 0`),
la sección queda vacía con `warning: "Sin herraje"` y no aporta al
subtotal. Toggle **incluir** en el header de la sección la apaga del
PDF sin perder los datos.

## 6. Sección Mano de obra (cycle-time del nest)

![Sección Mano de obra con cycle-time](img/06-seccion-mano-obra.jpg)

La sección **Mano de obra** se calcula desde el **cycle-time** del último
resultado de nest (`state.nesting.lastResult.cycleTimeMinutes`) y la
**tarifa por hora** configurada en el tab **Corte** de Settings.

Fórmula:

```
mano_obra = (cycle_time_min / 60) × hourly_rate × projectMultiplier
```

> **Sin nest previo**, la sección queda bloqueada con el mensaje
> *"Calculá nest primero"* y `computable: false`. El resto del
> presupuesto se puede editar y exportar igual — la mano de obra es la
> única sección gateada por el nest.

La tarifa por hora (`hourlyRate`) es la misma del settings de corte,
aplicada por hora. Default `0` (la sección queda en $0 si no la
configurás).

## 7. Sección Extras (CRUD libre)

![Sección Extras con líneas](img/07-seccion-extras.jpg)

La sección **Extras** es un CRUD libre de líneas para agregar costos
que no entran en las otras secciones: flete, instalación, mano de obra
adicional, descuentos, etc.

Cada línea expone 4 inputs editables: **Descripción**, **Cant.**,
**Unidad**, **$ U.**. El subtotal se calcula como `cant × unitPrice` y
se actualiza en vivo. Botones `+ Agregar` y `×` (en cada row) para
agregar/quitar filas. Sin filas, la sección queda vacía y no aporta al
subtotal.

## 8. Totales: subtotal, margen, ×N, total

![Footer de totales](img/08-totales.jpg)

El **footer de totales** se renderiza debajo de las 5 secciones y
explica la cascada:

```
subtotal  = sum(sección.subtotal)
margen    = subtotal × marginPct / 100
total     = (subtotal + margen) × projectMultiplier
```

Tres inputs editables (sin recargar el panel):

| Campo | Rango | Default |
|-------|-------|---------|
| **Moneda** | string hasta 8 chars | `ARS` |
| **Margen (%)** | 0–200 | `30` |
| **×N (multiplicador de obra)** | 1–99 | `1` |

El multiplicador se lee del proyecto (`state.settings.projectMultiplier`)
y **se aplica después del margen**: un proyecto ×3 con $1000 subtotal y
30% margen da `total = (1000 + 300) × 3 = 3900`. Toggle **incluir** en
cada sección la apaga del PDF sin perder los datos — el subtotal refleja
solamente lo efectivamente cotizado.

## 9. Términos y Exportar PDF

![Términos + barra de export](img/09-terminos-export.jpg)

La sección **Términos y condiciones** es un textarea libre. Si está
vacía, se omite del PDF; si tiene texto, se imprime al pie del
documento antes de la firma. Persiste en `localStorage` (settings) y
sobrevive al cierre del panel.

La **barra inferior** tiene el botón **Exportar PDF** + un indicador de
último cálculo. Click dispara `exportHandler()` que serializa el draft
a PDF vía `src/presupuesto/builder.ts:buildPresupuestoPdfOptions` →
`export-build-pdf.ts`. El archivo se descarga con la forma:

```
presupuesto-{nombre-proyecto}-{yyyymmdd}.pdf
```

> **Hoja de armado no entra acá.** El PDF de Presupuesto es ortogonal
> al PDF de Hoja de armado (`src/assembly-sheet/`) — son dos exports
> distintos con dos botones distintos.

## 10. Configuración → Presupuesto

![Tab Presupuesto en Configuración](img/10-settings-presupuesto.jpg)

`Configuración → Presupuesto` (en el grupo **Pieza**) tiene 2 bloques:

### 10.1 · Master toggle

![Habilitar Presupuesto](img/10a-enabled-toggle.jpg)

`Activar módulo Presupuesto` (default **off**) controla el módulo entero:

- **Off** → el botón `[Q]` del topbar desaparece, `Shift+Q` no hace
  nada, los borradores existentes **se preservan** en
  `parts.quoteDraft` (campo soft del `.despiece`).
- **On** → todo vuelve a funcionar.

> El campo `parts.quoteDraft` es **soft** (`src/types/project.ts`) —
> no bump de schema del `.despiece`. Sobrevive entre proyectos. Los
> proyectos viejos sin presupuesto siguen abriendo normalmente.

### 10.2 · Cotización por defecto

![Defaults de cotización](img/10b-defaults.jpg)

Cuatro campos que prefilledan cada nuevo presupuesto:

| Campo | Tipo | Default | Significado |
|-------|------|---------|-------------|
| `quoteCurrency` | string (≤8 chars) | `ARS` | Prefijo/sufijo del importe (`$`, `US$`, `€`) |
| `quoteMarginPct` | número (0–200) | `30` | Porcentaje sobre el subtotal |
| `quoteValidityDays` | número (1–365) | `15` | Días hasta la fecha de vencimiento |
| `quoteFooterTerms` | textarea (≤6000) | `""` | Texto libre al pie del PDF |

Persiste en `localStorage` (vía `despiece.settings.v1`) y **sí entra en
el export de Configuración** (la sección `presupuesto` del IO config).

> **El brand del taller (logo + nombre fantasía + CUIT + dirección)
> ya no vive acá** — pasó al tab **Taller** en la fase de reducción
> 2026-07-21. El módulo de Presupuesto consume el brand global vía
> `getWorkshopBrand()`. Si querés cambiar el logo, editás en
> `Configuración → Taller` (no en Presupuesto).

## 11. Atajos de teclado

![Hoja de atajos](img/11-atajos-presupuesto.jpg)

`Help` (botón de la topbar o `?`) abre la hoja completa. Los más
usados en el flujo de presupuesto:

| Atajo | Acción |
|-------|--------|
| `Shift+Q` | Abre / cierra el panel Presupuesto |
| `Esc` | Cierra el panel |
| `Shift+C` | Abre el panel Cliente (per-proyecto) |
| `Shift+K` | Abre la Agenda de clientes (workshop-global CRM; gated por Settings → Agenda) |
| `/` | Filtrar lista de piezas (sidebar 2D) |
| `B` / `Shift+B` | Auto-cantos ✦ (complementario) |
| `I` | Aislar selección |
| `F` / `Home` | Fit / Fit + iso |

En Mac el modificador de sistema es `⌘`; en otros sistemas `Ctrl`.

> **Recalculo automático.** El panel Presupuesto re-renderiza cuando
> cambian epochs estructurales (`settingsEpoch`, `nestingEpoch`,
> `modelKind`, `hardwareEpoch`, `herrajeriaEpoch`). Las ediciones del
> usuario (cliente, extras CRUD, margin, currency local edits) NO
> causan re-render — los handlers aplican updates quirúrgicos al DOM
> preservando foco y scroll entre keystrokes.

---

## Ver también

- [Tutorial · Glue-up y Auto-glueup](glueup-y-auto-glueup.html) —
  patrón análogo de settings soft + módulo gated.
- [Tutorial · Listones y Auto-lineales](lineales.html) —
  módulo 1D paralelo con su propio catálogo.
- [Tutorial · Cantos y auto-cantos](cantos-y-auto-cantos.html) — flujo
  2D análogo.
- [Tutorial · Roles y auto-roles](roles-y-auto-roles.html) — flujo
  análogo para `partRole`.
- Manual
  [§ 16 · Presupuesto al cliente](../manual/presupuesto.html) —
  referencia corta de la feature.
- Manual
  [§ 11 · Configuración](../manual/configuracion.html#presupuesto) —
  tab Presupuesto en contexto de la sección de taller.
- Spec
  [`2026-07-19-presupuesto-cliente.md`](../superpowers/specs/2026-07-19-presupuesto-cliente.md)
  — diseño del módulo, builder y totales.