UNPKG

@fin.cx/skr

Version:

SKR03 and SKR04 German accounting standards for double-entry bookkeeping

548 lines (515 loc) 26.2 kB
import * as plugins from './plugins.js'; import * as path from 'path'; export class PdfReportGenerator { constructor(exportPath, options) { this.pdfInstance = null; this.exportPath = exportPath; this.options = options; } /** * Initializes the PDF generator */ async initialize() { this.pdfInstance = new plugins.smartpdf.SmartPdf(); await this.pdfInstance.start(); } /** * Generates the trial balance PDF report */ async generateTrialBalancePdf(report) { if (!this.pdfInstance) { throw new Error('PDF generator not initialized'); } const html = this.generateTrialBalanceHtml(report); const pdfResult = await this.pdfInstance.getA4PdfResultForHtmlString(html); return Buffer.from(pdfResult.buffer); } /** * Generates the income statement PDF report */ async generateIncomeStatementPdf(report) { if (!this.pdfInstance) { throw new Error('PDF generator not initialized'); } const html = this.generateIncomeStatementHtml(report); const pdfResult = await this.pdfInstance.getA4PdfResultForHtmlString(html); return Buffer.from(pdfResult.buffer); } /** * Generates the balance sheet PDF report */ async generateBalanceSheetPdf(report) { if (!this.pdfInstance) { throw new Error('PDF generator not initialized'); } const html = this.generateBalanceSheetHtml(report); const pdfResult = await this.pdfInstance.getA4PdfResultForHtmlString(html); return Buffer.from(pdfResult.buffer); } /** * Generates the comprehensive Jahresabschluss PDF */ async generateJahresabschlussPdf(trialBalance, incomeStatement, balanceSheet) { if (!this.pdfInstance) { throw new Error('PDF generator not initialized'); } const html = this.generateJahresabschlussHtml(trialBalance, incomeStatement, balanceSheet); const pdfResult = await this.pdfInstance.getA4PdfResultForHtmlString(html); return Buffer.from(pdfResult.buffer); } /** * Generates HTML for trial balance report */ generateTrialBalanceHtml(report) { const entries = report.entries || []; const tableRows = entries.map(entry => ` <tr> <td>${entry.accountNumber}</td> <td>${entry.accountName}</td> <td class="number">${this.formatGermanNumber(0)}</td> <td class="number">${this.formatGermanNumber(entry.debitBalance)}</td> <td class="number">${this.formatGermanNumber(entry.creditBalance)}</td> <td class="number">${this.formatGermanNumber(entry.netBalance)}</td> </tr> `).join(''); return ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> ${this.getBaseStyles()} </style> </head> <body> ${this.generateHeader('Summen- und Saldenliste')} <table class="report-table"> <thead> <tr> <th>Konto</th> <th>Bezeichnung</th> <th>Anfangssaldo</th> <th>Soll</th> <th>Haben</th> <th>Saldo</th> </tr> </thead> <tbody> ${tableRows} </tbody> <tfoot> <tr class="total-row"> <td colspan="3">Summe</td> <td class="number">${this.formatGermanNumber(report.totalDebits)}</td> <td class="number">${this.formatGermanNumber(report.totalCredits)}</td> <td class="number">${this.formatGermanNumber(report.totalDebits - report.totalCredits)}</td> </tr> </tfoot> </table> ${this.generateFooter()} </body> </html> `; } /** * Generates HTML for income statement report */ generateIncomeStatementHtml(report) { const revenueRows = (report.revenue || []).map(entry => ` <tr> <td>${entry.accountNumber}</td> <td>${entry.accountName}</td> <td class="number">${this.formatGermanNumber(entry.amount)}</td> </tr> `).join(''); const expenseRows = (report.expenses || []).map(entry => ` <tr> <td>${entry.accountNumber}</td> <td>${entry.accountName}</td> <td class="number">${this.formatGermanNumber(entry.amount)}</td> </tr> `).join(''); return ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> ${this.getBaseStyles()} </style> </head> <body> ${this.generateHeader('Gewinn- und Verlustrechnung')} <h2>Erträge</h2> <table class="report-table"> <thead> <tr> <th>Konto</th> <th>Bezeichnung</th> <th>Betrag</th> </tr> </thead> <tbody> ${revenueRows} </tbody> <tfoot> <tr class="subtotal-row"> <td colspan="2">Summe Erträge</td> <td class="number">${this.formatGermanNumber(report.totalRevenue)}</td> </tr> </tfoot> </table> <h2>Aufwendungen</h2> <table class="report-table"> <thead> <tr> <th>Konto</th> <th>Bezeichnung</th> <th>Betrag</th> </tr> </thead> <tbody> ${expenseRows} </tbody> <tfoot> <tr class="subtotal-row"> <td colspan="2">Summe Aufwendungen</td> <td class="number">${this.formatGermanNumber(report.totalExpenses)}</td> </tr> </tfoot> </table> <div class="result-section"> <h2>Ergebnis</h2> <table class="summary-table"> <tr> <td>Erträge</td> <td class="number">${this.formatGermanNumber(report.totalRevenue)}</td> </tr> <tr> <td>Aufwendungen</td> <td class="number">- ${this.formatGermanNumber(report.totalExpenses)}</td> </tr> <tr class="total-row"> <td>${report.netIncome >= 0 ? 'Jahresüberschuss' : 'Jahresfehlbetrag'}</td> <td class="number ${report.netIncome >= 0 ? 'positive' : 'negative'}"> ${this.formatGermanNumber(report.netIncome)} </td> </tr> </table> </div> ${this.generateFooter()} </body> </html> `; } /** * Generates HTML for balance sheet report */ generateBalanceSheetHtml(report) { const assetRows = [...(report.assets.current || []), ...(report.assets.fixed || [])].map(entry => ` <tr> <td>${entry.accountNumber}</td> <td>${entry.accountName}</td> <td class="number">${this.formatGermanNumber(entry.amount)}</td> </tr> `).join(''); const liabilityRows = [...(report.liabilities.current || []), ...(report.liabilities.longTerm || [])].map(entry => ` <tr> <td>${entry.accountNumber}</td> <td>${entry.accountName}</td> <td class="number">${this.formatGermanNumber(entry.amount)}</td> </tr> `).join(''); const equityRows = (report.equity.entries || []).map(entry => ` <tr> <td>${entry.accountNumber}</td> <td>${entry.accountName}</td> <td class="number">${this.formatGermanNumber(entry.amount)}</td> </tr> `).join(''); return ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> ${this.getBaseStyles()} </style> </head> <body> ${this.generateHeader('Bilanz')} <div class="balance-sheet"> <div class="aktiva"> <h2>Aktiva</h2> <table class="report-table"> <thead> <tr> <th>Konto</th> <th>Bezeichnung</th> <th>Betrag</th> </tr> </thead> <tbody> ${assetRows} </tbody> <tfoot> <tr class="total-row"> <td colspan="2">Summe Aktiva</td> <td class="number">${this.formatGermanNumber(report.assets.totalAssets)}</td> </tr> </tfoot> </table> </div> <div class="passiva"> <h2>Passiva</h2> <h3>Eigenkapital</h3> <table class="report-table"> <tbody> ${equityRows} </tbody> <tfoot> <tr class="subtotal-row"> <td colspan="2">Summe Eigenkapital</td> <td class="number">${this.formatGermanNumber(report.equity.totalEquity)}</td> </tr> </tfoot> </table> <h3>Fremdkapital</h3> <table class="report-table"> <tbody> ${liabilityRows} </tbody> <tfoot> <tr class="subtotal-row"> <td colspan="2">Summe Fremdkapital</td> <td class="number">${this.formatGermanNumber(report.liabilities.totalLiabilities)}</td> </tr> </tfoot> </table> <table class="summary-table"> <tr class="total-row"> <td>Summe Passiva</td> <td class="number">${this.formatGermanNumber(report.liabilities.totalLiabilities + report.equity.totalEquity)}</td> </tr> </table> </div> </div> ${this.generateFooter()} </body> </html> `; } /** * Generates comprehensive Jahresabschluss HTML */ generateJahresabschlussHtml(trialBalance, incomeStatement, balanceSheet) { return ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> ${this.getBaseStyles()} .page-break { page-break-after: always; } .cover-page { display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100vh; text-align: center; } .cover-page h1 { font-size: 36px; margin-bottom: 20px; } .cover-page h2 { font-size: 24px; margin-bottom: 40px; } .toc { margin-top: 50px; } .toc h2 { margin-bottom: 20px; } .toc ul { list-style: none; padding: 0; } .toc li { margin: 10px 0; font-size: 16px; } </style> </head> <body> <div class="cover-page"> <h1>Jahresabschluss</h1> <h2>${this.options.companyName}</h2> <p>Geschäftsjahr ${this.options.fiscalYear}</p> <p>${this.formatGermanDate(this.options.dateFrom)} bis ${this.formatGermanDate(this.options.dateTo)}</p> <div class="toc"> <h2>Inhalt</h2> <ul> <li>1. Bilanz</li> <li>2. Gewinn- und Verlustrechnung</li> <li>3. Summen- und Saldenliste</li> </ul> </div> </div> <div class="page-break"></div> ${this.generateBalanceSheetHtml(balanceSheet)} <div class="page-break"></div> ${this.generateIncomeStatementHtml(incomeStatement)} <div class="page-break"></div> ${this.generateTrialBalanceHtml(trialBalance)} </body> </html> `; } /** * Generates the report header */ generateHeader(reportTitle) { return ` <div class="header"> <h1>${this.options.companyName}</h1> ${this.options.companyAddress ? `<p>${this.options.companyAddress}</p>` : ''} ${this.options.taxId ? `<p>Steuernummer: ${this.options.taxId}</p>` : ''} ${this.options.registrationNumber ? `<p>Handelsregister: ${this.options.registrationNumber}</p>` : ''} <hr> <h2>${reportTitle}</h2> <p>Periode: ${this.formatGermanDate(this.options.dateFrom)} bis ${this.formatGermanDate(this.options.dateTo)}</p> </div> `; } /** * Generates the report footer */ generateFooter() { const preparedDate = this.options.preparedDate || new Date(); return ` <div class="footer"> <hr> <p>Erstellt am: ${this.formatGermanDate(preparedDate)}</p> ${this.options.preparedBy ? `<p>Erstellt von: ${this.options.preparedBy}</p>` : ''} <p class="disclaimer"> Dieser Bericht wurde automatisch generiert und ist Teil des revisionssicheren Jahresabschluss-Exports gemäß GoBD. </p> </div> `; } /** * Gets the base CSS styles for all reports */ getBaseStyles() { return ` body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 40px; color: #333; line-height: 1.6; } h1 { color: #2c3e50; margin-bottom: 10px; } h2 { color: #34495e; margin-top: 30px; margin-bottom: 15px; } h3 { color: #7f8c8d; margin-top: 20px; margin-bottom: 10px; } .header { text-align: center; margin-bottom: 40px; } .footer { margin-top: 50px; text-align: center; font-size: 12px; color: #7f8c8d; } .disclaimer { margin-top: 20px; font-style: italic; } table { width: 100%; border-collapse: collapse; margin: 20px 0; } th { background-color: #34495e; color: white; padding: 10px; text-align: left; font-weight: 600; } td { padding: 8px; border-bottom: 1px solid #ecf0f1; } tbody tr:hover { background-color: #f8f9fa; } .number { text-align: right; font-family: 'Courier New', monospace; } .total-row { font-weight: bold; background-color: #ecf0f1; } .subtotal-row { font-weight: 600; background-color: #f8f9fa; } .positive { color: #27ae60; } .negative { color: #e74c3c; } .result-section { margin-top: 40px; padding: 20px; background-color: #f8f9fa; border-radius: 5px; } .summary-table { max-width: 500px; margin: 20px auto; } .balance-sheet { display: flex; gap: 40px; } .aktiva, .passiva { flex: 1; } @media print { body { margin: 20px; } .page-break { page-break-after: always; } } `; } /** * Formats number in German format (1.234,56) */ formatGermanNumber(value) { return value.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } /** * Formats date in German format (DD.MM.YYYY) */ formatGermanDate(date) { return date.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }); } /** * Saves a PDF report to the export directory */ async savePdfReport(filename, pdfBuffer) { const reportsDir = path.join(this.exportPath, 'data', 'reports'); await plugins.smartfile.fs.ensureDir(reportsDir); const filePath = path.join(reportsDir, filename); await plugins.smartfile.memory.toFs(pdfBuffer, filePath); return filePath; } /** * Closes the PDF generator */ async close() { if (this.pdfInstance) { await this.pdfInstance.stop(); this.pdfInstance = null; } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLmV4cG9ydC5wZGYuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9za3IuZXhwb3J0LnBkZi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQWU3QixNQUFNLE9BQU8sa0JBQWtCO0lBSzdCLFlBQVksVUFBa0IsRUFBRSxPQUEwQjtRQUZsRCxnQkFBVyxHQUFxQyxJQUFJLENBQUM7UUFHM0QsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLENBQUM7UUFDN0IsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7SUFDekIsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLFVBQVU7UUFDckIsSUFBSSxDQUFDLFdBQVcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDbkQsTUFBTSxJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ2pDLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyx1QkFBdUIsQ0FBQyxNQUEyQjtRQUM5RCxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1lBQ3RCLE1BQU0sSUFBSSxLQUFLLENBQUMsK0JBQStCLENBQUMsQ0FBQztRQUNuRCxDQUFDO1FBRUQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLHdCQUF3QixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQ25ELE1BQU0sU0FBUyxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQywyQkFBMkIsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMzRSxPQUFPLE1BQU0sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ3ZDLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQywwQkFBMEIsQ0FBQyxNQUF3QjtRQUM5RCxJQUFJLENBQUMs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