phase-7: code restructure

This commit is contained in:
Sai Kiran Vella
2026-06-05 22:05:55 -04:00
parent c1b3b26caa
commit 5185f03c12
108 changed files with 8931 additions and 3434 deletions
@@ -0,0 +1,53 @@
import type { CategoryBreakdown, FinanceAnalysis, SimpleFINAccount } from '../types';
export class PersonalFinanceAnalyzer {
analyze(accounts: SimpleFINAccount[]): FinanceAnalysis {
const assets = accounts.filter((a) => !['CREDIT', 'LOAN'].includes(a.type));
const liabilities = accounts.filter((a) => ['CREDIT', 'LOAN'].includes(a.type));
const totalAssets = assets.reduce((s, a) => s + Math.max(0, a.balance), 0);
const totalLiabilities = liabilities.reduce((s, a) => s + Math.abs(Math.min(0, a.balance)), 0);
const netWorth = totalAssets - totalLiabilities;
const cash = accounts.filter((a) => ['CHECKING', 'SAVINGS'].includes(a.type));
const investments = accounts.filter((a) => a.type === 'INVESTMENT');
const totalCash = cash.reduce((s, a) => s + Math.max(0, a.balance), 0);
const totalInvest = investments.reduce((s, a) => s + Math.max(0, a.balance), 0);
const allTx = accounts.flatMap((a) => a.transactions);
const spending = allTx.filter((tx) => tx.amount < 0 && tx.category !== 'Transfer');
const income = allTx.filter((tx) => tx.amount > 0 && tx.category === 'Income');
const totalSpend = spending.reduce((s, tx) => s + Math.abs(tx.amount), 0);
const totalIncome = income.reduce((s, tx) => s + tx.amount, 0);
const byCategory: Record<string, number> = {};
for (const tx of spending) {
byCategory[tx.category] = (byCategory[tx.category] ?? 0) + Math.abs(tx.amount);
}
const categoryBreakdown: CategoryBreakdown[] = Object.entries(byCategory)
.sort((a, b) => b[1] - a[1])
.map(([category, amount]) => ({
category,
amount,
pct: totalSpend > 0 ? ((amount / totalSpend) * 100).toFixed(1) : '0',
}));
return {
netWorth,
totalAssets,
totalLiabilities,
totalCash,
totalInvestments: totalInvest,
cashPct: totalAssets > 0 ? ((totalCash / totalAssets) * 100).toFixed(1) : '0',
investPct: totalAssets > 0 ? ((totalInvest / totalAssets) * 100).toFixed(1) : '0',
totalIncome,
totalSpend,
savingsRate:
totalIncome > 0 ? (((totalIncome - totalSpend) / totalIncome) * 100).toFixed(1) : null,
categoryBreakdown,
accounts,
};
}
}