feat(docs): actualizar documentación de configuración fiscal de Proforma y agregar notas sobre obsolescencia

This commit is contained in:
David Arranz 2026-09-10 11:49:52 +02:00
parent 3d8f110cb7
commit 77851f5dba
3 changed files with 65 additions and 1 deletions

View File

@ -2,6 +2,11 @@
> Estado: **HISTÓRICO — NO NORMATIVO**. > 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). > 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. > 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.

View File

@ -1,5 +1,9 @@
# Proforma Tax Config # 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 ## Objetivo
Persistir la configuracion fiscal de cabecera de una proforma sin confundirla con los impuestos realmente calculados desde lineas valoradas. 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` - `uses_retention`
- `default_retention_code` - `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 ## Contrato publico
`GET /proformas/:id` debe devolver: `GET /proformas/:id` debe devolver:
@ -78,6 +110,32 @@ No debe reconstruirse desde:
- el cliente actual - el cliente actual
- defaults implícitos de frontend - 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 ## No incluido
- nueva tabla para configuracion fiscal - nueva tabla para configuracion fiscal

View File

@ -1,11 +1,12 @@
import type { Criteria } from "@repo/rdx-criteria/server"; import type { Criteria } from "@repo/rdx-criteria/server";
import type { UniqueID } from "@repo/rdx-ddd"; 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 { Proforma } from "../../../domain";
import type { ProformaStatusCounts, ProformaSummary } from "../models"; import type { ProformaStatusCounts, ProformaSummary } from "../models";
import type { IProformaRepository } from "../repositories"; import type { IProformaRepository } from "../repositories";
import type { ProformaListScope } from "../use-cases"; import type { ProformaListScope } from "../use-cases";
import type { ProformaTaxResolver } from "./catalog-resolver"; import type { ProformaTaxResolver } from "./catalog-resolver";
export interface IProformaFinder { export interface IProformaFinder {