fix: subcontractor PDF title + multi-page support
- 'PURCHASE ORDER' → 'SUBCONTRACTOR INVOICE' - Items loop now calls addPage() when hitting safeBottom (792-80pt) - Continuation pages get header with invoice number + '(continued)' - Summary/Notes sections also page-break if needed - Footer text updated to match invoice context - Footer line position dynamic (tracks y), not hardcoded at 704 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,8 @@
|
|||||||
"Bash(sed -n '1,15p' src/pages/external/ExternalMyInvoices.jsx)",
|
"Bash(sed -n '1,15p' src/pages/external/ExternalMyInvoices.jsx)",
|
||||||
"Bash(sed -n '1,15p' src/pages/client/ClientMyInvoices.jsx)",
|
"Bash(sed -n '1,15p' src/pages/client/ClientMyInvoices.jsx)",
|
||||||
"Bash(git commit -q -m 'fix: restore popupOverlayStyle import in ExternalMyInvoices *)",
|
"Bash(git commit -q -m 'fix: restore popupOverlayStyle import in ExternalMyInvoices *)",
|
||||||
"Bash(git commit -q -m 'style: increase card-bg alpha to match Safari rendering *)"
|
"Bash(git commit -q -m 'style: increase card-bg alpha to match Safari rendering *)",
|
||||||
|
"Bash(git commit -q -m 'fix: subcontractor PDF title + multi-page support *)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-14
@@ -624,7 +624,7 @@ export async function generateSubcontractorPOPDF(po, options = {}) {
|
|||||||
doc.setTextColor(30, 30, 30);
|
doc.setTextColor(30, 30, 30);
|
||||||
doc.setFont('helvetica', 'bold');
|
doc.setFont('helvetica', 'bold');
|
||||||
doc.setFontSize(22);
|
doc.setFontSize(22);
|
||||||
doc.text('PURCHASE ORDER', margin, y);
|
doc.text('SUBCONTRACTOR INVOICE', margin, y);
|
||||||
|
|
||||||
doc.setFontSize(10);
|
doc.setFontSize(10);
|
||||||
doc.setTextColor(90, 90, 90);
|
doc.setTextColor(90, 90, 90);
|
||||||
@@ -678,6 +678,29 @@ export async function generateSubcontractorPOPDF(po, options = {}) {
|
|||||||
});
|
});
|
||||||
y += rows.length * 28 + 22;
|
y += rows.length * 28 + 22;
|
||||||
|
|
||||||
|
const pageHeight = 792;
|
||||||
|
const safeBottom = pageHeight - 80; // leave room for footer
|
||||||
|
|
||||||
|
function addContinuationHeader() {
|
||||||
|
doc.addPage();
|
||||||
|
doc.setFillColor(20, 20, 20);
|
||||||
|
doc.rect(0, 0, pageWidth, headerH, 'F');
|
||||||
|
if (logo) {
|
||||||
|
const logoW = 96;
|
||||||
|
const logoH = logoW / (logo.naturalWidth / logo.naturalHeight);
|
||||||
|
doc.addImage(logo, 'PNG', margin, 16, logoW, logoH);
|
||||||
|
}
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.setFontSize(9);
|
||||||
|
doc.setTextColor(170, 170, 170);
|
||||||
|
doc.text('1855.368.7434 | hello@fourgebranding.com | www.fourgebranding.com', rightEdge, 35, { align: 'right' });
|
||||||
|
doc.setFont('helvetica', 'normal');
|
||||||
|
doc.setFontSize(9);
|
||||||
|
doc.setTextColor(120, 120, 120);
|
||||||
|
doc.text(`${poNumber} (continued)`, margin, headerH + 14);
|
||||||
|
return headerH + 28;
|
||||||
|
}
|
||||||
|
|
||||||
if (po.items?.length > 0) {
|
if (po.items?.length > 0) {
|
||||||
doc.setFont('helvetica', 'bold');
|
doc.setFont('helvetica', 'bold');
|
||||||
doc.setFontSize(11);
|
doc.setFontSize(11);
|
||||||
@@ -689,23 +712,31 @@ export async function generateSubcontractorPOPDF(po, options = {}) {
|
|||||||
.slice()
|
.slice()
|
||||||
.sort((a, b) => Number(a.sort_order || 0) - Number(b.sort_order || 0));
|
.sort((a, b) => Number(a.sort_order || 0) - Number(b.sort_order || 0));
|
||||||
|
|
||||||
sortedItems.forEach((item, index) => {
|
let rowIndex = 0;
|
||||||
const rowTop = y + index * 28;
|
for (const item of sortedItems) {
|
||||||
if (index % 2 === 0) {
|
if (y + 28 > safeBottom) {
|
||||||
|
y = addContinuationHeader();
|
||||||
|
rowIndex = 0;
|
||||||
|
}
|
||||||
|
if (rowIndex % 2 === 0) {
|
||||||
doc.setFillColor(248, 248, 248);
|
doc.setFillColor(248, 248, 248);
|
||||||
doc.rect(tableX, rowTop - 14, tableW, 28, 'F');
|
doc.rect(tableX, y - 14, tableW, 28, 'F');
|
||||||
}
|
}
|
||||||
doc.setFont('helvetica', 'normal');
|
doc.setFont('helvetica', 'normal');
|
||||||
doc.setFontSize(10);
|
doc.setFontSize(10);
|
||||||
doc.setTextColor(45, 45, 45);
|
doc.setTextColor(45, 45, 45);
|
||||||
doc.text(item.description || item.task?.title || 'Work Item', tableX + 12, rowTop);
|
doc.text(item.description || item.task?.title || 'Work Item', tableX + 12, y);
|
||||||
doc.setFont('helvetica', 'bold');
|
doc.setFont('helvetica', 'bold');
|
||||||
doc.text(formatCurrency(item.amount), tableX + tableW - 12, rowTop, { align: 'right' });
|
doc.text(formatCurrency(item.amount), tableX + tableW - 12, y, { align: 'right' });
|
||||||
});
|
y += 28;
|
||||||
|
rowIndex++;
|
||||||
y += sortedItems.length * 28 + 22;
|
}
|
||||||
|
y += 22;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (y + 40 > safeBottom) y = addContinuationHeader();
|
||||||
|
|
||||||
|
if (po.description) {
|
||||||
doc.setFont('helvetica', 'bold');
|
doc.setFont('helvetica', 'bold');
|
||||||
doc.setFontSize(11);
|
doc.setFontSize(11);
|
||||||
doc.setTextColor(30, 30, 30);
|
doc.setTextColor(30, 30, 30);
|
||||||
@@ -715,12 +746,15 @@ export async function generateSubcontractorPOPDF(po, options = {}) {
|
|||||||
doc.setFont('helvetica', 'normal');
|
doc.setFont('helvetica', 'normal');
|
||||||
doc.setFontSize(10);
|
doc.setFontSize(10);
|
||||||
doc.setTextColor(70, 70, 70);
|
doc.setTextColor(70, 70, 70);
|
||||||
const scopeLines = doc.splitTextToSize(po.description || '', pageWidth - margin * 2);
|
const scopeLines = doc.splitTextToSize(po.description, pageWidth - margin * 2);
|
||||||
doc.text(scopeLines, margin, y);
|
doc.text(scopeLines, margin, y);
|
||||||
y += scopeLines.length * 13 + 18;
|
y += scopeLines.length * 13 + 18;
|
||||||
|
}
|
||||||
|
|
||||||
if (po.notes) {
|
if (po.notes) {
|
||||||
|
if (y + 40 > safeBottom) y = addContinuationHeader();
|
||||||
doc.setFont('helvetica', 'bold');
|
doc.setFont('helvetica', 'bold');
|
||||||
|
doc.setFontSize(11);
|
||||||
doc.setTextColor(30, 30, 30);
|
doc.setTextColor(30, 30, 30);
|
||||||
doc.text('Notes', margin, y);
|
doc.text('Notes', margin, y);
|
||||||
y += 16;
|
y += 16;
|
||||||
@@ -730,15 +764,16 @@ export async function generateSubcontractorPOPDF(po, options = {}) {
|
|||||||
doc.text(noteLines, margin, y);
|
doc.text(noteLines, margin, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const footerLineY = Math.max(y + 20, pageHeight - 60);
|
||||||
doc.setDrawColor(220, 220, 220);
|
doc.setDrawColor(220, 220, 220);
|
||||||
doc.line(margin, 704, rightEdge, 704);
|
doc.line(margin, footerLineY, rightEdge, footerLineY);
|
||||||
doc.setFont('helvetica', 'normal');
|
doc.setFont('helvetica', 'normal');
|
||||||
doc.setFontSize(9);
|
doc.setFontSize(9);
|
||||||
doc.setTextColor(120, 120, 120);
|
doc.setTextColor(120, 120, 120);
|
||||||
doc.text(
|
doc.text(
|
||||||
'This purchase order authorizes the subcontractor work described above. Payment is subject to Fourge Branding approval and completion of assigned work.',
|
'This invoice authorizes payment for the subcontractor work described above. Payment is subject to Fourge Branding approval and completion of assigned work.',
|
||||||
margin,
|
margin,
|
||||||
724,
|
footerLineY + 16,
|
||||||
{ maxWidth: pageWidth - margin * 2 },
|
{ maxWidth: pageWidth - margin * 2 },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user