Unify manual and ZIP-exported invoice PDF styling #2

Merged
odweta merged 1 commits from odweta-invoice-pdf-styling into main 2026-09-10 16:01:25 +02:00
odweta commented 2026-09-10 16:01:19 +02:00 (Migrated from github.com)

Why

PDFs exported manually from the create screen looked different from the PDFs exported inside the ZIP bundle. The ZIP export rendered invoices server-side with PDFKit (styled table, colored header, payment box, embedded QR code), while the create screen built a separate HTML page and opened it in a print window with its own, simpler styling.

Approach

  • Extracted the ZIP export's rendering logic in app/src/app.js into a shared renderInvoicePdf(document, invoice) function, reused by both appendInvoicePdf (ZIP export) and a new POST /api/invoices/pdf endpoint.
  • The new endpoint accepts the invoice form data from the editor, normalizes it the same way POST /api/invoices does, and streams back a PDF generated with the exact same PDFKit layout used in the ZIP export.
  • Updated the "Vygenerovat a vytisknout fakturu" button in app/src/static/script.js to call this endpoint and download the resulting PDF, instead of building a standalone HTML document and invoking the browser print dialog.
  • Removed the now-unused HTML-template rendering helpers (buildInvoiceHtml, renderPerson, renderItems, getPersonName, getAddress, the duplicate normalizeIban/domesticAccountToIban/buildPaymentQrPayload client-side implementations) since the server-side renderer is now the single source of truth for invoice PDF styling.

Notes for reviewers

  • The manually generated PDF is now downloaded (like the ZIP invoices) rather than opened in a print dialog. This is an intentional UX change to keep both paths consistent and reuse the same code path.
  • Existing tests (npm test in app/) pass unchanged.
## Why PDFs exported manually from the create screen looked different from the PDFs exported inside the ZIP bundle. The ZIP export rendered invoices server-side with PDFKit (styled table, colored header, payment box, embedded QR code), while the create screen built a separate HTML page and opened it in a print window with its own, simpler styling. ## Approach - Extracted the ZIP export's rendering logic in `app/src/app.js` into a shared `renderInvoicePdf(document, invoice)` function, reused by both `appendInvoicePdf` (ZIP export) and a new `POST /api/invoices/pdf` endpoint. - The new endpoint accepts the invoice form data from the editor, normalizes it the same way `POST /api/invoices` does, and streams back a PDF generated with the exact same PDFKit layout used in the ZIP export. - Updated the "Vygenerovat a vytisknout fakturu" button in `app/src/static/script.js` to call this endpoint and download the resulting PDF, instead of building a standalone HTML document and invoking the browser print dialog. - Removed the now-unused HTML-template rendering helpers (`buildInvoiceHtml`, `renderPerson`, `renderItems`, `getPersonName`, `getAddress`, the duplicate `normalizeIban`/`domesticAccountToIban`/`buildPaymentQrPayload` client-side implementations) since the server-side renderer is now the single source of truth for invoice PDF styling. ## Notes for reviewers - The manually generated PDF is now downloaded (like the ZIP invoices) rather than opened in a print dialog. This is an intentional UX change to keep both paths consistent and reuse the same code path. - Existing tests (`npm test` in `app/`) pass unchanged.
Sign in to join this conversation.