Allow recurring costs and confirmations to remain unassigned when created from a purchase without a post. Add browser backup retention and restore controls, with coverage and documentation updates.
30 lines
1.1 KiB
TypeScript
30 lines
1.1 KiB
TypeScript
import type { MonthViewModel } from "../domain/views.ts";
|
|
|
|
export type MonthPieMode = "spending" | "allocation";
|
|
export interface MonthPieSlice {
|
|
id: string;
|
|
label: string;
|
|
amount: number;
|
|
}
|
|
|
|
/** Chart data comes from the same folded figures as the month table. */
|
|
export function monthPieData(view: MonthViewModel, mode: MonthPieMode) {
|
|
const items: MonthPieSlice[] = view.rows.map(({ post, figures }) => ({
|
|
id: post.id,
|
|
label: post.name,
|
|
amount: mode === "spending" ? figures.charges : figures.allocation,
|
|
}));
|
|
if (mode === "spending" && view.looseCharges !== 0) {
|
|
items.push({ id: "unassigned", label: "Unassigned", amount: view.looseCharges });
|
|
}
|
|
const byAmount = (a: MonthPieSlice, b: MonthPieSlice) => b.amount - a.amount || a.label.localeCompare(b.label);
|
|
const positive = items.filter((item) => item.amount > 0).sort(byAmount);
|
|
const adjustments = items.filter((item) => item.amount < 0).sort((a, b) => a.amount - b.amount);
|
|
return {
|
|
positive,
|
|
adjustments,
|
|
positiveTotal: positive.reduce((sum, item) => sum + item.amount, 0),
|
|
netTotal: mode === "spending" ? view.totalCharges : view.totalAllocation,
|
|
};
|
|
}
|