From e6d49fd335f35de442274636cf534172adec1b51 Mon Sep 17 00:00:00 2001 From: "Prit V. Vasani" Date: Sat, 10 Oct 2026 19:17:52 +0000 Subject: [PATCH 1/2] feat(invoices): polish print tab + PDF download MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Print page now renders an on-screen toolbar (Print / Download PDF / Close) that is hidden by @media print; the Download link reuses the tab's token to fetch the PDF (attachTokenFromQuery now stashes req.tokenFromQuery). - PDF download already returns Content-Disposition: attachment with a friendly filenname (INV_FY_SEQ_fmt.pdf) — verified end-to-end. --- server/controllers/invoiceController.js | 29 ++++++++++++++++++++++++- server/middleware/auth.js | 1 + 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/server/controllers/invoiceController.js b/server/controllers/invoiceController.js index c5d9d70..a9fd3da 100644 --- a/server/controllers/invoiceController.js +++ b/server/controllers/invoiceController.js @@ -71,8 +71,35 @@ exports.getInvoicePrint = async (req, res) => { const mode = req.query.mode === 'thermal' ? 'thermal' : 'a4'; const { renderInvoiceHtml } = require('../services/invoicePrintHtml'); const html = renderInvoiceHtml(invoice, { mode }); + + // Small on-screen toolbar (hidden in print), so the print tab isn't a + // dead-end page. Reuses the token passed for this tab to link the PDF. + const token = req.tokenFromQuery || String(req.headers.authorization || '').replace(/^Bearer\s+/i, ''); + const pdfFormat = mode === 'thermal' ? 'thermal80' : 'a4'; + const pdfHref = `/api/v1/invoices/${req.params.id}/pdf?format=${pdfFormat}&download=1&token=${encodeURIComponent(token)}`; + const title = String(invoice.invoiceNumber || 'Invoice').replace(/[<>&"]/g, ''); + + const chrome = ` + + +`; + res.setHeader('Content-Type', 'text/html; charset=utf-8'); - return res.send(html); + return res.send(html.replace('', `${chrome}`)); } catch (err) { console.error('Error rendering invoice HTML:', err); return sendResponse(res, 500, false, err.message || 'Server error'); diff --git a/server/middleware/auth.js b/server/middleware/auth.js index aaeb463..75c933f 100644 --- a/server/middleware/auth.js +++ b/server/middleware/auth.js @@ -223,6 +223,7 @@ const attachUserIfPresent = (req, _res, next) => { */ const attachTokenFromQuery = (req, res, next) => { if (!req.headers.authorization && req.query && typeof req.query.token === 'string') { + req.tokenFromQuery = req.query.token; req.headers.authorization = `Bearer ${req.query.token}`; delete req.query.token; } From 33a68ae6d5adefc55485387391806d04e010903b Mon Sep 17 00:00:00 2001 From: "Prit V. Vasani" Date: Sat, 10 Oct 2026 19:20:47 +0000 Subject: [PATCH 2/2] fix(invoices): guard missing req.headers in print toolbar injection --- server/controllers/invoiceController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/controllers/invoiceController.js b/server/controllers/invoiceController.js index a9fd3da..faf6d79 100644 --- a/server/controllers/invoiceController.js +++ b/server/controllers/invoiceController.js @@ -74,7 +74,7 @@ exports.getInvoicePrint = async (req, res) => { // Small on-screen toolbar (hidden in print), so the print tab isn't a // dead-end page. Reuses the token passed for this tab to link the PDF. - const token = req.tokenFromQuery || String(req.headers.authorization || '').replace(/^Bearer\s+/i, ''); + const token = req.tokenFromQuery || String((req.headers && req.headers.authorization) || '').replace(/^Bearer\s+/i, ''); const pdfFormat = mode === 'thermal' ? 'thermal80' : 'a4'; const pdfHref = `/api/v1/invoices/${req.params.id}/pdf?format=${pdfFormat}&download=1&token=${encodeURIComponent(token)}`; const title = String(invoice.invoiceNumber || 'Invoice').replace(/[<>&"]/g, '');