# Proforma Create Contract ## Objetivo Definir la semantica correcta de `create proforma` como creacion de cabecera inicial y separarla del calculo fiscal real que depende de lineas valoradas. ## Regla principal `tax_regime_code` no es un impuesto aplicado. En create: - `taxRegimeCode` representa configuracion fiscal inicial - `tax_config` representa la configuracion fiscal persistida de cabecera - `taxes` representa impuestos realmente calculados desde lineas valoradas - `totals` representa importes realmente calculados desde lineas valoradas - `tax_mode` por defecto es `single` ## Contrato funcional de create Una proforma puede crearse sin lineas. Si `items` se omite o se envia como `[]`: - la proforma debe crearse correctamente como borrador - debe generarse `proforma_reference` - pueden resolverse defaults comerciales y fiscales iniciales - `items` debe devolverse como `[]` - `taxes` debe devolverse como `[]` - los totales monetarios deben devolverse a `0` - no deben crearse filas artificiales en `proforma_taxes` ## Payload minimo esperado ```ts { proforma_date: string; customer_id: string; proforma_series_code?: string | null; target_invoice_series_code?: string | null; currency_code?: string; payment_method_id?: string | null; payment_term_id?: string | null; tax_regime_code?: string | null; tax_config?: { tax_mode?: "single" | "per_line"; default_iva_code?: string | null; uses_equivalence_surcharge?: boolean; default_rec_code?: string | null; uses_retention?: boolean; default_retention_code?: string | null; }; items?: []; } ``` Notas: - el contrato actual del modulo puede seguir exigiendo otros campos tecnicos ya existentes - `items` es opcional; si existe, puede ir vacio - no se usan defaults ocultos en Zod para disfrazar la intencion del request - si no se informa `tax_config`, el backend resuelve defaults iniciales y los persiste - si el cliente cambia después, la proforma conserva su `tax_config` persistido ## Response esperada al crear sin lineas Adaptando nombres exactos al contrato publico actual: ```ts { id: string; proforma_reference: string; proforma_date: string; customer_id: string; currency_code: string; payment_method_id: string | null; payment_term_id: string | null; tax_regime_code: string | null; target_invoice_series_code: string | null; items: []; taxes: []; totals: { subtotal_before_discounts: "0.00"; line_discount_total: "0.00"; taxable_base: "0.00"; tax_total: "0.00"; total: "0.00"; }; } ``` ## Punto correcto de calculo - `create` crea cabecera minima, numera y resuelve defaults - `create` persiste tambien el `tax_config` resuelto - `update` permite anadir o editar lineas - al existir lineas valoradas, `update` recalcula `taxes` y `totals` - `issue` genera la `issued_invoice` materializada usando `target_invoice_series_code` si existe y, si no, la default activa de `issued_invoice` ## Relacion con borrado - una proforma creada en `draft` puede borrarse logicamente mas adelante - ese borrado no modifica `tax_config`, impuestos, totales ni numeracion historica - la referencia visible consumida por create no vuelve a reutilizarse aunque la proforma se borre ## Wording de UI La UI de create debe hablar de: - "Se preparara automaticamente" - "valores iniciales" - "configuracion fiscal inicial" La UI no debe sugerir: - que los impuestos ya quedaron aplicados al crear - que existe desglose fiscal real sin lineas - que los totales ya fueron calculados si todavia no hay lineas valoradas ## Diagnostico SQL recomendado ```sql SELECT p.id, p.proforma_reference, COUNT(DISTINCT pi.item_id) AS item_rows, COUNT(DISTINCT pt.tax_id) AS tax_rows FROM proformas p LEFT JOIN proforma_items pi ON pi.proforma_id = p.id LEFT JOIN proforma_taxes pt ON pt.proforma_id = p.id GROUP BY p.id, p.proforma_reference HAVING COUNT(DISTINCT pi.item_id) = 0 AND COUNT(DISTINCT pt.tax_id) > 0; ``` Resultado esperado: - `0` filas Si devuelve filas: - documentar los ids afectados - revisar si provienen de datos legacy o de una version intermedia del flujo - no borrar datos sin confirmacion explicita ## Pendientes conocidos - `payment_term_id` aparece ya en contratos de transporte, pero el dominio/snapshot de proformas sigue sin soporte funcional completo en esta fase - no se introduce un motor fiscal nuevo en este ajuste ## Archivado - una proforma creada nace con `archived_at = NULL` - el archivado posterior no altera `status` - una proforma `draft` archivada puede borrarse sin desarchivarse antes - en listados, la visibilidad archivada se expresa mediante `Criteria filters[]` sobre `archived_at`, no mediante un estado de negocio nuevo