UNPKG

@fin.cx/skr

Version:

SKR03 and SKR04 German accounting standards for double-entry bookkeeping

753 lines 57.1 kB
import * as plugins from './plugins.js'; import * as path from 'path'; import { ChartOfAccounts } from './skr.classes.chartofaccounts.js'; import { Ledger } from './skr.classes.ledger.js'; import { Reports } from './skr.classes.reports.js'; import { Account } from './skr.classes.account.js'; import { Transaction } from './skr.classes.transaction.js'; import { JournalEntry } from './skr.classes.journalentry.js'; import { SkrExport } from './skr.export.js'; import { LedgerExporter } from './skr.export.ledger.js'; import { AccountsExporter } from './skr.export.accounts.js'; import { BalancesExporter } from './skr.export.balances.js'; import { PdfReportGenerator } from './skr.export.pdf.js'; import { SecurityManager } from './skr.security.js'; import { InvoiceAdapter } from './skr.invoice.adapter.js'; import { InvoiceStorage } from './skr.invoice.storage.js'; import { InvoiceBookingEngine } from './skr.invoice.booking.js'; /** * Main API class for SKR accounting operations */ export class SkrApi { constructor(config) { this.config = config; this.ledger = null; this.reports = null; this.initialized = false; this.currentSKRType = null; this.invoiceAdapter = null; this.invoiceStorage = null; this.invoiceBookingEngine = null; this.chartOfAccounts = new ChartOfAccounts(config); this.logger = new plugins.smartlog.Smartlog({ logContext: { company: 'fin.cx', companyunit: 'skr', containerName: 'SkrApi', environment: 'local', runtime: 'node', zone: 'local', }, }); } /** * Initialize the API with specified SKR type */ async initialize(skrType) { this.logger.log('info', `Initializing SKR API with ${skrType}`); // Initialize chart of accounts if (skrType === 'SKR03') { await this.chartOfAccounts.initializeSKR03(); } else if (skrType === 'SKR04') { await this.chartOfAccounts.initializeSKR04(); } else { throw new Error(`Invalid SKR type: ${skrType}`); } this.currentSKRType = skrType; this.ledger = new Ledger(skrType); this.reports = new Reports(skrType); // Initialize invoice components this.invoiceAdapter = new InvoiceAdapter(); const invoicePath = this.config.invoiceExportPath || path.resolve(process.cwd(), 'exports', 'invoices'); this.invoiceStorage = new InvoiceStorage(invoicePath); this.invoiceBookingEngine = new InvoiceBookingEngine(skrType); this.initialized = true; this.logger.log('info', 'SKR API initialized successfully'); } /** * Ensure API is initialized */ ensureInitialized() { if (!this.initialized || !this.currentSKRType) { throw new Error('API not initialized. Call initialize() first.'); } } // ========== Account Management ========== /** * Create a new account */ async createAccount(accountData) { this.ensureInitialized(); return await this.chartOfAccounts.createCustomAccount(accountData); } /** * Get account by number */ async getAccount(accountNumber) { this.ensureInitialized(); return await this.chartOfAccounts.getAccountByNumber(accountNumber); } /** * Update an account */ async updateAccount(accountNumber, updates) { this.ensureInitialized(); return await this.chartOfAccounts.updateAccount(accountNumber, updates); } /** * Delete an account */ async deleteAccount(accountNumber) { this.ensureInitialized(); await this.chartOfAccounts.deleteAccount(accountNumber); } /** * List accounts with optional filter */ async listAccounts(filter) { this.ensureInitialized(); return await this.chartOfAccounts.getAllAccounts(filter); } /** * Search accounts by term */ async searchAccounts(searchTerm) { this.ensureInitialized(); return await this.chartOfAccounts.searchAccounts(searchTerm); } /** * Get accounts by class */ async getAccountsByClass(accountClass) { this.ensureInitialized(); return await this.chartOfAccounts.getAccountsByClass(accountClass); } /** * Get accounts by type */ async getAccountsByType(accountType) { this.ensureInitialized(); return await this.chartOfAccounts.getAccountsByType(accountType); } // ========== Transaction Management ========== /** * Post a simple transaction */ async postTransaction(transactionData) { this.ensureInitialized(); if (!this.ledger) throw new Error('Ledger not initialized'); return await this.ledger.postTransaction(transactionData); } /** * Post a journal entry */ async postJournalEntry(journalData) { this.ensureInitialized(); if (!this.ledger) throw new Error('Ledger not initialized'); return await this.ledger.postJournalEntry(journalData); } /** * Get transaction by ID */ async getTransaction(transactionId) { this.ensureInitialized(); return await Transaction.getTransactionById(transactionId); } /** * List transactions with optional filter */ async listTransactions(filter) { this.ensureInitialized(); return await this.chartOfAccounts.getTransactions(filter); } /** * Get transactions for specific account */ async getAccountTransactions(accountNumber) { this.ensureInitialized(); return await this.chartOfAccounts.getAccountTransactions(accountNumber); } /** * Reverse a transaction */ async reverseTransaction(transactionId) { this.ensureInitialized(); return await this.chartOfAccounts.reverseTransaction(transactionId); } /** * Reverse a journal entry */ async reverseJournalEntry(journalId) { this.ensureInitialized(); if (!this.ledger) throw new Error('Ledger not initialized'); return await this.ledger.reverseJournalEntry(journalId); } // ========== Reporting ========== /** * Generate trial balance */ async generateTrialBalance(params) { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.getTrialBalance(params); } /** * Generate income statement */ async generateIncomeStatement(params) { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.getIncomeStatement(params); } /** * Generate balance sheet */ async generateBalanceSheet(params) { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.getBalanceSheet(params); } /** * Generate general ledger */ async generateGeneralLedger(params) { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.getGeneralLedger(params); } /** * Generate cash flow statement */ async generateCashFlowStatement(params) { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.getCashFlowStatement(params); } /** * Export report to CSV */ async exportReportToCSV(reportType, params) { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.exportToCSV(reportType, params); } /** * Export to DATEV format */ async exportToDATEV(params) { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.exportToDATEV(params); } // ========== Period Management ========== /** * Close accounting period */ async closePeriod(period, closingAccountNumber) { this.ensureInitialized(); if (!this.ledger) throw new Error('Ledger not initialized'); return await this.ledger.closeAccountingPeriod(period, closingAccountNumber); } /** * Get account balance */ async getAccountBalance(accountNumber, asOfDate) { this.ensureInitialized(); if (!this.ledger) throw new Error('Ledger not initialized'); return await this.ledger.getAccountBalance(accountNumber, asOfDate); } /** * Recalculate all account balances */ async recalculateBalances() { this.ensureInitialized(); if (!this.ledger) throw new Error('Ledger not initialized'); await this.ledger.recalculateAllBalances(); } // ========== Import/Export ========== /** * Import accounts from CSV */ async importAccountsFromCSV(csvContent) { this.ensureInitialized(); return await this.chartOfAccounts.importAccountsFromCSV(csvContent); } /** * Export accounts to CSV */ async exportAccountsToCSV() { this.ensureInitialized(); return await this.chartOfAccounts.exportAccountsToCSV(); } /** * Export Jahresabschluss in GoBD-compliant BagIt format * Creates a revision-safe export for 10-year archival */ async exportJahresabschluss(options) { this.ensureInitialized(); if (!this.ledger || !this.reports || !this.currentSKRType) { throw new Error('API not fully initialized'); } this.logger.log('info', `Starting Jahresabschluss export for fiscal year ${options.fiscalYear}`); // Create export instance const exporter = new SkrExport(options); // Create BagIt structure await exporter.createBagItStructure(); await exporter.createExportMetadata(this.currentSKRType); await exporter.createSchemas(); // Export accounting data await this.exportLedgerData(exporter, options); await this.exportAccountData(exporter, options); await this.exportBalanceData(exporter, options); // Generate PDF reports if requested if (options.generatePdfReports) { await this.generatePdfReports(exporter, options); } // Sign export if requested if (options.signExport) { await this.signExport(exporter, options); } // Create manifests and validate await exporter.writeManifests(); const merkleRoot = await exporter.createMerkleTree(); const isValid = await exporter.validateBagIt(); if (!isValid) { throw new Error('BagIt validation failed'); } this.logger.log('ok', `Jahresabschluss export completed. Merkle root: ${merkleRoot}`); return options.exportPath; } /** * Export ledger data in NDJSON format */ async exportLedgerData(exporter, options) { if (!this.ledger) throw new Error('Ledger not initialized'); const ledgerExporter = new LedgerExporter(options.exportPath); await ledgerExporter.initialize(); // Get all transactions for the period const transactions = await this.chartOfAccounts.getTransactions({ dateFrom: options.dateFrom, dateTo: options.dateTo }); // Export each transaction for (const transaction of transactions) { const transactionData = transaction; await ledgerExporter.exportTransaction(transactionData); } // Get all journal entries for the period // Use MongoDB query syntax for date range const journalEntries = await JournalEntry.getInstances({ date: { $gte: options.dateFrom, $lte: options.dateTo }, // SmartData supports MongoDB query operators skrType: this.currentSKRType }); // Export each journal entry for (const entry of journalEntries) { const entryData = entry; await ledgerExporter.exportJournalEntry(entryData); } const entryCount = await ledgerExporter.close(); this.logger.log('info', `Exported ${entryCount} ledger entries`); } /** * Export account data in CSV format */ async exportAccountData(exporter, options) { const accountsExporter = new AccountsExporter(options.exportPath); // Get all accounts const accounts = await this.chartOfAccounts.getAllAccounts(); // Add each account to export for (const account of accounts) { const accountData = account; accountsExporter.addAccount(accountData); } // Export to CSV and JSON await accountsExporter.exportToCSV(); await accountsExporter.exportToJSON(); this.logger.log('info', `Exported ${accountsExporter.getAccountCount()} accounts`); } /** * Export balance data in CSV format */ async exportBalanceData(exporter, options) { if (!this.ledger) throw new Error('Ledger not initialized'); const balancesExporter = new BalancesExporter(options.exportPath, options.fiscalYear); // Get all accounts with balances const accounts = await this.chartOfAccounts.getAllAccounts(); for (const account of accounts) { const balance = await this.ledger.getAccountBalance(account.accountNumber, options.dateTo); if (balance) { balancesExporter.addBalance(account.accountNumber, account.accountName, balance, `${options.fiscalYear}`); } } // Export balance reports await balancesExporter.exportToCSV(); await balancesExporter.exportTrialBalance(); await balancesExporter.exportClassSummary(); this.logger.log('info', `Exported ${balancesExporter.getBalanceCount()} account balances`); } /** * Generate PDF reports for the export */ async generatePdfReports(exporter, options) { if (!this.reports) throw new Error('Reports not initialized'); const pdfOptions = { companyName: options.companyInfo?.name || 'Unternehmen', companyAddress: options.companyInfo?.address, taxId: options.companyInfo?.taxId, registrationNumber: options.companyInfo?.registrationNumber, fiscalYear: options.fiscalYear, dateFrom: options.dateFrom, dateTo: options.dateTo, preparedDate: new Date() }; const pdfGenerator = new PdfReportGenerator(options.exportPath, pdfOptions); await pdfGenerator.initialize(); try { // Generate reports const trialBalance = await this.reports.getTrialBalance({ dateFrom: options.dateFrom, dateTo: options.dateTo, skrType: this.currentSKRType }); const incomeStatement = await this.reports.getIncomeStatement({ dateFrom: options.dateFrom, dateTo: options.dateTo, skrType: this.currentSKRType }); const balanceSheet = await this.reports.getBalanceSheet({ dateFrom: options.dateFrom, dateTo: options.dateTo, skrType: this.currentSKRType }); // Generate PDFs const jahresabschlussPdf = await pdfGenerator.generateJahresabschlussPdf(trialBalance, incomeStatement, balanceSheet); // Save PDFs await pdfGenerator.savePdfReport('jahresabschluss.pdf', jahresabschlussPdf); // Store in BagIt structure await exporter.storeDocument(jahresabschlussPdf, 'jahresabschluss.pdf'); this.logger.log('info', 'PDF reports generated successfully'); } finally { await pdfGenerator.close(); } } /** * Sign the export with CAdES signature */ async signExport(exporter, options) { const signingOptions = { certificatePem: options.signExport ? undefined : undefined, // Use provided cert or generate privateKeyPem: options.signExport ? undefined : undefined, includeTimestamp: options.timestampExport !== false }; const security = new SecurityManager(signingOptions); // Generate self-signed certificate if none provided let cert, key; if (!signingOptions.certificatePem) { const generated = await security.generateSelfSignedCertificate(options.companyInfo?.name || 'SKR Export System'); cert = generated.certificate; key = generated.privateKey; } else { cert = signingOptions.certificatePem; key = signingOptions.privateKeyPem; } // Sign the manifest const manifestPath = path.resolve(options.exportPath, `jahresabschluss_${options.fiscalYear}`, 'manifest-sha256.txt'); await security.createDetachedSignature(manifestPath, path.resolve(options.exportPath, `jahresabschluss_${options.fiscalYear}`, 'data', 'metadata', 'signatures', 'manifest.cades')); this.logger.log('info', 'Export signed with CAdES signature'); } // ========== Utility Methods ========== /** * Get current SKR type */ getSKRType() { return this.currentSKRType; } /** * Get account class description */ getAccountClassDescription(accountClass) { this.ensureInitialized(); return this.chartOfAccounts.getAccountClassDescription(accountClass); } /** * Validate double-entry rules */ validateDoubleEntry(debitAmount, creditAmount) { if (!this.ledger) throw new Error('Ledger not initialized'); return this.ledger.validateDoubleEntry(debitAmount, creditAmount); } /** * Get unbalanced transactions (for audit) */ async getUnbalancedTransactions() { this.ensureInitialized(); if (!this.ledger) throw new Error('Ledger not initialized'); return await this.ledger.getUnbalancedTransactions(); } /** * Close the API and database connection */ async close() { await this.chartOfAccounts.close(); this.initialized = false; this.currentSKRType = null; this.ledger = null; this.reports = null; this.logger.log('info', 'SKR API closed'); } // ========== Batch Operations ========== /** * Post multiple transactions */ async postBatchTransactions(transactions) { this.ensureInitialized(); const results = []; const errors = []; for (let i = 0; i < transactions.length; i++) { try { const transaction = await this.postTransaction(transactions[i]); results.push(transaction); } catch (error) { errors.push({ index: i, error: error.message }); } } if (errors.length > 0) { this.logger.log('warn', `Batch transaction posting completed with ${errors.length} errors`); throw new Error(`Batch posting failed for ${errors.length} transactions: ${JSON.stringify(errors)}`); } return results; } /** * Create multiple accounts */ async createBatchAccounts(accounts) { this.ensureInitialized(); const results = []; const errors = []; for (let i = 0; i < accounts.length; i++) { try { const account = await this.createAccount(accounts[i]); results.push(account); } catch (error) { errors.push({ index: i, error: error.message }); } } if (errors.length > 0) { this.logger.log('warn', `Batch account creation completed with ${errors.length} errors`); throw new Error(`Batch creation failed for ${errors.length} accounts: ${JSON.stringify(errors)}`); } return results; } // ========== Pagination Support ========== /** * Get paginated accounts */ async getAccountsPaginated(page = 1, pageSize = 50, filter) { this.ensureInitialized(); const allAccounts = await this.listAccounts(filter); const total = allAccounts.length; const totalPages = Math.ceil(total / pageSize); const start = (page - 1) * pageSize; const end = start + pageSize; const data = allAccounts.slice(start, end); return { data, total, page, pageSize, totalPages, }; } /** * Get paginated transactions */ async getTransactionsPaginated(page = 1, pageSize = 50, filter) { this.ensureInitialized(); const allTransactions = await this.listTransactions(filter); const total = allTransactions.length; const totalPages = Math.ceil(total / pageSize); const start = (page - 1) * pageSize; const end = start + pageSize; const data = allTransactions.slice(start, end); return { data, total, page, pageSize, totalPages, }; } // ========== Invoice Management ========== /** * Import an invoice from file or buffer * Parses, validates, and optionally books the invoice */ async importInvoice(file, direction, options) { this.ensureInitialized(); if (!this.invoiceAdapter || !this.invoiceStorage || !this.invoiceBookingEngine) { throw new Error('Invoice components not initialized'); } this.logger.log('info', `Importing ${direction} invoice`); // Parse and validate invoice const invoice = await this.invoiceAdapter.parseInvoice(file, direction); // Store invoice await this.invoiceStorage.initialize(); const contentHash = await this.invoiceStorage.storeInvoice(invoice); invoice.contentHash = contentHash; // Auto-book if requested if (options?.autoBook) { const bookingResult = await this.bookInvoice(invoice, options.bookingRules, { autoBook: true, confidenceThreshold: options.confidenceThreshold || 80, skipValidation: options.validateOnly }); if (bookingResult.success && bookingResult.bookingInfo) { invoice.bookingInfo = bookingResult.bookingInfo; invoice.status = 'posted'; // Update stored metadata with booking information await this.invoiceStorage.updateMetadata(invoice.contentHash, { journalEntryId: bookingResult.bookingInfo.journalEntryId, transactionIds: bookingResult.bookingInfo.transactionIds }); } } this.logger.log('info', `Invoice imported successfully: ${invoice.invoiceNumber}`); return invoice; } /** * Book an invoice to the ledger */ async bookInvoice(invoice, bookingRules, options) { this.ensureInitialized(); if (!this.invoiceBookingEngine) { throw new Error('Invoice booking engine not initialized'); } this.logger.log('info', `Booking invoice ${invoice.invoiceNumber}`); const result = await this.invoiceBookingEngine.bookInvoice(invoice, bookingRules, options); if (result.success) { this.logger.log('info', `Invoice booked successfully with confidence ${result.confidence}%`); // Update stored metadata if invoice has a content hash if (invoice.contentHash && result.bookingInfo && this.invoiceStorage) { await this.invoiceStorage.updateMetadata(invoice.contentHash, { journalEntryId: result.bookingInfo.journalEntryId, transactionIds: result.bookingInfo.transactionIds }); } } else { this.logger.log('error', `Invoice booking failed: ${result.errors?.join(', ')}`); } return result; } /** * Export an invoice in a different format */ async exportInvoice(invoice, options) { this.ensureInitialized(); if (!this.invoiceAdapter) { throw new Error('Invoice adapter not initialized'); } this.logger.log('info', `Exporting invoice ${invoice.invoiceNumber} to ${options.format}`); // Convert format if needed const xml = await this.invoiceAdapter.convertFormat(invoice, options.format); // Generate PDF if requested let pdf; if (options.embedInPdf) { const result = await this.invoiceAdapter.generateInvoice(invoice, options.format); pdf = result.pdf; } return { xml, pdf }; } /** * Search invoices by filter */ async searchInvoices(filter) { this.ensureInitialized(); if (!this.invoiceStorage) { throw new Error('Invoice storage not initialized'); } await this.invoiceStorage.initialize(); const metadata = await this.invoiceStorage.searchInvoices(filter); const invoices = []; for (const meta of metadata) { const invoice = await this.invoiceStorage.retrieveInvoice(meta.contentHash); if (invoice) { invoices.push(invoice); } } return invoices; } /** * Get invoice by content hash */ async getInvoice(contentHash) { this.ensureInitialized(); if (!this.invoiceStorage) { throw new Error('Invoice storage not initialized'); } await this.invoiceStorage.initialize(); return await this.invoiceStorage.retrieveInvoice(contentHash); } /** * Get invoice storage statistics */ async getInvoiceStatistics() { this.ensureInitialized(); if (!this.invoiceStorage) { throw new Error('Invoice storage not initialized'); } await this.invoiceStorage.initialize(); return await this.invoiceStorage.getStatistics(); } /** * Create EN16931 compliance report for invoices */ async createInvoiceComplianceReport() { this.ensureInitialized(); if (!this.invoiceStorage) { throw new Error('Invoice storage not initialized'); } await this.invoiceStorage.initialize(); await this.invoiceStorage.createComplianceReport(); this.logger.log('info', 'Invoice compliance report created'); } /** * Generate an invoice from internal data */ async generateInvoice(invoiceData, format) { this.ensureInitialized(); if (!this.invoiceAdapter) { throw new Error('Invoice adapter not initialized'); } this.logger.log('info', `Generating invoice in ${format} format`); return await this.invoiceAdapter.generateInvoice(invoiceData, format); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLmFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3Nrci5hcGkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ25FLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNqRCxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDbkQsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ25ELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDN0QsT0FBTyxFQUFFLFNBQVMsRUFBdUIsTUFBTSxpQkFBaUIsQ0FBQztBQUNqRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDNUQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDNUQsT0FBTyxFQUFFLGtCQUFrQixFQUEwQixNQUFNLHFCQUFxQixDQUFDO0FBQ2pGLE9BQU8sRUFBRSxlQUFlLEVBQXdCLE1BQU0sbUJBQW1CLENBQUM7QUFDMUUsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQzFELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMxRCxPQUFPLEVBQUUsb0JBQW9CLEVBQTZDLE1BQU0sMEJBQTBCLENBQUM7QUF3QjNHOztHQUVHO0FBQ0gsTUFBTSxPQUFPLE1BQU07SUFXakIsWUFBb0IsTUFBdUI7UUFBdkIsV0FBTSxHQUFOLE1B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