const BaseFinancialRepository = require('./BaseFinancialRepository'); /** * Report Repository * * Manages financial report generation and storage. */ class ReportRepository extends BaseFinancialRepository { constructor() { super('pg_fn_reports'); } getRepositories() { if (!this.transactionLineRepository) { const { transactionRepository, transactionLineRepository, chartOfAccountsRepository } = require('./index'); this.transactionRepository = transactionRepository; this.transactionLineRepository = transactionLineRepository; this.chartOfAccountsRepository = chartOfAccountsRepository; } } async findBySiteId(siteId) { return await this.findAll({ site_id: siteId }, { orderBy: 'generated_at', orderDirection: 'desc' }); } async findByType(siteId, reportType) { return await this.findAll({ site_id: siteId, report_type: reportType }, { orderBy: 'generated_at', orderDirection: 'desc' }); } async createReport(reportData) { return await this.create({ ...reportData, generated_at: new Date().toISOString(), status: reportData.status || 'generated' }); } /** * Generate Trial Balance Report * @param {string} siteId - Site ID * @param {Object} options - Date range * @returns {Promise} Trial balance report */ async generateTrialBalance(siteId, options = {}) { try { this.getRepositories(); const { generalLedgerRepository } = require('./index'); return await generalLedgerRepository.getTrialBalance(siteId, options); } catch (error) { throw new Error(`Failed to generate trial balance: ${error.message}`); } } /** * Generate Balance Sheet * @param {string} siteId - Site ID * @param {Object} options - Date range * @returns {Promise} Balance sheet report */ async generateBalanceSheet(siteId, options = {}) { try { this.getRepositories(); const accounts = await this.chartOfAccountsRepository.findAll({ site_id: siteId, is_active: true }); const { transactionRepository, transactionLineRepository } = require('./index'); const assets = []; const liabilities = []; const equity = []; for (const account of accounts) { const lines = await this.transactionLineRepository.findAll({ account_id: account.id }); let balance = 0; for (const line of lines) { const transaction = await transactionRepository.findById(line.transaction_id); if (transaction && transaction.site_id === siteId) { if (options.endDate && transaction.transaction_date <= options.endDate) { balance += parseFloat(line.debit_amount || 0) - parseFloat(line.credit_amount || 0); } else if (!options.endDate) { balance += parseFloat(line.debit_amount || 0) - parseFloat(line.credit_amount || 0); } } } const accountData = { account_code: account.account_code, account_name: account.account_name, balance: balance }; if (account.account_type === 'asset') { assets.push(accountData); } else if (account.account_type === 'liability') { liabilities.push(accountData); } else if (account.account_type === 'equity') { equity.push(accountData); } } const totalAssets = assets.reduce((sum, a) => sum + a.balance, 0); const totalLiabilities = liabilities.reduce((sum, l) => sum + l.balance, 0); const totalEquity = equity.reduce((sum, e) => sum + e.balance, 0); return { site_id: siteId, report_date: options.endDate || new Date().toISOString().split('T')[0], assets: { accounts: assets, total: totalAssets }, liabilities: { accounts: liabilities, total: totalLiabilities }, equity: { accounts: equity, total: totalEquity }, total_liabilities_and_equity: totalLiabilities + totalEquity, balanced: Math.abs(totalAssets - (totalLiabilities + totalEquity)) < 0.01 }; } catch (error) { throw new Error(`Failed to generate balance sheet: ${error.message}`); } } /** * Generate Income Statement * @param {string} siteId - Site ID * @param {Object} options - Date range * @returns {Promise} Income statement report */ async generateIncomeStatement(siteId, options = {}) { try { this.getRepositories(); const revenueAccounts = await this.chartOfAccountsRepository.findAll({ site_id: siteId, account_type: 'revenue', is_active: true }); const expenseAccounts = await this.chartOfAccountsRepository.findAll({ site_id: siteId, account_type: 'expense', is_active: true }); const { transactionRepository, transactionLineRepository } = require('./index'); const revenues = []; const expenses = []; // Calculate revenue for (const account of revenueAccounts) { const lines = await this.transactionLineRepository.findAll({ account_id: account.id }); let total = 0; for (const line of lines) { const transaction = await transactionRepository.findById(line.transaction_id); if (transaction && transaction.site_id === siteId) { const transactionDate = transaction.transaction_date; if (options.startDate && options.endDate) { if (transactionDate >= options.startDate && transactionDate <= options.endDate) { total += parseFloat(line.debit_amount || 0) - parseFloat(line.credit_amount || 0); } } else { total += parseFloat(line.debit_amount || 0) - parseFloat(line.credit_amount || 0); } } } if (total !== 0) { revenues.push({ account_code: account.account_code, account_name: account.account_name, amount: total }); } } // Calculate expenses for (const account of expenseAccounts) { const lines = await this.transactionLineRepository.findAll({ account_id: account.id }); let total = 0; for (const line of lines) { const transaction = await transactionRepository.findById(line.transaction_id); if (transaction && transaction.site_id === siteId) { const transactionDate = transaction.transaction_date; if (options.startDate && options.endDate) { if (transactionDate >= options.startDate && transactionDate <= options.endDate) { total += parseFloat(line.debit_amount || 0) - parseFloat(line.credit_amount || 0); } } else { total += parseFloat(line.debit_amount || 0) - parseFloat(line.credit_amount || 0); } } } if (total !== 0) { expenses.push({ account_code: account.account_code, account_name: account.account_name, amount: total }); } } const totalRevenue = revenues.reduce((sum, r) => sum + r.amount, 0); const totalExpenses = expenses.reduce((sum, e) => sum + e.amount, 0); const netIncome = totalRevenue - totalExpenses; return { site_id: siteId, start_date: options.startDate, end_date: options.endDate, revenues: { accounts: revenues, total: totalRevenue }, expenses: { accounts: expenses, total: totalExpenses }, net_income: netIncome }; } catch (error) { throw new Error(`Failed to generate income statement: ${error.message}`); } } } module.exports = ReportRepository;