server.js aktualisiert

This commit is contained in:
alf
2026-07-14 14:05:26 +00:00
parent ae53161f88
commit 63645aef6e

136
server.js
View File

@ -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)
]); ];
});
// Draw table rowData.forEach((cell, i) => {
const tableTop = doc.y; doc.text(cell, x, y, { width: scaledColumns[i].width, align: 'left' });
const columnSpacing = 10; x += scaledColumns[i].width + columnSpacing;
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 y += rowHeight;
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();
}); });
}); });