FactuGES_ERP/docs/customer-invoices/proforma-create-contract.md
david cf07de4836 feat: add proforma customer change evaluation utility
- Implemented `evaluateProformaCustomerChange` function to assess changes in customer details for proforma invoices.
- Introduced types for customer change details, blockers, field differences, and fiscal targets.
- Added utility functions for normalizing text and formatting recipient information.

test: add unit tests for customer payment resolver

- Created tests for `CustomerPaymentResolver` to validate payment term retrieval and handling of inactive terms.
- Ensured proper behavior when clearing payment terms and handling non-existent terms.

test: add unit tests for customer default payment term resolver

- Developed tests for `CustomerDefaultPaymentTermResolver` to verify default payment term resolution logic.
- Included scenarios for creating customers with and without explicit payment term IDs.

feat: implement customer default payment term resolver

- Created `CustomerDefaultPaymentTermResolver` to manage default payment term resolution during customer creation.
- Integrated with company finder and payment resolver services.

test: add unit tests for create customer use case

- Implemented tests for `CreateCustomerUseCase` to ensure correct handling of payment term IDs during customer creation.
- Validated behavior when no explicit payment term ID is provided.

test: add unit tests for update customer use case

- Developed tests for `UpdateCustomerUseCase` to verify payment term handling during customer updates.
- Ensured that inactive payment terms are not assigned and that explicit null values are respected.

feat: add dependency injection for customer company services

- Implemented DI for customer company dependencies to facilitate service resolution.

test: add tests for customer create data utility

- Created tests for `buildCustomerCreateData` to validate payment term handling based on user input.
2026-08-17 11:25:34 +02:00

5.0 KiB

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.

Para la semántica vigente de payment_term_id, herencia desde Company o Customer y diferencia entre referencia viva y snapshot, usar como fuente principal:

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

{
  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
  • payment_term_id sigue la prioridad vigente:
    • Customer.paymentTermId
    • Company.defaultPaymentTermId
    • null
  • payment_term_id = null significa “sin plazo de pago”
  • una selección manual de payment_term_id = null no debe volver a activar un default después

Response esperada al crear sin lineas

Adaptando nombres exactos al contrato publico actual:

{
  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

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

  • 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