UNPKG

@fin.cx/skr

Version:

SKR03 and SKR04 German accounting standards for double-entry bookkeeping

377 lines 29 kB
import * as plugins from './plugins.js'; import { getDb, closeDb } from './skr.database.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, SKR03_ACCOUNT_CLASSES } from './skr03.data.js'; import { SKR04_ACCOUNTS, SKR04_ACCOUNT_CLASSES } from './skr04.data.js'; export class ChartOfAccounts { constructor(config) { this.config = config; this.initialized = false; this.skrType = null; this.logger = new plugins.smartlog.Smartlog({ logContext: { company: 'fin.cx', companyunit: 'skr', containerName: 'ChartOfAccounts', environment: 'local', runtime: 'node', zone: 'local', }, }); this.logger.enableConsole(); } /** * Initialize the database connection */ async init() { if (this.initialized) { this.logger.log('info', 'ChartOfAccounts already initialized'); return; } if (!this.config) { throw new Error('Database configuration required for initialization'); } await getDb(this.config); this.initialized = true; this.logger.log('info', 'ChartOfAccounts initialized successfully'); } /** * Initialize SKR03 chart of accounts */ async initializeSKR03() { await this.init(); this.logger.log('info', 'Initializing SKR03 chart of accounts'); // Check if SKR03 accounts already exist const existingAccounts = await Account.getInstances({ skrType: 'SKR03' }); if (existingAccounts.length > 0) { this.logger.log('info', `SKR03 already initialized with ${existingAccounts.length} accounts`); this.skrType = 'SKR03'; return; } // Create all SKR03 accounts const accounts = []; for (const accountData of SKR03_ACCOUNTS) { const account = await Account.createAccount(accountData); accounts.push(account); } this.skrType = 'SKR03'; this.logger.log('info', `Successfully initialized SKR03 with ${accounts.length} accounts`); } /** * Initialize SKR04 chart of accounts */ async initializeSKR04() { await this.init(); this.logger.log('info', 'Initializing SKR04 chart of accounts'); // Check if SKR04 accounts already exist const existingAccounts = await Account.getInstances({ skrType: 'SKR04' }); if (existingAccounts.length > 0) { this.logger.log('info', `SKR04 already initialized with ${existingAccounts.length} accounts`); this.skrType = 'SKR04'; return; } // Create all SKR04 accounts const accounts = []; for (const accountData of SKR04_ACCOUNTS) { const account = await Account.createAccount(accountData); accounts.push(account); } this.skrType = 'SKR04'; this.logger.log('info', `Successfully initialized SKR04 with ${accounts.length} accounts`); } /** * Get the current SKR type */ getSKRType() { return this.skrType; } /** * Set the active SKR type */ setSKRType(skrType) { this.skrType = skrType; } /** * Get account by number */ async getAccountByNumber(accountNumber) { if (!this.skrType) { throw new Error('SKR type not set. Initialize SKR03 or SKR04 first.'); } return await Account.getAccountByNumber(accountNumber, this.skrType); } /** * Get accounts by class */ async getAccountsByClass(accountClass) { if (!this.skrType) { throw new Error('SKR type not set. Initialize SKR03 or SKR04 first.'); } return await Account.getAccountsByClass(accountClass, this.skrType); } /** * Get accounts by type */ async getAccountsByType(accountType) { if (!this.skrType) { throw new Error('SKR type not set. Initialize SKR03 or SKR04 first.'); } return await Account.getAccountsByType(accountType, this.skrType); } /** * Create a custom account */ async createCustomAccount(accountData) { if (!this.skrType) { throw new Error('SKR type not set. Initialize SKR03 or SKR04 first.'); } // Ensure the account uses the current SKR type const fullAccountData = { accountNumber: accountData.accountNumber || '', accountName: accountData.accountName || '', accountClass: accountData.accountClass || 0, accountType: accountData.accountType || 'asset', skrType: this.skrType, description: accountData.description, vatRate: accountData.vatRate, isActive: accountData.isActive !== undefined ? accountData.isActive : true, }; // Validate account number doesn't already exist const existing = await this.getAccountByNumber(fullAccountData.accountNumber); if (existing) { throw new Error(`Account ${fullAccountData.accountNumber} already exists`); } return await Account.createAccount(fullAccountData); } /** * Update an existing account */ async updateAccount(accountNumber, updates) { const account = await this.getAccountByNumber(accountNumber); if (!account) { throw new Error(`Account ${accountNumber} not found`); } // Apply updates if (updates.accountName !== undefined) account.accountName = updates.accountName; if (updates.description !== undefined) account.description = updates.description; if (updates.vatRate !== undefined) account.vatRate = updates.vatRate; if (updates.isActive !== undefined) account.isActive = updates.isActive; account.updatedAt = new Date(); await account.save(); return account; } /** * Delete a custom account (only non-system accounts) */ async deleteAccount(accountNumber) { const account = await this.getAccountByNumber(accountNumber); if (!account) { throw new Error(`Account ${accountNumber} not found`); } if (account.isSystemAccount) { throw new Error(`Cannot delete system account ${accountNumber}`); } // Check if account has transactions const transactions = await Transaction.getTransactionsByAccount(accountNumber, account.skrType); if (transactions.length > 0) { throw new Error(`Cannot delete account ${accountNumber} with existing transactions`); } await account.delete(); } /** * Search accounts */ async searchAccounts(searchTerm) { return await Account.searchAccounts(searchTerm, this.skrType); } /** * Get all accounts */ async getAllAccounts(filter) { const query = {}; if (this.skrType) { query.skrType = this.skrType; } if (filter) { if (filter.accountClass !== undefined) query.accountClass = filter.accountClass; if (filter.accountType !== undefined) query.accountType = filter.accountType; if (filter.isActive !== undefined) query.isActive = filter.isActive; } const accounts = await Account.getInstances(query); // Apply text search if provided if (filter?.searchTerm) { const lowerSearchTerm = filter.searchTerm.toLowerCase(); return accounts.filter((account) => account.accountNumber.includes(filter.searchTerm) || account.accountName.toLowerCase().includes(lowerSearchTerm) || account.description.toLowerCase().includes(lowerSearchTerm)); } return accounts; } /** * Post a simple transaction */ async postTransaction(transactionData) { if (!this.skrType) { throw new Error('SKR type not set. Initialize SKR03 or SKR04 first.'); } // Ensure the transaction uses the current SKR type const fullTransactionData = { ...transactionData, skrType: this.skrType, }; return await Transaction.createTransaction(fullTransactionData); } /** * Post a journal entry */ async postJournalEntry(journalData) { if (!this.skrType) { throw new Error('SKR type not set. Initialize SKR03 or SKR04 first.'); } // Ensure the journal entry uses the current SKR type const fullJournalData = { ...journalData, skrType: this.skrType, }; const journalEntry = await JournalEntry.createJournalEntry(fullJournalData); await journalEntry.post(); return journalEntry; } /** * Get transactions for an account */ async getAccountTransactions(accountNumber) { if (!this.skrType) { throw new Error('SKR type not set. Initialize SKR03 or SKR04 first.'); } return await Transaction.getTransactionsByAccount(accountNumber, this.skrType); } /** * Get transactions by filter */ async getTransactions(filter) { if (!this.skrType) { throw new Error('SKR type not set. Initialize SKR03 or SKR04 first.'); } const query = { skrType: this.skrType, status: 'posted', }; if (filter) { if (filter.dateFrom || filter.dateTo) { query.date = {}; if (filter.dateFrom) query.date.$gte = filter.dateFrom; if (filter.dateTo) query.date.$lte = filter.dateTo; } if (filter.accountNumber) { query.$or = [ { debitAccount: filter.accountNumber }, { creditAccount: filter.accountNumber }, ]; } if (filter.minAmount || filter.maxAmount) { query.amount = {}; if (filter.minAmount) query.amount.$gte = filter.minAmount; if (filter.maxAmount) query.amount.$lte = filter.maxAmount; } } const transactions = await Transaction.getInstances(query); // Apply text search if provided if (filter?.searchTerm) { const lowerSearchTerm = filter.searchTerm.toLowerCase(); return transactions.filter((transaction) => transaction.description.toLowerCase().includes(lowerSearchTerm) || transaction.reference.toLowerCase().includes(lowerSearchTerm)); } return transactions; } /** * Reverse a transaction */ async reverseTransaction(transactionId) { const transaction = await Transaction.getTransactionById(transactionId); if (!transaction) { throw new Error(`Transaction ${transactionId} not found`); } return await transaction.reverseTransaction(); } /** * Get account class description */ getAccountClassDescription(accountClass) { if (!this.skrType) { throw new Error('SKR type not set. Initialize SKR03 or SKR04 first.'); } const classes = this.skrType === 'SKR03' ? SKR03_ACCOUNT_CLASSES : SKR04_ACCOUNT_CLASSES; return (classes[accountClass] || `Class ${accountClass}`); } /** * Import accounts from CSV */ async importAccountsFromCSV(csvContent) { if (!this.skrType) { throw new Error('SKR type not set. Initialize SKR03 or SKR04 first.'); } const lines = csvContent.split('\n').filter((line) => line.trim()); let importedCount = 0; for (const line of lines) { // Parse CSV line (expecting format: "account";"name";"description";"type";"active") const parts = line .split(';') .map((part) => part.replace(/"/g, '').trim()); if (parts.length >= 5) { const accountData = { accountNumber: parts[0], accountName: parts[1], accountClass: parseInt(parts[0][0]), accountType: parts[3], skrType: this.skrType, description: parts[2], isActive: parts[4].toLowerCase() === 'standard' || parts[4].toLowerCase() === 'active', }; try { await this.createCustomAccount(accountData); importedCount++; } catch (error) { this.logger.log('warn', `Failed to import account ${parts[0]}: ${error.message}`); } } } return importedCount; } /** * Export accounts to CSV */ async exportAccountsToCSV() { const accounts = await this.getAllAccounts(); const csvLines = []; csvLines.push('"Account";"Name";"Description";"Type";"Active"'); for (const account of accounts) { csvLines.push(`"${account.accountNumber}";"${account.accountName}";"${account.description}";"${account.accountType}";"${account.isActive ? 'Active' : 'Inactive'}"`); } return csvLines.join('\n'); } /** * Close the database connection */ async close() { await closeDb(); this.initialized = false; this.logger.log('info', 'ChartOfAccounts closed'); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2tyLmNsYXNzZXMuY2hhcnRvZmFjY291bnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc2tyLmNsYXNzZXMuY2hhcnRvZmFjY291bnRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDbkQsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ25ELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDN0QsT0FBTyxFQUFFLGNBQWMsRUFBRSxxQkFBcUIsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxjQUFjLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQVd4RSxNQUFNLE9BQU8sZUFBZTtJQUsxQixZQUFvQixNQUF3QjtRQUF4QixXQUFNLEdBQU4sTUFBTSxDQUFrQjtRQUhwQyxnQkFBVyxHQUFZLEtBQUssQ0FBQztRQUM3QixZQUFPLEdBQW9CLElBQUksQ0FBQztRQUd0QyxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUM7WUFDMUMsVUFBVSxFQUFFO2dCQUNWLE9BQU8sRUFBRSxRQUFRO2dCQUNqQixXQUFXLEVBQUUsS0FBSztnQkFDbEIsYUFBYSxFQUFFLGlCQUFpQjtnQkFDaEMsV0FBVyxFQUFFLE9BQU87Z0JBQ3BCLE9BQU8sRUFBRSxNQUFNO2dCQUNmLElBQUksRUFBRSxPQUFPO2FBQ2Q7U0FDRixDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsTUFBTSxDQUFDLGFBQWEsRUFBRSxDQUFDO0lBQzlCLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxJQUFJ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