# Contrato de readiness de emisión de proformas ## Objetivo La readiness de emisión es una lectura informativa del estado persistido de una proforma. Su propósito es responder: ```txt ¿Puede emitirse esta proforma ahora mismo? ``` No sustituye la validación transaccional final de: ```txt PUT /proformas/:id/issue ``` La emisión sigue revalidando siempre en backend para cubrir concurrencia y cambios entre consulta y submit. ## Endpoint vigente ```http GET /proformas/:proforma_id/issuance-readiness ``` Comportamiento: ```txt - 200 cuando la proforma existe dentro del scope de company - 200 aunque can_issue sea false - blockers con códigos estables orientados a frontend - sin textos i18n en backend ``` No se expone readiness completa en el listado de proformas para evitar N+1 HTTP y resolución externa por fila. ## Contrato HTTP ```json { "can_issue": false, "blockers": [ { "code": "missing_operation_date" }, { "code": "missing_description" } ] } ``` ## Códigos públicos | Code | Significado | | ---- | ----------- | | `already_issued` | La proforma ya tiene emisión materializada o enlazada. | | `invalid_status` | El estado actual no permite emitir; hoy debe estar aprobada. | | `missing_description` | Falta la descripción obligatoria para emitir. | | `missing_operation_date` | Falta la fecha de operación. | | `incomplete_recipient` | Faltan datos fiscales mínimos del destinatario. | | `missing_items` | No existen líneas de detalle persistidas. | | `missing_payment_method` | Falta la forma de pago. | | `missing_tax_regime` | Falta el régimen fiscal. | | `payment_method_unavailable` | La referencia company-scoped de forma de pago no se puede resolver. | | `payment_method_inactive` | La forma de pago resuelta existe, pero está inactiva. | ## Fuente de verdad La semántica se reparte de forma explícita pero sin duplicar reglas: ```txt Proforma.getIssuanceBlockers() -> blockers internos de dominio Proforma.validateCanBeIssued() -> consume el primer blocker compatible con el contrato legacy de error ProformaIssuanceReadinessChecker -> añade blockers externos dependientes de catálogos ``` Esto permite: ```txt readiness -> enumerar blockers útiles issue -> seguir fallando de forma autoritativa ``` ## Semántica importante en frontend - La readiness representa el estado guardado en backend. - Los cambios no guardados del editor no alteran este read model. - Tras guardar, cambiar estado o emitir, la query de readiness debe invalidarse. ## Pendientes fuera de esta fase - exponer readiness en listado sin introducir N+1; - migrar la ruta legacy de emisión de `PUT` a un contrato nuevo si se aprueba; - deep-link o focus directo al campo bloqueado desde frontend.