server.js aktualisiert
This commit is contained in:
136
server.js
136
server.js
@ -345,6 +345,7 @@ app.post('/accounts/:id', requireAuth, (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// PDF Export route
|
// PDF Export route
|
||||||
|
// PDF Export route - OPTIMIZED
|
||||||
app.get('/accounts/:id/pdf', requireAuth, (req, res) => {
|
app.get('/accounts/:id/pdf', requireAuth, (req, res) => {
|
||||||
const accountId = req.params.id;
|
const accountId = req.params.id;
|
||||||
|
|
||||||
@ -353,7 +354,6 @@ app.get('/accounts/:id/pdf', requireAuth, (req, res) => {
|
|||||||
return res.status(404).send('Konto nicht gefunden');
|
return res.status(404).send('Konto nicht gefunden');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all transactions for this account
|
|
||||||
db.all(`
|
db.all(`
|
||||||
SELECT t.*, u.username as created_by_name, i.name as item_name
|
SELECT t.*, u.username as created_by_name, i.name as item_name
|
||||||
FROM transactions t
|
FROM transactions t
|
||||||
@ -367,80 +367,110 @@ app.get('/accounts/:id/pdf', requireAuth, (req, res) => {
|
|||||||
return res.status(500).send('Fehler beim Generieren des PDFs');
|
return res.status(500).send('Fehler beim Generieren des PDFs');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create PDF document
|
const doc = new PDFDocument({
|
||||||
const doc = new PDFDocument({ margin: 30 });
|
margin: 30,
|
||||||
|
bufferPages: true,
|
||||||
|
autoFirstPage: true,
|
||||||
|
compress: true
|
||||||
|
});
|
||||||
|
|
||||||
// Set response headers for PDF download
|
|
||||||
res.setHeader('Content-Type', 'application/pdf');
|
res.setHeader('Content-Type', 'application/pdf');
|
||||||
res.setHeader('Content-Disposition', `attachment; filename="Kontoauszug_${account.name.replace(/\s+/g, '_')}_${new Date().toISOString().split('T')[0]}.pdf"`);
|
res.setHeader('Content-Disposition', `attachment; filename="Kontoauszug_${account.name.replace(/\s+/g, '_')}_${new Date().toISOString().split('T')[0]}.pdf"`);
|
||||||
|
|
||||||
// Pipe the PDF to the response
|
|
||||||
doc.pipe(res);
|
doc.pipe(res);
|
||||||
|
|
||||||
// Add content to PDF
|
// Title - smaller
|
||||||
doc.fontSize(20).text(`Kontoauszug für ${account.name}`, { align: 'center' });
|
doc.fontSize(16).text(`Kontoauszug für ${account.name}`, { align: 'center' });
|
||||||
doc.moveDown();
|
doc.moveDown(0.5);
|
||||||
|
|
||||||
doc.fontSize(14).text(`Kontostand: ${account.balance.toFixed(2)} €`);
|
// Account info - smaller fonts
|
||||||
doc.fontSize(12).text(`Erstellt am: ${new Date(account.created_at).toLocaleDateString('de-DE')}`);
|
doc.fontSize(10);
|
||||||
doc.moveDown(2);
|
doc.text(`Kontostand: ${account.balance.toFixed(2)} €`);
|
||||||
|
doc.text(`Konto erstellt: ${new Date(account.created_at).toLocaleDateString('de-DE')}`);
|
||||||
|
doc.text(`Generiert am: ${new Date().toLocaleString('de-DE')}`);
|
||||||
|
doc.text(`Generiert von: ${req.user.username}`);
|
||||||
|
doc.moveDown(1.5);
|
||||||
|
|
||||||
doc.fontSize(16).text('Transaktionshistorie:', { underline: true });
|
// Transaction history header
|
||||||
doc.moveDown();
|
doc.fontSize(12).text('Transaktionshistorie:', { underline: true });
|
||||||
|
doc.moveDown(1);
|
||||||
|
|
||||||
// Table headers
|
// Table setup
|
||||||
const table = {
|
const tableTop = doc.y;
|
||||||
headers: ['Datum', 'Typ', 'Betrag', 'Beschreibung', 'Artikel', 'Durchgeführt von'],
|
const columnSpacing = 8;
|
||||||
rows: []
|
const pageWidth = doc.page.width - 60;
|
||||||
};
|
|
||||||
|
|
||||||
transactions.forEach(tx => {
|
const columns = [
|
||||||
table.rows.push([
|
{ name: 'Datum', width: 100 },
|
||||||
|
{ name: 'Typ', width: 60 },
|
||||||
|
{ name: 'Betrag', width: 50 },
|
||||||
|
{ name: 'Beschreibung', width: 120 },
|
||||||
|
{ name: 'Artikel', width: 80 },
|
||||||
|
{ name: 'Von', width: 70 }
|
||||||
|
];
|
||||||
|
|
||||||
|
const totalWidth = columns.reduce((sum, col) => sum + col.width, 0) + (columnSpacing * (columns.length - 1));
|
||||||
|
const scaleFactor = totalWidth > pageWidth ? pageWidth / totalWidth : 1;
|
||||||
|
const scaledColumns = columns.map(col => ({ ...col, width: col.width * scaleFactor }));
|
||||||
|
|
||||||
|
// Draw headers
|
||||||
|
doc.font('Helvetica-Bold').fontSize(10);
|
||||||
|
let x = 30;
|
||||||
|
scaledColumns.forEach(col => {
|
||||||
|
doc.text(col.name, x, tableTop, { width: col.width, align: 'left' });
|
||||||
|
x += col.width + columnSpacing;
|
||||||
|
});
|
||||||
|
|
||||||
|
const tableEndX = x - columnSpacing;
|
||||||
|
doc.moveTo(30, tableTop + 14).lineTo(tableEndX, tableTop + 14).stroke('#000000');
|
||||||
|
|
||||||
|
// Draw rows
|
||||||
|
doc.font('Helvetica').fontSize(9);
|
||||||
|
let y = tableTop + 22;
|
||||||
|
const rowHeight = 16;
|
||||||
|
|
||||||
|
transactions.forEach((tx, rowIndex) => {
|
||||||
|
if (y > doc.page.height - 80) {
|
||||||
|
doc.addPage();
|
||||||
|
y = 30;
|
||||||
|
doc.font('Helvetica-Bold').fontSize(10);
|
||||||
|
x = 30;
|
||||||
|
scaledColumns.forEach(col => {
|
||||||
|
doc.text(col.name, x, y, { width: col.width, align: 'left' });
|
||||||
|
x += col.width + columnSpacing;
|
||||||
|
});
|
||||||
|
doc.moveTo(30, y + 14).lineTo(tableEndX, y + 14).stroke('#000000');
|
||||||
|
doc.font('Helvetica').fontSize(9);
|
||||||
|
y += 22;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = 30;
|
||||||
|
const rowData = [
|
||||||
new Date(tx.created_at).toLocaleString('de-DE'),
|
new Date(tx.created_at).toLocaleString('de-DE'),
|
||||||
tx.type === 'deposit' ? 'Einzahlung' : 'Abbuchung',
|
tx.type === 'deposit' ? 'Einzahlung' : 'Abbuchung',
|
||||||
`${tx.amount.toFixed(2)} €`,
|
`${tx.amount.toFixed(2)} €`,
|
||||||
tx.description || '-',
|
(tx.description || '-').substring(0, 40),
|
||||||
tx.item_name || '-',
|
(tx.item_name || '-').substring(0, 25),
|
||||||
tx.created_by_name || 'System'
|
(tx.created_by_name || 'System').substring(0, 20)
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
rowData.forEach((cell, i) => {
|
||||||
|
doc.text(cell, x, y, { width: scaledColumns[i].width, align: 'left' });
|
||||||
|
x += scaledColumns[i].width + columnSpacing;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Draw table
|
y += rowHeight;
|
||||||
const tableTop = doc.y;
|
|
||||||
const columnSpacing = 10;
|
|
||||||
const columnWidths = [80, 60, 50, 120, 80, 80];
|
|
||||||
|
|
||||||
// Draw headers
|
|
||||||
doc.font('Helvetica-Bold');
|
|
||||||
let x = 30;
|
|
||||||
table.headers.forEach((header, i) => {
|
|
||||||
doc.text(header, x, tableTop, { width: columnWidths[i], align: 'left' });
|
|
||||||
x += columnWidths[i] + columnSpacing;
|
|
||||||
});
|
|
||||||
doc.font('Helvetica');
|
|
||||||
|
|
||||||
// Draw rows
|
|
||||||
let y = tableTop + 20;
|
|
||||||
transactions.forEach((tx, rowIndex) => {
|
|
||||||
x = 30;
|
|
||||||
const row = table.rows[rowIndex];
|
|
||||||
row.forEach((cell, i) => {
|
|
||||||
doc.text(cell, x, y, { width: columnWidths[i], align: 'left' });
|
|
||||||
x += columnWidths[i] + columnSpacing;
|
|
||||||
});
|
|
||||||
y += 20;
|
|
||||||
|
|
||||||
// Add horizontal line between rows
|
|
||||||
if (rowIndex < transactions.length - 1) {
|
if (rowIndex < transactions.length - 1) {
|
||||||
doc.moveTo(30, y - 5).lineTo(550, y - 5).stroke('#cccccc');
|
doc.moveTo(30, y - 6).lineTo(tableEndX, y - 6).stroke('#e0e0e0');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
doc.moveDown(2);
|
doc.moveDown(2);
|
||||||
doc.fontSize(10).text(`Generiert am: ${new Date().toLocaleString('de-DE')}`, { align: 'right' });
|
doc.fontSize(10);
|
||||||
doc.fontSize(10).text(`Generiert von: ${req.user.username}`, { align: 'right' });
|
doc.text(`Gesamt: ${transactions.length} Transaktionen`, { align: 'right' });
|
||||||
|
doc.text(`Endstand: ${account.balance.toFixed(2)} €`, { align: 'right' });
|
||||||
|
|
||||||
// Finalize PDF
|
|
||||||
doc.end();
|
doc.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user