diff --git a/docs/architecture/history/backend-design-consolidated-legacy.md b/docs/architecture/history/backend-design-consolidated-legacy.md index 98ffbb5e..9ee071ba 100644 --- a/docs/architecture/history/backend-design-consolidated-legacy.md +++ b/docs/architecture/history/backend-design-consolidated-legacy.md @@ -2,6 +2,11 @@ > Estado: **HISTÓRICO — NO NORMATIVO**. > +> Nota de obsolescencia fiscal: para el comportamiento CURRENT de Proforma, disponibilidad, +> snapshot de IssuedInvoice, grouping P10 y estado de DELETE de TaxDefinition, consultar +> [Proforma Tax Config](../../customer-invoices/proforma-tax-config.md). Este documento no se +> actualiza retrospectivamente. +> > Las reglas transversales vigentes están distribuidas por responsabilidad en [../backend-architecture.md](../backend-architecture.md), [../dependency-injection.md](../dependency-injection.md), [../http-request-flow.md](../http-request-flow.md), [../dto-guidelines.md](../dto-guidelines.md), [../module-boundaries.md](../module-boundaries.md) y [../testing-guidelines.md](../testing-guidelines.md). > > En particular, el flujo `RequestDTO → RequestMapper → Command → PropsMapper` que aparece más abajo no es una norma vigente: RequestMapper es opcional y se usa solo cuando existe adaptación HTTP propia. diff --git a/docs/customer-invoices/proforma-tax-config.md b/docs/customer-invoices/proforma-tax-config.md index 1e675267..3f5d6379 100644 --- a/docs/customer-invoices/proforma-tax-config.md +++ b/docs/customer-invoices/proforma-tax-config.md @@ -1,5 +1,9 @@ # 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. @@ -20,6 +24,34 @@ Persistir la configuracion fiscal de cabecera de una proforma sin confundirla co - `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 + +Una Proforma es fiscalmente viva. Al cargarla, si existe la `TaxDefinition` de la empresa para +un código, prevalece la definición actual: `code`, `name`, porcentaje/rate, family/group y +`calculationBehavior`. Por ejemplo, una línea persistida como `IVA21` al 21 % se presenta al +22 % si la definición viva `IVA21` se actualizó al 22 %. + +La línea conserva además un respaldo persistido completo: código, nombre, porcentaje, group y +calculation behavior (más los importes calculados). Ese respaldo no es fuente funcional primaria +cuando existe catálogo vivo; sirve para recuperación, diagnóstico y lectura ante inconsistencias. + +## 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 Tax 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: @@ -78,6 +110,32 @@ No debe reconstruirse desde: - 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 diff --git a/modules/customer-invoices/src/api/application/proformas/services/proforma-finder.ts b/modules/customer-invoices/src/api/application/proformas/services/proforma-finder.ts index ac33416e..3c1160ac 100644 --- a/modules/customer-invoices/src/api/application/proformas/services/proforma-finder.ts +++ b/modules/customer-invoices/src/api/application/proformas/services/proforma-finder.ts @@ -1,11 +1,12 @@ import type { Criteria } from "@repo/rdx-criteria/server"; import type { UniqueID } from "@repo/rdx-ddd"; -import type { Collection, Result } from "@repo/rdx-utils"; +import { Collection, Result } from "@repo/rdx-utils"; import type { Proforma } from "../../../domain"; import type { ProformaStatusCounts, ProformaSummary } from "../models"; import type { IProformaRepository } from "../repositories"; import type { ProformaListScope } from "../use-cases"; + import type { ProformaTaxResolver } from "./catalog-resolver"; export interface IProformaFinder {