FactuGES_ERP/docs/customer-invoices/proforma-create-contract.md
david 080dcaaa61 feat: implement GetProformaIssuanceReadiness use case and associated controller
- Add GetProformaIssuanceReadinessUseCase to check proforma issuance readiness.
- Create GetProformaIssuanceReadinessController to handle requests and responses.
- Introduce request and response DTOs for proforma issuance readiness.
- Develop tests for the use case and controller to ensure functionality.
- Implement adapter and hooks for integrating issuance readiness in the web application.
- Create UI components for displaying issuance readiness alerts and help popover.
- Add utility functions for translating blocker messages and details.
2026-08-18 23:03:04 +02:00

5.9 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

Diferencia entre draft y emision

create y update no deben imponer automaticamente todas las precondiciones de emision.

Una Proforma puede existir validamente como borrador incompleto.

En concreto:

  • proforma_date forma parte de la estructura base del documento y debe existir desde create;
  • operation_date no se exige en create ni en update general;
  • description no se exige en create ni en update general;
  • payment_method_id puede seguir vacio mientras el documento permanezca en draft.

La obligatoriedad de esos campos pertenece al boundary de issue.

Precondiciones funcionales de emision auditadas en backend:

  • description obligatoria;
  • operation_date obligatoria;
  • payment_method_id obligatorio y resoluble dentro de la company activa;
  • el payment_method debe seguir activo al emitir;
  • payment_term_id sigue siendo opcional;
  • la emision sigue usando invoice_date = today como fecha documental de la factura emitida.

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