-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest-pdf.js
More file actions
150 lines (118 loc) · 5.87 KB
/
test-pdf.js
File metadata and controls
150 lines (118 loc) · 5.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#!/usr/bin/env node
import { PDFDocument, rgb, StandardFonts } from 'pdf-lib';
import fs from 'fs';
import path from 'path';
import config from './src/config.json' with { type: 'json' };
async function create_invoice_pdf(invoice_number, client_name, client_details, payment_details, services = []) {
// Layout constants
const MARGINS = { left: 50, right: 50 };
const ROW_HEIGHT = 40;
const ROW_SPACING = 40;
const COLUMN_POSITIONS = [50, 250, 330, 410, 470];
const PAYMENT_DETAILS_Y = 150;
const now = new Date();
const creation_date = now.toISOString().split('T')[0];
const due_date = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
const pdfDoc = await PDFDocument.create();
const page = pdfDoc.addPage();
const { width, height } = page.getSize();
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
const boldFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
// Header with company name and invoice details
page.drawText(config.company.name, { x: MARGINS.left, y: height - 65, font: boldFont, size: 18 });
page.drawText(`Invoice #${invoice_number.toString().padStart(4, '0')}`, { x: width - 154, y: height - 65, font: boldFont, size: 18 });
page.drawText(`Issued on: ${creation_date}`, { x: width - 148, y: height - 85, font, size: 11 });
page.drawText(`Due by: ${due_date}`, { x: width - 135, y: height - 100, font, size: 11 });
// From section
const fromStartY = height - 135;
page.drawText("From", { x: MARGINS.left, y: fromStartY, font: boldFont, size: 11 });
page.drawText(config.company.name, { x: MARGINS.left, y: fromStartY - 20, font, size: 11 });
config.company.details.forEach((line, index) => {
page.drawText(line, { x: MARGINS.left, y: fromStartY - 35 - (index * 15), font, size: 11 });
});
// To section
const toStartY = fromStartY;
page.drawText("To", { x: 300, y: toStartY, font: boldFont, size: 11 });
page.drawText(client_name, { x: 300, y: toStartY - 20, font, size: 11 });
client_details.forEach((line, index) => {
page.drawText(line, { x: 300, y: toStartY - 35 - (index * 15), font, size: 11 });
});
// Services table
const tableStartY = height - 295;
const tableHeaders = ['Product', 'Quantity', 'Unit Price', 'Tax', 'Total'];
const columnX = COLUMN_POSITIONS;
// Table header
page.drawRectangle({ x: 45, y: tableStartY - 5, width: 510, height: 25, color: rgb(0.9, 0.9, 0.9) });
tableHeaders.forEach((header, index) => {
page.drawText(header, { x: columnX[index], y: tableStartY + 6, font: boldFont, size: 10 });
});
// Table rows
let currentY = tableStartY - 35;
let subtotal = 0;
let totalTax = 0;
services.forEach((service, rowIndex) => {
const lineTotal = service.quantity * service.unit_price;
const tax = lineTotal * service.tax_rate;
const total = lineTotal + tax;
subtotal += lineTotal;
totalTax += tax;
// Alternating row colors
if (rowIndex % 2 === 0) {
page.drawRectangle({ x: 45, y: currentY - 5, width: 510, height: ROW_HEIGHT, color: rgb(0.98, 0.98, 0.98) });
}
page.drawText(`${service.description}`, { x: columnX[0], y: currentY + 17, font, size: 10 });
page.drawText(`${service.period || ''}`, { x: columnX[0], y: currentY + 17 - 12, font, size: 8, color: rgb(0.5, 0.5, 0.5) });
page.drawText(`${service.quantity}`, { x: columnX[1], y: currentY + 12, font, size: 10 });
page.drawText(`$ ${service.unit_price.toFixed(2)}`, { x: columnX[2], y: currentY + 12, font, size: 10 });
page.drawText(`$ ${tax.toFixed(2)}`, { x: columnX[3], y: currentY + 12, font, size: 10 });
page.drawText(`$ ${total.toFixed(2)}`, { x: columnX[4], y: currentY + 12, font, size: 10 });
currentY -= ROW_SPACING;
});
// Invoice Summary
const summaryStartY = currentY - 30;
const summaryX = 350;
page.drawText("Invoice Summary", { x: summaryX, y: summaryStartY, font: boldFont, size: 12 });
page.drawText("Subtotal", { x: summaryX, y: summaryStartY - 25, font, size: 10 });
page.drawText(`$ ${subtotal.toFixed(2)}`, { x: summaryX + 100, y: summaryStartY - 25, font, size: 10 });
page.drawText("Tax", { x: summaryX, y: summaryStartY - 40, font, size: 10 });
page.drawText(`$ ${totalTax.toFixed(2)}`, { x: summaryX + 100, y: summaryStartY - 40, font, size: 10 });
page.drawText("Total", { x: summaryX, y: summaryStartY - 55, font: boldFont, size: 10 });
page.drawText(`$ ${(subtotal + totalTax).toFixed(2)}`, { x: summaryX + 100, y: summaryStartY - 55, font: boldFont, size: 10 });
// Payment Details
const paymentDetailsY = PAYMENT_DETAILS_Y;
page.drawText("Payment details", { x: MARGINS.left, y: paymentDetailsY, font: boldFont, size: 12 });
payment_details.forEach((line, index) => {
page.drawText(line, { x: MARGINS.left, y: paymentDetailsY - 20 - (index * 15), font, size: 10 });
});
return await pdfDoc.save();
}
async function generateTestPDFs() {
const outputDir = './test-output';
// Create output directory if it doesn't exist
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
console.log('Generating test PDFs...');
for (let i = 0; i < config.clients.length; i++) {
const client = config.clients[i];
const invoice_number = 1000 + i; // Test invoice numbers
try {
const pdfBytes = await create_invoice_pdf(
invoice_number,
client.name,
client.details,
client.payment_details,
client.services || []
);
const filename = `test-invoice-${invoice_number}-${client.name.replace(/[^a-zA-Z0-9]/g, '_')}.pdf`;
const filepath = path.join(outputDir, filename);
fs.writeFileSync(filepath, pdfBytes);
console.log(`✓ Generated: ${filepath}`);
} catch (error) {
console.error(`✗ Failed to generate PDF for ${client.name}:`, error);
}
}
console.log(`\nTest PDFs saved to: ${path.resolve(outputDir)}`);
}
// Run the test
generateTestPDFs().catch(console.error);