import * as plugins from './plugins.js'; 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 type { IDatabaseConfig, TSKRType, IAccountData, IAccountFilter, ITransactionData, ITransactionFilter, IJournalEntry, IReportParams, ITrialBalanceReport, IIncomeStatement, IBalanceSheet, } from './skr.types.js'; /** * Main API class for SKR accounting operations */ export class SkrApi { private chartOfAccounts: ChartOfAccounts; private ledger: Ledger | null = null; private reports: Reports | null = null; private logger: plugins.smartlog.Smartlog; private initialized: boolean = false; private currentSKRType: TSKRType | null = null; constructor(private config: IDatabaseConfig) { 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 */ public async initialize(skrType: TSKRType): Promise { 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); this.initialized = true; this.logger.log('info', 'SKR API initialized successfully'); } /** * Ensure API is initialized */ private ensureInitialized(): void { if (!this.initialized || !this.currentSKRType) { throw new Error('API not initialized. Call initialize() first.'); } } // ========== Account Management ========== /** * Create a new account */ public async createAccount( accountData: Partial, ): Promise { this.ensureInitialized(); return await this.chartOfAccounts.createCustomAccount(accountData); } /** * Get account by number */ public async getAccount(accountNumber: string): Promise { this.ensureInitialized(); return await this.chartOfAccounts.getAccountByNumber(accountNumber); } /** * Update an account */ public async updateAccount( accountNumber: string, updates: Partial, ): Promise { this.ensureInitialized(); return await this.chartOfAccounts.updateAccount(accountNumber, updates); } /** * Delete an account */ public async deleteAccount(accountNumber: string): Promise { this.ensureInitialized(); await this.chartOfAccounts.deleteAccount(accountNumber); } /** * List accounts with optional filter */ public async listAccounts(filter?: IAccountFilter): Promise { this.ensureInitialized(); return await this.chartOfAccounts.getAllAccounts(filter); } /** * Search accounts by term */ public async searchAccounts(searchTerm: string): Promise { this.ensureInitialized(); return await this.chartOfAccounts.searchAccounts(searchTerm); } /** * Get accounts by class */ public async getAccountsByClass(accountClass: number): Promise { this.ensureInitialized(); return await this.chartOfAccounts.getAccountsByClass(accountClass); } /** * Get accounts by type */ public async getAccountsByType( accountType: IAccountData['accountType'], ): Promise { this.ensureInitialized(); return await this.chartOfAccounts.getAccountsByType(accountType); } // ========== Transaction Management ========== /** * Post a simple transaction */ public async postTransaction( transactionData: ITransactionData, ): Promise { this.ensureInitialized(); return await this.chartOfAccounts.postTransaction(transactionData); } /** * Post a journal entry */ public async postJournalEntry( journalData: IJournalEntry, ): Promise { this.ensureInitialized(); return await this.chartOfAccounts.postJournalEntry(journalData); } /** * Get transaction by ID */ public async getTransaction( transactionId: string, ): Promise { this.ensureInitialized(); return await Transaction.getTransactionById(transactionId); } /** * List transactions with optional filter */ public async listTransactions( filter?: ITransactionFilter, ): Promise { this.ensureInitialized(); return await this.chartOfAccounts.getTransactions(filter); } /** * Get transactions for specific account */ public async getAccountTransactions( accountNumber: string, ): Promise { this.ensureInitialized(); return await this.chartOfAccounts.getAccountTransactions(accountNumber); } /** * Reverse a transaction */ public async reverseTransaction(transactionId: string): Promise { this.ensureInitialized(); return await this.chartOfAccounts.reverseTransaction(transactionId); } /** * Reverse a journal entry */ public async reverseJournalEntry(journalId: string): Promise { this.ensureInitialized(); if (!this.ledger) throw new Error('Ledger not initialized'); return await this.ledger.reverseJournalEntry(journalId); } // ========== Reporting ========== /** * Generate trial balance */ public async generateTrialBalance( params?: IReportParams, ): Promise { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.getTrialBalance(params); } /** * Generate income statement */ public async generateIncomeStatement( params?: IReportParams, ): Promise { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.getIncomeStatement(params); } /** * Generate balance sheet */ public async generateBalanceSheet( params?: IReportParams, ): Promise { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.getBalanceSheet(params); } /** * Generate general ledger */ public async generateGeneralLedger(params?: IReportParams): Promise { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.getGeneralLedger(params); } /** * Generate cash flow statement */ public async generateCashFlowStatement(params?: IReportParams): Promise { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.getCashFlowStatement(params); } /** * Export report to CSV */ public async exportReportToCSV( reportType: 'trial_balance' | 'income_statement' | 'balance_sheet', params?: IReportParams, ): Promise { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.exportToCSV(reportType, params); } /** * Export to DATEV format */ public async exportToDATEV(params?: IReportParams): Promise { this.ensureInitialized(); if (!this.reports) throw new Error('Reports not initialized'); return await this.reports.exportToDATEV(params); } // ========== Period Management ========== /** * Close accounting period */ public async closePeriod( period: string, closingAccountNumber?: string, ): Promise { this.ensureInitialized(); if (!this.ledger) throw new Error('Ledger not initialized'); return await this.ledger.closeAccountingPeriod( period, closingAccountNumber, ); } /** * Get account balance */ public async getAccountBalance( accountNumber: string, asOfDate?: Date, ): Promise { this.ensureInitialized(); if (!this.ledger) throw new Error('Ledger not initialized'); return await this.ledger.getAccountBalance(accountNumber, asOfDate); } /** * Recalculate all account balances */ public async recalculateBalances(): Promise { this.ensureInitialized(); if (!this.ledger) throw new Error('Ledger not initialized'); await this.ledger.recalculateAllBalances(); } // ========== Import/Export ========== /** * Import accounts from CSV */ public async importAccountsFromCSV(csvContent: string): Promise { this.ensureInitialized(); return await this.chartOfAccounts.importAccountsFromCSV(csvContent); } /** * Export accounts to CSV */ public async exportAccountsToCSV(): Promise { this.ensureInitialized(); return await this.chartOfAccounts.exportAccountsToCSV(); } // ========== Utility Methods ========== /** * Get current SKR type */ public getSKRType(): TSKRType | null { return this.currentSKRType; } /** * Get account class description */ public getAccountClassDescription(accountClass: number): string { this.ensureInitialized(); return this.chartOfAccounts.getAccountClassDescription(accountClass); } /** * Validate double-entry rules */ public validateDoubleEntry( debitAmount: number, creditAmount: number, ): boolean { if (!this.ledger) throw new Error('Ledger not initialized'); return this.ledger.validateDoubleEntry(debitAmount, creditAmount); } /** * Get unbalanced transactions (for audit) */ public async getUnbalancedTransactions(): Promise { this.ensureInitialized(); if (!this.ledger) throw new Error('Ledger not initialized'); return await this.ledger.getUnbalancedTransactions(); } /** * Close the API and database connection */ public async close(): Promise { 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 */ public async postBatchTransactions( transactions: ITransactionData[], ): Promise { this.ensureInitialized(); const results: Transaction[] = []; const errors: Array<{ index: number; error: string }> = []; 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 */ public async createBatchAccounts( accounts: IAccountData[], ): Promise { this.ensureInitialized(); const results: Account[] = []; const errors: Array<{ index: number; error: string }> = []; 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 */ public async getAccountsPaginated( page: number = 1, pageSize: number = 50, filter?: IAccountFilter, ): Promise<{ data: Account[]; total: number; page: number; pageSize: number; totalPages: number; }> { 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 */ public async getTransactionsPaginated( page: number = 1, pageSize: number = 50, filter?: ITransactionFilter, ): Promise<{ data: Transaction[]; total: number; page: number; pageSize: number; totalPages: number; }> { 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, }; } }