- 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.
193 lines
5.9 KiB
Markdown
193 lines
5.9 KiB
Markdown
# 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:
|
|
|
|
- [Plazos de pago](../catalogs/payment-terms.md)
|
|
|
|
## 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
|
|
|
|
```ts
|
|
{
|
|
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:
|
|
|
|
```ts
|
|
{
|
|
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
|
|
|
|
```sql
|
|
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
|