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

178 lines
5.8 KiB
JavaScript

const BaseFinancialRepository = require('./BaseFinancialRepository');
/**
* Analytics Repository
*
* Manages financial analytics and metrics.
*/
class AnalyticsRepository extends BaseFinancialRepository {
constructor() {
super('pg_fn_analytics');
}
async findBySiteId(siteId) {
return await this.findAll({ site_id: siteId }, { orderBy: 'metric_date', orderDirection: 'desc' });
}
async findByMetric(siteId, metricName) {
return await this.findAll({ site_id: siteId, metric_name: metricName }, {
orderBy: 'metric_date',
orderDirection: 'desc'
});
}
async createAnalytic(analyticData) {
return await this.create({
...analyticData,
created_at: new Date().toISOString()
});
}
/**
* Get comprehensive financial analytics
* @param {string} siteId - Site ID
* @param {Object} options - Date range options
* @returns {Promise<Object>} Analytics data
*/
async getFinancialAnalytics(siteId, options = {}) {
try {
const { revenueRepository, expenseRepository } = require('./index');
const { budgetRepository } = require('./index');
const { transactionRepository, transactionLineRepository } = require('./index');
const { chartOfAccountsRepository } = require('./index');
// Get date range
const startDate = options.start_date || new Date(new Date().getFullYear(), 0, 1).toISOString().split('T')[0];
const endDate = options.end_date || new Date().toISOString().split('T')[0];
// Calculate total revenue
const revenues = await revenueRepository.findAll({ site_id: siteId });
let totalRevenue = 0;
for (const rev of revenues) {
if (rev.revenue_date >= startDate && rev.revenue_date <= endDate) {
totalRevenue += parseFloat(rev.amount || 0);
}
}
// Calculate total expenses
const expenses = await expenseRepository.findAll({ site_id: siteId });
let totalExpenses = 0;
for (const exp of expenses) {
if (exp.expense_date >= startDate && exp.expense_date <= endDate) {
totalExpenses += parseFloat(exp.amount || 0);
}
}
// Calculate net income
const netIncome = totalRevenue - totalExpenses;
// Get budget for variance calculation
let budgetVariance = null;
const currentYear = new Date().getFullYear();
const budgets = await budgetRepository.findAll({ site_id: siteId, fiscal_year: currentYear });
if (budgets.length > 0) {
const budget = budgets[0];
const budgetedAmount = parseFloat(budget.total_amount || 0);
const actualAmount = totalExpenses;
budgetVariance = budgetedAmount > 0 ? ((actualAmount - budgetedAmount) / budgetedAmount * 100) : 0;
}
// Calculate cash flow (simplified - from transactions)
const transactions = await transactionRepository.findAll({ site_id: siteId });
let cashFlow = 0;
for (const trans of transactions) {
if (trans.transaction_date >= startDate && trans.transaction_date <= endDate) {
if (trans.transaction_type === 'income') {
cashFlow += parseFloat(trans.amount || 0);
} else if (trans.transaction_type === 'expense') {
cashFlow -= parseFloat(trans.amount || 0);
}
}
}
return {
site_id: siteId,
period: {
start_date: startDate,
end_date: endDate
},
total_revenue: totalRevenue,
total_expenses: totalExpenses,
net_income: netIncome,
budget_variance: budgetVariance,
cash_flow: cashFlow
};
} catch (error) {
throw new Error(`Failed to calculate financial analytics: ${error.message}`);
}
}
/**
* Get HOA-specific metrics
* @param {string} siteId - Site ID
* @returns {Promise<Object>} HOA metrics
*/
async getHOAMetrics(siteId) {
try {
const { unitBalanceRepository, invoiceRepository, paymentRepository } = require('./index');
// Get all unit balances
const unitBalances = await unitBalanceRepository.findAll({ site_id: siteId });
let totalOutstanding = 0;
let totalPaid = 0;
let unitCount = 0;
for (const unit of unitBalances) {
const balance = await unitBalanceRepository.calculateUnitBalance(unit.unit_id, siteId);
totalOutstanding += parseFloat(balance.current_balance || 0);
unitCount++;
}
// Get all invoices
const invoices = await invoiceRepository.findAll({ site_id: siteId });
let paidCount = 0;
let overdueCount = 0;
for (const invoice of invoices) {
const amount = parseFloat(invoice.amount || 0);
if (invoice.status === 'paid') {
totalPaid += amount;
paidCount++;
} else if (invoice.status === 'overdue') {
overdueCount++;
}
}
// Get recent payments
const payments = await paymentRepository.findAll({ site_id: siteId });
const recentPayments = payments
.filter(p => p.payment_date >= new Date(new Date().setMonth(new Date().getMonth() - 1)).toISOString().split('T')[0])
.reduce((sum, p) => sum + parseFloat(p.payment_amount || 0), 0);
return {
site_id: siteId,
unit_metrics: {
total_units: unitCount,
total_outstanding: totalOutstanding,
average_outstanding: unitCount > 0 ? totalOutstanding / unitCount : 0
},
invoice_metrics: {
total_invoices: invoices.length,
paid_invoices: paidCount,
overdue_invoices: overdueCount,
total_paid: totalPaid
},
recent_activity: {
payments_last_month: recentPayments
}
};
} catch (error) {
throw new Error(`Failed to calculate HOA metrics: ${error.message}`);
}
}
}
module.exports = AnalyticsRepository;