FactuGES_ERP/docs/customer-invoices/proforma-tax-config.md
David 0a2e7795f1 feat: Refactor tax handling in proforma and recurring proforma modules
- Introduced ProformaItemTax value object to encapsulate tax properties (code, name, percentage).
- Updated ProformaTaxResolver to utilize ProformaItemTax instead of Tax.
- Modified recurring proforma materializer to handle new tax structure.
- Adjusted proforma aggregate and item entities to accommodate ProformaItemTax.
- Enhanced Sequelize mappers to map to and from the new ProformaItemTax structure.
- Updated tests to reflect changes in tax handling and ensure correctness.
2026-09-10 13:21:49 +02:00

5.7 KiB
Raw Blame History

Proforma Tax Config

Estado: CURRENT — SSOT fiscal para Proforma e IssuedInvoice. Las propuestas futuras deben identificarse expresamente como TARGET; la protección de DELETE de TaxDefinition permanece APARCADA.

Objetivo

Persistir la configuracion fiscal de cabecera de una proforma sin confundirla con los impuestos realmente calculados desde lineas valoradas.

Alcance

  • tax_regime_code sigue representando el regimen fiscal general
  • tax_config representa los controles fiscales de cabecera usados por la UI
  • taxes sigue representando solo desglose fiscal materializado
  • totals sigue representando solo importes calculados desde lineas valoradas

Campos persistidos en proformas

  • tax_mode
  • default_iva_code
  • uses_equivalence_surcharge
  • default_rec_code
  • uses_retention
  • default_retention_code

Estos códigos son referencias fiscales persistidas de la Proforma. No existe todavía una protección transversal consolidada que impida borrar una TaxDefinition por estar referenciada.

Fiscalidad viva de Proforma

Tax es el objeto operacional obtenido del catálogo: contiene code, name, rate, group y calculationBehavior. Antes de entrar en una línea de Proforma se convierte explícitamente a ProformaItemTax, el snapshot fiscal de la línea: code, name y percentage.

Al cargar una Proforma, si existe la TaxDefinition para un código, prevalece su definición viva y se vuelve a materializar como ProformaItemTax. Si falta, se conserva el snapshot persistido (code, name, percentage) para que la Proforma siga siendo legible. Los importes no forman parte de ProformaItemTax: se derivan siempre de taxableAmount × percentage.

group y calculationBehavior pertenecen exclusivamente a Tax/TaxDefinition operacional; no se persisten en ProformaItem.

Disponibilidad fiscal

La disponibilidad se evalúa por companyId, código fiscal y proformaDate:

  • available: puede asignarse, cambiar de estado y emitirse.
  • inactive o out_of_validity: una lectura existente se resuelve live, pero no admite nueva asignación, cambio de estado ni emisión.
  • missing: la lectura hace fallback al ProformaItemTax persistido; no permite nueva asignación, cambio de estado ni emisión.

No hay fallback a defaults de Customer o Company. Readiness expone blockers tax_unavailable con razón inactive, out_of_validity o missing; la emisión revalida en ese instante y no confía en un readiness previo.

Contrato publico

GET /proformas/:id debe devolver:

{
  tax_regime: {
    code: string;
    name: string;
    description: 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;
  };
}

POST /proformas y PATCH /proformas/:id pueden aceptar tax_config como bloque independiente de items.

Regla funcional

  • una proforma puede existir con items = []
  • en ese caso taxes = []
  • en ese caso todos los totales monetarios deben quedar a 0
  • la UI debe hidratar los controles de fiscalidad desde tax_config, no desde taxes

Resolucion de defaults en create

Los defaults del cliente solo se usan al crear la proforma y el resultado se persiste.

Fallback actual:

  • tax_mode: single
  • IVA por defecto: iva_21
  • recargo de equivalencia: desactivado salvo indicacion explicita
  • retencion: desactivada salvo indicacion explicita
  • codigo de retencion por defecto si uses_retention = true: retention_15

Si el cliente cambia despues de crear la proforma:

  • no se reconstruye tax_config desde el cliente actual
  • la proforma conserva su tax_config persistido

Regla de update

tax_config se actualiza de forma parcial y se mergea sobre la configuracion persistida actual.

No debe reconstruirse desde:

  • taxes
  • el cliente actual
  • defaults implícitos de frontend

Para items: undefined no toca la colección ni revalida su fiscalidad existente; [] borra todas las líneas; una lista reemplaza completamente las líneas y valida todos sus impuestos como activos y vigentes. No existe mantenimiento diferencial por itemId.

IssuedInvoice: snapshot histórico

Una factura emitida es un snapshot histórico inmutable. IssuedInvoiceItem e IssuedInvoiceTax conservan, por IVA/REC/retención según corresponda, código, nombre, porcentaje e importe. Rehidratar, construir DTO o reporte no consulta TaxDefinition ni Proforma; cambios posteriores de nombre, porcentaje, actividad o vigencia no alteran la factura.

No existe fallback name = code: code != null && name == null es una inconsistencia legacy.

Grouping fiscal actual (P10)

La identidad actual del breakdown es ivaCode | recCode | retentionCode: las tres dimensiones participan, no solo IVA. El porcentaje no forma parte de la key, pero dentro de un grupo el mismo código con nombre distinto o porcentaje distinto produce fail-fast. Esto no afirma que un código tenga un porcentaje históricamente inmutable; afirma que no pueden coexistir valores contradictorios para el mismo código dentro del mismo documento.

Se eliminaron las constraints equivalentes a UNIQUE(document_id, iva_code) de los breakdowns de Proforma e IssuedInvoice: contradecían la identidad con REC/retención. El dominio garantiza la agrupación única; la BD todavía no tiene una UNIQUE exacta para los tres slots nullable debido a la semántica SQL de NULL. Es deuda futura no bloqueante.

No incluido

  • nueva tabla para configuracion fiscal
  • recalculo fiscal fuera del flujo normal de lineas
  • cambios de Verifacti
  • cobros o vencimientos vivos
  • rectificativas