import { parseLocalDate } from "@/lib/utils"; import { monthKeyOf } from "@/lib/format"; import type { Transaction } from "@/types/finance"; import { countsAsSpend } from "@/lib/transfers"; export interface CashflowPoint { key: string; label: string; income: number; expenses: number; } export function buildCashflow(transactions: Transaction[], months: number): CashflowPoint[] { const byMonth = new Map(); for (const t of transactions) { if (!countsAsSpend(t)) continue; const key = monthKeyOf(t.date); if (!key || !Number.isFinite(t.amount)) continue; if (!byMonth.has(key)) byMonth.set(key, { income: 0, expenses: 0 }); const bucket = byMonth.get(key)!; if (t.type === "income") bucket.income += Math.abs(t.amount); else bucket.expenses += Math.abs(t.amount); } return [...byMonth.entries()] .sort(([a], [b]) => a.localeCompare(b)) .slice(-months) .map(([key, v]) => { const d = parseLocalDate(`${key}-01`); return { key, // The year is included: a 6-month window crossing a year boundary used // to render two identical "Jan" ticks. label: d.toLocaleDateString("en-US", { month: "short", year: "2-digit" }), income: Math.round(v.income * 100) / 100, expenses: Math.round(v.expenses * 100) / 100, }; }); }