Email y PDF¶
El backend genera el PDF de la factura y lo envía por email al cliente final. En el legacy esto vivía sobre Seam Mail (Facelets .xhtml) y Seam PDF/Renderer; la migración a Spring Boot lo reescribe sobre Thymeleaf + JavaMailSender (email) y PDFBox + Flying Saucer (PDF). Estas dos piezas están especificadas en SPEC-012 (email) y SPEC-013 (pdf).
Estado: PENDIENTE de implementación
A diferencia del Cockpit y los jobs Quartz —que ya están en código— el email y el PDF todavía no están portados. Verificado contra el repo: no existen las clases EmailHome, TrxToPdfHome, DynamicMailSender ni plantillas en resources/templates/, y en pom.xml los bloques de dependencias de SPEC-012 y SPEC-013 están comentados:
<!-- [SPEC-012] Email
<artifactId>spring-boot-starter-mail</artifactId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
...
<!-- [SPEC-013] PDF (PDFBox nativo + Flying Saucer + iText5 + ZXing)
Las versiones sí ya están fijadas como properties (pdfbox 2.0.31, flyingsaucer 9.1.22, itext 5.5.13.3, zxing 3.5.3), listas para descomentar cuando se implemente. Lo que sigue describe el diseño previsto por las SPECs, no código existente. Tratalo como plan, no como referencia de runtime.
Email — SPEC-012¶
Objetivo¶
Reemplazar el envío del legacy (EmailHome.send() → renderer.render("email/X.xhtml") + mail:mail-session de Seam) por Thymeleaf + JavaMailSender, construido dinámicamente desde la configuración en la tabla Parametro, preservando exactamente destinatarios, asunto, cuerpo y adjunto (el PDF).
Config dinámica desde Parametro¶
La config de mail NO vive en application.yml
Es una invariante del proyecto (§5.10): host/puerto/TLS/usuario/clave/from/baseUrl de mail salen de la tabla Parametro vía ParametroList, no del application.yml. El motivo: el cliente cambia esos valores en caliente sin redeploy. El application.yml lo dice explícito: "mail/URLs/flags operativos NO van acá: siguen saliendo de la tabla Parametro". Ver Configuración y perfiles.
El plan (SPEC-012) es un DynamicMailSender que arma un JavaMailSenderImpl por envío con los parámetros actuales de ParametroList (getMailHost, getMailPort, getMailUsuario, getMailClave, getMailTls, etc.). El flag TLS legacy se modela como mail.smtp.starttls.enable y vale true cuando el parámetro es "1".
Lo que se preserva (contrato con el cliente final)¶
- Plantillas por comercio: el legacy resuelve la plantilla por
enteFacturador.getEmailPath()(defaultsimple); hay variantessimple/factura/dino/ferniplast. Se portan a HTML Thymeleaf enresources/templates/email/, modelando solo el cuerpo (from/to/subject/adjunto pasan a ser parámetros del envío, no de la plantilla). - Destinatarios múltiples:
to/ccseparados por;(getToInList()/getCCInList()). - Imagen de pie embebida: el PNG de footer por comercio (
addInline). - Asunto y nombre del adjunto: se replican exactos del
.xhtmllegacy (suelen incluir tipo de comprobante, PV y número) — es contrato con el cliente. - Misma API pública de
EmailHome(setIdTrx/setTo/setCC/send/getPath/…) para no tocar el callerTiFacturaOnlineManagerWS.
Criterios de aceptación (SPEC-012)¶
EmailHome.send()produce un correo con el mismo from/to/cc/asunto/cuerpo/adjunto que el legacy, comparado contra un correo viejo del golden set.- Cambiar el host en la tabla
Parametrocambia el envío sin redeploy. to/cccon varios destinatarios separados por;funcionan.
PDF — SPEC-013¶
Objetivo¶
Mantener la generación del PDF de la factura. En el legacy TrxToPdfHome tiene dos caminos, y la SPEC los trata distinto:
| Path | Legacy | Plan en la migración |
|---|---|---|
| A — PDFBox nativo | createPDF() + Paginator/Pagina/Linea, fuentes, barcodes ZXing |
Se preserva (port de API PDFBox 1.8 → 2.0) |
| B — Seam Renderer | renderer.render("email/factura.xhtml") + DocumentStore (Seam PDF → iText) |
Se reemplaza por Thymeleaf + Flying Saucer, o se elimina si no se usa en prod |
Path A — PDFBox nativo (se preserva)¶
La lógica de layout (Paginator, Pagina, Linea, fuentes, barcodes ZXing Code128/QR) no cambia. Lo que cambia es el port de PDFBox 1.8 → 2.0 (los renombres de API: PDPageContentStream de paquete, fuentes como instancias estáticas, PDImageXObject/LosslessFactory, document.save(out) lanzando IOException, PDType0Font.load para TTF, etc.).
Decisión de riesgo abierta (PDFBox 2.0 vs pinear 1.8)
SPEC-013 deja explícito que si el upgrade a 2.0 introduce diferencias de render (posiciones, kerning), la alternativa de mínimo riesgo es pinear pdfbox:1.8.17 y diferir el upgrade. La decisión se toma según el diff visual del golden set. Hoy la property apunta a 2.0.31, pero la implementación final debe validar paridad antes de cerrar esto.
Path B — Seam Renderer (se reemplaza)¶
El plan es portar factura.xhtml a una plantilla HTML Thymeleaf y renderizarla a PDF con Flying Saucer (ITextRenderer → iText 5, flying-saucer-pdf-itext5). SPEC-013 también plantea eliminarlo en vez de portarlo si un grep confirma que B solo servía para preview web y el adjunto productivo siempre salió del path A (menos superficie).
Lo que se preserva¶
- Barcodes Code128/QR presentes y escaneables, con el mismo dato (CAE/CAE barcode).
- ORIGINAL/DUPLICADO según
enteFacturador.getLayout()(esa lógica no se toca). - Misma API pública de
TrxToPdfHome(setIdTrx/getTrx/createPDF/…) para no tocarEmailHome.
Validación de paridad¶
SPEC-013 pide un tools/pdf-compare que genere el PDF de N facturas del golden set con el sistema nuevo y lo compare contra el viejo: texto extraído idéntico (PDFBox PDFTextStripper) como gate obligatorio, más diff visual por render a imagen bajo un umbral acordado.
Relación email ↔ PDF¶
El email depende del PDF: SPEC-012 declara que necesita SPEC-013, porque el adjunto del correo es el PDF que produce TrxToPdfHome.createPDF(). Cuando se implementen, deben usar la misma fuente de PDF (coordinar el path A/B para que el adjunto del correo coincida con el documento que ve el cliente).
Por dónde seguir¶
- Configuración y perfiles — por qué la config de mail vive en
Parametroy no enapplication.yml. - El ciclo de facturación — cuándo, en el flujo, se emite el PDF y se manda el correo.
- Modelo de dominio —
EnteFacturador(emailPath, layout) yParametro.