plugin-financials/repositories/ReportRepository.js
2025-11-03 13:51:33 +02:00

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;