87 lines
2.5 KiB
TypeScript
87 lines
2.5 KiB
TypeScript
import type { Asset, MonthlyIncome, MonthlyRunningCost } from "./data";
|
|
import { computeProfit, type ProfitOptions } from "./profit";
|
|
|
|
export type IncomeRow = {
|
|
id: string;
|
|
code: string;
|
|
name: string;
|
|
months: number[];
|
|
profit: number[];
|
|
income: number;
|
|
fixed: number;
|
|
running: number;
|
|
ytdProfit: number;
|
|
};
|
|
|
|
export function buildIncomeRows(
|
|
assets: Asset[],
|
|
income: MonthlyIncome[],
|
|
running?: MonthlyRunningCost[],
|
|
profitOpts: ProfitOptions = {}
|
|
): IncomeRow[] {
|
|
const byAsset = new Map<string, IncomeRow>();
|
|
for (const a of assets) {
|
|
if (!a.code) continue;
|
|
byAsset.set(a.id, {
|
|
id: a.id, code: a.code, name: a.name,
|
|
months: Array(12).fill(0),
|
|
profit: Array(12).fill(0),
|
|
income: 0, fixed: 0, running: 0, ytdProfit: 0,
|
|
});
|
|
}
|
|
for (const r of income) {
|
|
const row = byAsset.get(r.asset);
|
|
if (!row) continue;
|
|
const i = r.month - 1;
|
|
row.months[i] = r.income;
|
|
row.profit[i] = computeProfit({ income: r.income, fixed: r.fixed_expenses, running: 0 }, profitOpts);
|
|
row.income += r.income;
|
|
row.fixed += r.fixed_expenses;
|
|
}
|
|
// Per Part 1.5: running cost is derived from monthly_running_costs, not stored on income.
|
|
for (const c of running ?? []) {
|
|
const row = byAsset.get(c.asset);
|
|
if (!row) continue;
|
|
row.running += c.amount;
|
|
// Re-compute per-month profit so it includes running cost regardless of includeRunningCost flag.
|
|
// When includeRunningCost is false (default), running is shown but not subtracted.
|
|
row.profit[c.month - 1] = computeProfit(
|
|
{ income: row.months[c.month - 1], fixed: row.fixed / 12, running: c.amount },
|
|
profitOpts
|
|
);
|
|
}
|
|
for (const row of byAsset.values()) {
|
|
row.ytdProfit = computeProfit({ income: row.income, fixed: row.fixed, running: row.running }, profitOpts);
|
|
}
|
|
return Array.from(byAsset.values());
|
|
}
|
|
|
|
export type RunningRow = {
|
|
id: string;
|
|
code: string;
|
|
name: string;
|
|
months: number[];
|
|
ytd: number;
|
|
};
|
|
|
|
export function buildRunningRows(
|
|
assets: Asset[],
|
|
costs: MonthlyRunningCost[]
|
|
): RunningRow[] {
|
|
const byAsset = new Map<string, RunningRow>();
|
|
for (const a of assets) {
|
|
if (!a.code) continue;
|
|
byAsset.set(a.id, {
|
|
id: a.id, code: a.code, name: a.name,
|
|
months: Array(12).fill(0),
|
|
ytd: 0,
|
|
});
|
|
}
|
|
for (const c of costs) {
|
|
const row = byAsset.get(c.asset);
|
|
if (!row) continue;
|
|
row.months[c.month - 1] = c.amount;
|
|
row.ytd += c.amount;
|
|
}
|
|
return Array.from(byAsset.values());
|
|
}
|