242 lines
7.9 KiB
JavaScript
242 lines
7.9 KiB
JavaScript
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<Object>} 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<Object>} 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<Object>} 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;
|
|
|
|
|