8.7 KiB
Proformas Reports
Resumen funcional
El flujo activo de reports de proformas en modules/customer-invoices ya no usa un endpoint ambiguo con format.
El endpoint legacy eliminado era:
GET /proformas/:proforma_id/report?format=pdf|html|json
El estado actual separa explícitamente:
GET /proformas/:proforma_id/report/pdf
GET /proformas/:proforma_id/report/preview
No existe soporte JSON para reports de proformas.
Endpoints finales
Descarga PDF
Endpoint:
GET /proformas/:proforma_id/report/pdf
Comportamiento:
- Content-Type: application/pdf
- descarga como attachment
- render con FastReport PDF
- plantilla: proforma.frx
- sin firma digital
- cache solo si la proforma no está en draft
- sin query format
Preview HTML
Endpoint:
GET /proformas/:proforma_id/report/preview
Comportamiento:
- Content-Type: text/html; charset=utf-8
- render inline
- FastReport HTML
- plantilla: proforma.frx
- sin firma digital
- sin cache
- sin query format
Uso previsto:
- visualizar el contenido de una proforma desde la lista
- previsualizar cómo quedaría la proforma antes de exportarla a PDF
Limitación actual:
Solo previsualiza una proforma persistida.
No existe preview de cambios no guardados del formulario.
No existen todavía endpoints como:
POST /proformas/report/preview
POST /proformas/:proforma_id/report/preview
Flujo técnico
Flujo conceptual:
Route
-> Controller
-> Use case
-> Proforma finder / read model assembler
-> Full snapshot
-> Report snapshot
-> Document pipeline
-> FastReport renderer
-> HTTP response
Separación por capas:
Application
Responsabilidades:
- use cases
- builders
- modelos de report
- errores funcionales
- puertos como CompanyReportProfileFinder
Reglas:
- Application no depende de Express, Sequelize, filesystem ni FastReport directamente
- los use cases devuelven Result
Infrastructure
Responsabilidades:
- routes Express
- controllers
- adapters
- FastReport renderers
- document pipelines
- cache/storage
Reglas:
- los controllers no contienen lógica documental
- FastReport vive en Infrastructure
- no hay firma digital en el pipeline de proformas
- no hay acoplamiento con issued-invoices
Diferencias entre PDF y HTML preview
| Aspecto | HTML preview | |
|---|---|---|
| Endpoint | /report/pdf |
/report/preview |
| MIME type | application/pdf |
text/html; charset=utf-8 |
| Disposition | attachment |
inline |
| Renderer | FastReport PDF | FastReport HTML |
| Cache | solo no draft |
no |
| Firma digital | no | no |
Reglas de cache
La cache PDF usa una storageKey determinista.
La key V1 incluye:
companyId
proformaId
status
format=pdf
languageCode
currencyCode
companyProfile.slug
companyProfile.version si existe
Regla funcional aplicada:
Una proforma no draft no puede modificarse.
Por tanto:
draft:
- no cachear PDF
non-draft:
- cachear PDF
La cache se considera segura respecto al contenido de la proforma cuando status !== "draft" por la inmutabilidad funcional de estados no draft.
Deuda pendiente:
- invalidación por cambios de plantilla
- invalidación por cambios de branding/logo/datos empresa
- invalidación por una versión documental más rica cuando exista
Reglas de firma digital
Las proformas no se firman digitalmente.
Regla explícita:
La firma digital aplica a issued invoices, no a proformas.
Esto evita copiar incorrectamente el pipeline de issued-invoices dentro del flujo de proformas.
CompanyReportProfile y error 409
El report de proforma necesita datos reales de empresa.
No se puede renderizar una proforma sin CompanyReportProfile.
Si el perfil no existe:
CompanyReportProfileNotFoundError
se mapea a:
409 Conflict
Motivo:
La proforma existe, pero falta configuración documental de empresa.
No está permitido:
- inventar datos de empresa
- hardcodear companySlug
- continuar renderizando con datos incompletos
- devolver error genérico 500
Actualmente CompanyReportProfileFinder usa el servicio público de companies.
Campos que hoy pueden no venir expuestos por companies y quedan como Maybe.none():
locale
currencyCode
logo
address
version
Localization
El flujo usa un modelo explícito:
export type ReportLocalization = {
languageCode: string;
locale: string;
currencyCode: string;
};
Criterio funcional:
- languageCode representa idioma: es, en, fr...
- locale representa formato regional: es-ES, en-GB...
- currencyCode representa moneda: EUR, USD...
Prioridad funcional deseada:
1. valores guardados en el documento
2. valores del cliente/recipient si existen
3. valores de la empresa
4. fallback técnico controlado
No debe repetirse la incoherencia anterior entre languageCode y locale.
Ficheros principales
Rutas y controllers:
modules/customer-invoices/src/api/infrastructure/proformas/express/proformas.routes.ts
modules/customer-invoices/src/api/infrastructure/proformas/express/controllers/report-proforma-pdf.controller.ts
modules/customer-invoices/src/api/infrastructure/proformas/express/controllers/preview-proforma-report.controller.ts
Use cases:
modules/customer-invoices/src/api/application/proformas/use-cases/report-proforma-pdf.use-case.ts
modules/customer-invoices/src/api/application/proformas/use-cases/preview-proforma-report.use-case.ts
Errores y mapping HTTP:
modules/customer-invoices/src/api/application/proformas/errors/company-report-profile-not-found.error.ts
modules/customer-invoices/src/api/infrastructure/proformas/express/proformas-api-error-mapper.ts
Adapter de empresa:
modules/customer-invoices/src/api/infrastructure/proformas/adapters/company-report-profile-finder.ts
Pipelines y renderers:
modules/customer-invoices/src/api/infrastructure/proformas/documents/pipelines/proforma-pdf-document-pipeline-factory.ts
modules/customer-invoices/src/api/infrastructure/proformas/documents/pipelines/proforma-html-preview-pipeline-factory.ts
modules/customer-invoices/src/api/infrastructure/proformas/documents/renderers/fastreport/proforma-pdf-document-renderer.ts
modules/customer-invoices/src/api/infrastructure/proformas/documents/renderers/fastreport/proforma-html-preview-renderer.ts
Metadata, cache y persistencia de documentos:
modules/customer-invoices/src/api/infrastructure/proformas/documents/services/proforma-pdf-document-metadata-factory.ts
modules/customer-invoices/src/api/infrastructure/proformas/documents/services/proforma-html-preview-document-metadata-factory.ts
modules/customer-invoices/src/api/infrastructure/proformas/documents/pre-processors/proforma-pdf-cache-pre-processor.ts
modules/customer-invoices/src/api/infrastructure/proformas/documents/side-effects/persist-proforma-pdf-side-effect.ts
Template:
modules/customer-invoices/templates/rodax/es/proforma.frx
Legacy eliminado
Ya no existe:
GET /proformas/:proforma_id/report
También se eliminó el soporte ambiguo:
format=pdf|html|json
Legacy eliminado del árbol de aplicación:
modules/customer-invoices/src/api/application/proformas/use-cases/report-proforma2
Motivo:
- legacy no cableado
- no referenciado por rutas, DI, exports ni tests
- contaminaba el typecheck del módulo
No debe reintroducirse:
report-proforma2
ReportProformaUseCase legacy
format=pdf|html|json
/proformas/:proforma_id/report
Frontend
No se ha cambiado todavía el frontend para preview HTML.
Sí se actualizó la descarga PDF para apuntar al endpoint nuevo:
/report/pdf
Pendiente:
- crear cliente web para /report/preview
- integrar preview HTML en lista/editor
- probar si el HTML generado por FastReport es usable dentro de iframe/panel/modal
Deuda pendiente
Pendientes explícitos:
- invalidación de cache por cambios de plantilla
- invalidación de cache por cambios de branding/logo/datos empresa
- exposición futura de logo, address, locale, currencyCode y version desde companies
- cliente frontend para /report/preview
- preview de cambios no guardados del formulario
- errores TypeScript restantes en otras zonas de customer-invoices no relacionadas con reports de proformas
Próximos pasos recomendados
Siguientes pasos razonables:
1. exponer más campos documentales desde companies
2. introducir invalidación de cache por plantilla/branding
3. añadir cliente frontend para /report/preview
4. validar si el HTML de FastReport es usable en iframe/panel/modal
5. diseñar preview de cambios no guardados sin persistencia