UNPKG

@fin.cx/skr

Version:

SKR03 and SKR04 German accounting standards for double-entry bookkeeping

434 lines 35.6 kB
import * as plugins from './plugins.js'; import { Account } from './skr.classes.account.js'; import { Transaction } from './skr.classes.transaction.js'; import { JournalEntry } from './skr.classes.journalentry.js'; import { SKR03_ACCOUNTS } from './skr03.data.js'; import { SKR04_ACCOUNTS } from './skr04.data.js'; // Module-level Maps for O(1) SKR standard lookups const STANDARD_SKR_MAP = { SKR03: new Map(SKR03_ACCOUNTS.map(a => [a.accountNumber, a])), SKR04: new Map(SKR04_ACCOUNTS.map(a => [a.accountNumber, a])), }; export class Ledger { constructor(skrType) { this.skrType = skrType; this.logger = new plugins.smartlog.Smartlog({ logContext: { company: 'fin.cx', companyunit: 'skr', containerName: 'Ledger', environment: 'local', runtime: 'node', zone: 'local', }, }); } /** * Post a transaction with validation */ async postTransaction(transactionData) { this.logger.log('info', `Posting transaction: ${transactionData.description}`); // Ensure SKR type matches const fullTransactionData = { ...transactionData, skrType: this.skrType, }; // Validate accounts exist await this.validateAccounts([ transactionData.debitAccount, transactionData.creditAccount, ]); // Create and post transaction const transaction = await Transaction.createTransaction(fullTransactionData); this.logger.log('info', `Transaction ${transaction.transactionNumber} posted successfully`); return transaction; } /** * Post a journal entry with validation */ async postJournalEntry(journalData) { this.logger.log('info', `Posting journal entry: ${journalData.description}`); // Ensure SKR type matches const fullJournalData = { ...journalData, skrType: this.skrType, }; // Validate all accounts exist const accountNumbers = journalData.lines.map((line) => line.accountNumber); await this.validateAccounts(accountNumbers); // Validate against SKR standard (warnings only by default) await this.validateAccountsAgainstSKR(journalData.lines, { strict: false, // Start with warnings only warnOnNameMismatch: false // Names vary, don't spam logs }); // Validate journal entry is balanced this.validateJournalBalance(journalData.lines); // Create and post journal entry const journalEntry = await JournalEntry.createJournalEntry(fullJournalData); await journalEntry.post(); this.logger.log('info', `Journal entry ${journalEntry.journalNumber} posted successfully`); return journalEntry; } /** * Validate that accounts exist and are active */ async validateAccounts(accountNumbers) { const uniqueAccountNumbers = [...new Set(accountNumbers)]; for (const accountNumber of uniqueAccountNumbers) { const account = await Account.getAccountByNumber(accountNumber, this.skrType); if (!account) { throw new Error(`Account ${accountNumber} not found for ${this.skrType}`); } if (!account.isActive) { throw new Error(`Account ${accountNumber} is not active`); } } } /** * Validate journal entry balance */ validateJournalBalance(lines) { let totalDebits = 0; let totalCredits = 0; for (const line of lines) { if (line.debit) totalDebits += line.debit; if (line.credit) totalCredits += line.credit; } const difference = Math.abs(totalDebits - totalCredits); if (difference >= 0.01) { throw new Error(`Journal entry is not balanced. Debits: ${totalDebits}, Credits: ${totalCredits}`); } } /** * Validate accounts against SKR standard data */ async validateAccountsAgainstSKR(lines, options) { const { strict = false, warnOnNameMismatch = false } = options || {}; const skrMap = STANDARD_SKR_MAP[this.skrType]; if (!skrMap) { this.logger.log('warn', `No SKR standard map available for ${this.skrType}`); return; } const uniqueAccountNumbers = [...new Set(lines.map(line => line.accountNumber))]; for (const accountNumber of uniqueAccountNumbers) { const standardAccount = skrMap.get(accountNumber); if (!standardAccount) { // Special case: SKR04 class 8 is designated for custom accounts ("frei") if (this.skrType === 'SKR04' && accountNumber.startsWith('8')) { this.logger.log('debug', `Account ${accountNumber} is in SKR04 class 8 (custom accounts allowed)`); continue; } const message = `Account ${accountNumber} is not a standard ${this.skrType} account`; if (strict) { throw new Error(message); } else { this.logger.log('warn', message); } continue; } // Get actual account from database to compare const dbAccount = await Account.getAccountByNumber(accountNumber, this.skrType); if (!dbAccount) { // Account doesn't exist in DB, will be caught by validateAccounts() continue; } // Validate type and class match SKR standard if (dbAccount.accountType !== standardAccount.accountType) { const message = `Account ${accountNumber} type mismatch: expected '${standardAccount.accountType}', got '${dbAccount.accountType}'`; if (strict) { throw new Error(message); } else { this.logger.log('warn', message); } } if (dbAccount.accountClass !== standardAccount.accountClass) { const message = `Account ${accountNumber} class mismatch: expected ${standardAccount.accountClass}, got ${dbAccount.accountClass}`; if (strict) { throw new Error(message); } else { this.logger.log('warn', message); } } // Warn on name mismatch (common and acceptable in practice) if (warnOnNameMismatch && dbAccount.accountName !== standardAccount.accountName) { this.logger.log('info', `Account ${accountNumber} name differs from SKR standard: '${dbAccount.accountName}' vs '${standardAccount.accountName}'`); } } } /** * Reverse a transaction */ async reverseTransaction(transactionId) { this.logger.log('info', `Reversing transaction: ${transactionId}`); const transaction = await Transaction.getTransactionById(transactionId); if (!transaction) { throw new Error(`Transaction ${transactionId} not found`); } if (transaction.skrType !== this.skrType) { throw new Error(`Transaction ${transactionId} belongs to different SKR type`); } const reversalTransaction = await transaction.reverseTransaction(); this.logger.log('info', `Transaction reversed: ${reversalTransaction.transactionNumber}`); return reversalTransaction; } /** * Reverse a journal entry */ async reverseJournalEntry(journalId) { this.logger.log('info', `Reversing journal entry: ${journalId}`); const journalEntry = await JournalEntry.getInstance({ id: journalId }); if (!journalEntry) { throw new Error(`Journal entry ${journalId} not found`); } if (journalEntry.skrType !== this.skrType) { throw new Error(`Journal entry ${journalId} belongs to different SKR type`); } const reversalEntry = await journalEntry.reverse(); this.logger.log('info', `Journal entry reversed: ${reversalEntry.journalNumber}`); return reversalEntry; } /** * Get account history (all transactions for an account) */ async getAccountHistory(accountNumber, dateFrom, dateTo) { const account = await Account.getAccountByNumber(accountNumber, this.skrType); if (!account) { throw new Error(`Account ${accountNumber} not found`); } let transactions = await Transaction.getTransactionsByAccount(accountNumber, this.skrType); // Apply date filter if provided if (dateFrom || dateTo) { transactions = transactions.filter((transaction) => { if (dateFrom && transaction.date < dateFrom) return false; if (dateTo && transaction.date > dateTo) return false; return true; }); } // Sort by date transactions.sort((a, b) => a.date.getTime() - b.date.getTime()); return transactions; } /** * Get account balance at a specific date */ async getAccountBalance(accountNumber, asOfDate) { const account = await Account.getAccountByNumber(accountNumber, this.skrType); if (!account) { throw new Error(`Account ${accountNumber} not found`); } let transactions = await Transaction.getTransactionsByAccount(accountNumber, this.skrType); // Filter transactions up to the specified date if (asOfDate) { transactions = transactions.filter((t) => t.date <= asOfDate); } // Calculate balance let debitTotal = 0; let creditTotal = 0; for (const transaction of transactions) { if (transaction.debitAccount === accountNumber) { debitTotal += transaction.amount; } if (transaction.creditAccount === accountNumber) { creditTotal += transaction.amount; } } // Calculate net balance based on account type let balance; switch (account.accountType) { case 'asset': case 'expense': // Normal debit accounts balance = debitTotal - creditTotal; break; case 'liability': case 'equity': case 'revenue': // Normal credit accounts balance = creditTotal - debitTotal; break; } return { accountNumber, debitTotal, creditTotal, balance, lastUpdated: new Date(), }; } /** * Close accounting period (create closing entries) */ async closeAccountingPeriod(period, // Format: YYYY-MM closingAccountNumber = '9400') { this.logger.log('info', `Closing accounting period: ${period}`); const closingEntries = []; // Get all revenue and expense accounts const revenueAccounts = await Account.getAccountsByType('revenue', this.skrType); const expenseAccounts = await Account.getAccountsByType('expense', this.skrType); // Calculate totals for each account in the period const periodTransactions = await Transaction.getTransactionsByPeriod(period, this.skrType); // Create closing entry for revenue accounts const revenueLines = []; let totalRevenue = 0; for (const account of revenueAccounts) { const balance = await this.getAccountBalanceForPeriod(account.accountNumber, periodTransactions); if (balance !== 0) { // Revenue accounts have credit balance, so debit to close revenueLines.push({ accountNumber: account.accountNumber, debit: Math.abs(balance), description: `Closing ${account.accountName}`, }); totalRevenue += Math.abs(balance); } } if (totalRevenue > 0) { // Credit the closing account revenueLines.push({ accountNumber: closingAccountNumber, credit: totalRevenue, description: 'Revenue closing to P&L', }); const revenueClosingEntry = await this.postJournalEntry({ date: new Date(), description: `Closing revenue accounts for period ${period}`, reference: `CLOSE-REV-${period}`, lines: revenueLines, skrType: this.skrType, }); closingEntries.push(revenueClosingEntry); } // Create closing entry for expense accounts const expenseLines = []; let totalExpense = 0; for (const account of expenseAccounts) { const balance = await this.getAccountBalanceForPeriod(account.accountNumber, periodTransactions); if (balance !== 0) { // Expense accounts have debit balance, so credit to close expenseLines.push({ accountNumber: account.accountNumber, credit: Math.abs(balance), description: `Closing ${account.accountName}`, }); totalExpense += Math.abs(balance); } } if (totalExpense > 0) { // Debit the closing account expenseLines.push({ accountNumber: closingAccountNumber, debit: totalExpense, description: 'Expense closing to P&L', }); const expenseClosingEntry = await this.postJournalEntry({ date: new Date(), description: `Closing expense accounts for period ${period}`, reference: `CLOSE-EXP-${period}`, lines: expenseLines, skrType: this.skrType, }); closingEntries.push(expenseClosingEntry); } this.logger.log('info', `Period ${period} closed with ${closingEntries.length} entries`); return closingEntries; } /** * Calculate account balance for a specific set of transactions */ async getAccountBalanceForPeriod(accountNumber, transactions) { const account = await Account.getAccountByNumber(accountNumber, this.skrType); if (!account) return 0; let debitTotal = 0; let creditTotal = 0; for (const transaction of transactions) { if (transaction.debitAccount === accountNumber) { debitTotal += transaction.amount; } if (transaction.creditAccount === accountNumber) { creditTotal += transaction.amount; } } // Calculate net balance based on account type switch (account.accountType) { case 'asset': case 'expense': return debitTotal - creditTotal; case 'liability': case 'equity': case 'revenue': return creditTotal - debitTotal; } } /** * Validate double-entry rules */ validateDoubleEntry(debitAmount, creditAmount) { return Math.abs(debitAmount - creditAmount) < 0.01; } /** * Get unbalanced transactions (for audit) */ async getUnbalancedTransactions() { // In a proper double-entry system, all posted transactions should be balanced // This method is mainly for audit purposes const allTransactions = await Transaction.getInstances({ skrType: this.skrType, status: 'posted', }); // Group transactions by journal entry if they have one const unbalanced = []; // Since our system ensures balance at posting time, // this should typically return an empty array // But we include it for completeness and audit purposes return unbalanced; } /** * Recalculate all account balances */ async recalculateAllBalances() { this.logger.log('info', 'Recalculating all account balances'); // Get all accounts const accounts = await Account.getInstances({ skrType: this.skrType }); for (const account of accounts) { // Reset balances account.debitTotal = 0; account.creditTotal = 0; account.balance = 0; // Get all transactions for this account const transactions = await Transaction.getTransactionsByAccount(account.accountNumber, this.skrType); // Recalculate totals for (const transaction of transactions) { if (transaction.debitAccount === account.accountNumber) { account.debitTotal += transaction.amount; } if (transaction.creditAccount === account.accountNumber) { account.creditTotal += transaction.amount; } } // Calculate balance based on account type switch (account.accountType) { case 'asset': case 'expense': account.balance = account.debitTotal - account.creditTotal; break; case 'liability': case 'equity': case 'revenue': account.balance = account.creditTotal - account.debitTotal; break; } account.updatedAt = new Date(); await account.save(); } this.logger.log('info', `Recalculated balances for ${accounts.length} accounts`); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLmNsYXNzZXMubGVkZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc2tyLmNsYXNzZXMubGVkZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDM0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBUTdELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUNqRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFakQsa0RBQWtEO0FBQ2xELE1BQU0sZ0JBQWdCLEdBQUc7SUFDdkIsS0FBSyxFQUFFLElBQUksR0FBRyxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxhQUFhLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM3RCxLQUFLLEVBQUUsSUFBSSxHQUFHLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLGFBQWEsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDO0NBQzlELENBQUM7QUFFRixNQUFNLE9BQU8sTUFBTTtJQUdqQixZQUFvQixPQUFpQjtRQUFqQixZQUFPLEdBQVAsT0FBTyxDQUFVO1FBQ25DLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQztZQUMxQyxVQUFVLEVBQUU7Z0JBQ1YsT0FBTyxFQUFFLFFBQVE7Z0JBQ2pCLFdBQVcsRUFBRSxLQUFLO2dCQUNsQixhQUFhLEVBQUUsUUFBUTtnQkFDdkIsV0FBVyxFQUFFLE9BQU87Z0JBQ3BCLE9BQU8sRUFBRSxN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