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:
+59
-24
@@ -624,7 +624,7 @@ export async function generateSubcontractorPOPDF(po, options = {}) {
|
||||
doc.setTextColor(30, 30, 30);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(22);
|
||||
doc.text('PURCHASE ORDER', margin, y);
|
||||
doc.text('SUBCONTRACTOR INVOICE', margin, y);
|
||||
|
||||
doc.setFontSize(10);
|
||||
doc.setTextColor(90, 90, 90);
|
||||
@@ -678,6 +678,29 @@ export async function generateSubcontractorPOPDF(po, options = {}) {
|
||||
});
|
||||
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) {
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(11);
|
||||
@@ -689,38 +712,49 @@ export async function generateSubcontractorPOPDF(po, options = {}) {
|
||||
.slice()
|
||||
.sort((a, b) => Number(a.sort_order || 0) - Number(b.sort_order || 0));
|
||||
|
||||
sortedItems.forEach((item, index) => {
|
||||
const rowTop = y + index * 28;
|
||||
if (index % 2 === 0) {
|
||||
let rowIndex = 0;
|
||||
for (const item of sortedItems) {
|
||||
if (y + 28 > safeBottom) {
|
||||
y = addContinuationHeader();
|
||||
rowIndex = 0;
|
||||
}
|
||||
if (rowIndex % 2 === 0) {
|
||||
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.setFontSize(10);
|
||||
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.text(formatCurrency(item.amount), tableX + tableW - 12, rowTop, { align: 'right' });
|
||||
});
|
||||
|
||||
y += sortedItems.length * 28 + 22;
|
||||
doc.text(formatCurrency(item.amount), tableX + tableW - 12, y, { align: 'right' });
|
||||
y += 28;
|
||||
rowIndex++;
|
||||
}
|
||||
y += 22;
|
||||
}
|
||||
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(11);
|
||||
doc.setTextColor(30, 30, 30);
|
||||
doc.text(po.items?.length > 0 ? 'Summary' : 'Scope of Work', margin, y);
|
||||
y += 16;
|
||||
if (y + 40 > safeBottom) y = addContinuationHeader();
|
||||
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.setFontSize(10);
|
||||
doc.setTextColor(70, 70, 70);
|
||||
const scopeLines = doc.splitTextToSize(po.description || '', pageWidth - margin * 2);
|
||||
doc.text(scopeLines, margin, y);
|
||||
y += scopeLines.length * 13 + 18;
|
||||
if (po.description) {
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(11);
|
||||
doc.setTextColor(30, 30, 30);
|
||||
doc.text(po.items?.length > 0 ? 'Summary' : 'Scope of Work', margin, y);
|
||||
y += 16;
|
||||
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.setFontSize(10);
|
||||
doc.setTextColor(70, 70, 70);
|
||||
const scopeLines = doc.splitTextToSize(po.description, pageWidth - margin * 2);
|
||||
doc.text(scopeLines, margin, y);
|
||||
y += scopeLines.length * 13 + 18;
|
||||
}
|
||||
|
||||
if (po.notes) {
|
||||
if (y + 40 > safeBottom) y = addContinuationHeader();
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(11);
|
||||
doc.setTextColor(30, 30, 30);
|
||||
doc.text('Notes', margin, y);
|
||||
y += 16;
|
||||
@@ -730,15 +764,16 @@ export async function generateSubcontractorPOPDF(po, options = {}) {
|
||||
doc.text(noteLines, margin, y);
|
||||
}
|
||||
|
||||
const footerLineY = Math.max(y + 20, pageHeight - 60);
|
||||
doc.setDrawColor(220, 220, 220);
|
||||
doc.line(margin, 704, rightEdge, 704);
|
||||
doc.line(margin, footerLineY, rightEdge, footerLineY);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.setFontSize(9);
|
||||
doc.setTextColor(120, 120, 120);
|
||||
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,
|
||||
724,
|
||||
footerLineY + 16,
|
||||
{ maxWidth: pageWidth - margin * 2 },
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user