diff --git a/public/js/app.js b/public/js/app.js index 912c2ef..d242bad 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -70,7 +70,6 @@ function renderTable() { tbody.innerHTML = checks.map(renderRow).join(''); updateSortIndicators(); - updateCheckboxStates(); // Attach row-level event listeners tbody.querySelectorAll('input[type="checkbox"]').forEach(cb => { @@ -157,14 +156,6 @@ function updateSortIndicators() { }); } -function updateCheckboxStates() { - document.querySelectorAll('#checks-tbody input[type="checkbox"]').forEach(cb => { - const id = parseInt(cb.dataset.id, 10); - if (!state.selected.has(id)) { - cb.disabled = state.selected.size >= 3; - } - }); -} function refreshPdfButton() { const n = state.selected.size; @@ -178,16 +169,11 @@ function refreshPdfButton() { function onCheckboxChange(cb) { const id = parseInt(cb.dataset.id, 10); if (cb.checked) { - if (state.selected.size >= 3) { - cb.checked = false; - return; - } state.selected.add(id); } else { state.selected.delete(id); } refreshPdfButton(); - updateCheckboxStates(); } // ── Slide-in panel ─────────────────────────────────────────────────────────── @@ -298,7 +284,7 @@ async function deleteCheck(id) { async function generatePdf() { const ids = [...state.selected]; - if (ids.length === 0 || ids.length > 3) return; + if (ids.length === 0) return; const btn = document.getElementById('btn-generate-pdf'); btn.disabled = true; diff --git a/src/routes/pdf.js b/src/routes/pdf.js index e92281b..4255925 100644 --- a/src/routes/pdf.js +++ b/src/routes/pdf.js @@ -17,8 +17,8 @@ const { generateCheckPdf } = require('../services/pdfService'); router.post('/', async (req, res) => { const { checkIds } = req.body; - if (!Array.isArray(checkIds) || checkIds.length === 0 || checkIds.length > 3) { - return res.status(400).json({ error: 'checkIds must be an array of 1–3 IDs' }); + if (!Array.isArray(checkIds) || checkIds.length === 0) { + return res.status(400).json({ error: 'checkIds must be a non-empty array' }); } // Fetch account diff --git a/src/services/pdfService.js b/src/services/pdfService.js index bb8d985..04c7ca2 100644 --- a/src/services/pdfService.js +++ b/src/services/pdfService.js @@ -88,7 +88,7 @@ function formatMicrLine(routingNo, accountNo, checkNo) { } /** - * Main export: generates a PDF buffer for 1–3 checks. + * Main export: generates a multi-page PDF buffer for any number of checks (3 per page). * * @param {Object} account - Account row from database * @param {Array} checks - Array of 1–3 check rows from database @@ -128,14 +128,17 @@ function generateCheckPdf(account, checks, fields) { const hasUsableLogo = !!(logoField && account.logo_data); const COMPANY_FIELDS = new Set(['Company Name', 'Company Name2', 'Company Name3', 'Company Name4']); - // We always render 3 slots; empty slots get a blank placeholder - for (let slot = 0; slot < 3; slot++) { - const check = checks[slot] || null; - const slotOriginY = slot * SLOT_HEIGHT_IN; + const offX = (account.offset_right - account.offset_left); + const offY = (account.offset_down - account.offset_up); - // Offset adjustments from account calibration - const offX = (account.offset_right - account.offset_left); - const offY = (account.offset_down - account.offset_up); + // Render checks in pages of 3; add a new page for each additional group + const pages = Math.ceil(checks.length / 3); + for (let page = 0; page < pages; page++) { + if (page > 0) doc.addPage(); + + for (let slot = 0; slot < 3; slot++) { + const check = checks[page * 3 + slot] || null; + const slotOriginY = slot * SLOT_HEIGHT_IN; // Helper: convert inches (relative to slot) to PDF points (absolute page) const pt = (xIn, yIn) => ({ @@ -220,7 +223,8 @@ function generateCheckPdf(account, checks, fields) { .text(micrLine, micrPos.x, micrPos.y, { lineBreak: false }); } - } + } // end slot loop + } // end page loop doc.end(); });