report models and prepareReport function
This commit is contained in:
61
src/features/report/report.models.ts
Normal file
61
src/features/report/report.models.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
export interface Payor {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Payee {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Account {
|
||||||
|
name: string;
|
||||||
|
number: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Tag {
|
||||||
|
name: string;
|
||||||
|
icon: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Transaction {
|
||||||
|
payor: Payor;
|
||||||
|
payee: Payee;
|
||||||
|
amount: number;
|
||||||
|
account: Account;
|
||||||
|
tags: Tag[]
|
||||||
|
occurred_at: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface _PeriodData {
|
||||||
|
sum: number;
|
||||||
|
count: number;
|
||||||
|
average: number;
|
||||||
|
txns: Transaction[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PeriodData extends _PeriodData {
|
||||||
|
compare?: _PeriodData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PeriodGroup {
|
||||||
|
group_key: string[];
|
||||||
|
expenses: PeriodData[];
|
||||||
|
incomes: PeriodData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Period {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
start: Date;
|
||||||
|
end: Date;
|
||||||
|
groups: PeriodGroup[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReportData {
|
||||||
|
period: "weekly" | "monthly" | "yearly" | "fyly" | "full";
|
||||||
|
rolling?: boolean;
|
||||||
|
report_date?: string;
|
||||||
|
group_by?: ("payee" | "tags")[];
|
||||||
|
ignore_self?: boolean;
|
||||||
|
buckets: Period[];
|
||||||
|
}
|
||||||
116
src/features/report/report.utils.ts
Normal file
116
src/features/report/report.utils.ts
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import { ReportData } from "./report.models";
|
||||||
|
|
||||||
|
/* ---------- ID BUILDING ---------- */
|
||||||
|
|
||||||
|
function formatDate(d: Date): string {
|
||||||
|
const y = d.getUTCFullYear();
|
||||||
|
const m = String(d.getUTCMonth() + 1).padStart(2, "0");
|
||||||
|
const day = String(d.getUTCDate()).padStart(2, "0");
|
||||||
|
return `${y}-${m}-${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPeriodId(
|
||||||
|
type: ReportData["period"],
|
||||||
|
start: Date,
|
||||||
|
end: Date
|
||||||
|
): string {
|
||||||
|
const s = formatDate(start);
|
||||||
|
const e = formatDate(end);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "weekly":
|
||||||
|
return `W:${s}_${e}`;
|
||||||
|
case "monthly":
|
||||||
|
return `M:${s}_${e}`;
|
||||||
|
case "yearly":
|
||||||
|
return `Y:${s}_${e}`;
|
||||||
|
case "fyly":
|
||||||
|
return `FY:${s}_${e}`;
|
||||||
|
case "full":
|
||||||
|
return `FULL:${s}_${e}`;
|
||||||
|
default:
|
||||||
|
return `${s}_${e}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- LABEL BUILDING ---------- */
|
||||||
|
|
||||||
|
const dayFmt = new Intl.DateTimeFormat("en-GB", {
|
||||||
|
day: "numeric",
|
||||||
|
month: "short",
|
||||||
|
timeZone: "UTC",
|
||||||
|
});
|
||||||
|
|
||||||
|
const monthDayFmt = new Intl.DateTimeFormat("en-GB", {
|
||||||
|
month: "short",
|
||||||
|
day: "numeric",
|
||||||
|
timeZone: "UTC",
|
||||||
|
});
|
||||||
|
|
||||||
|
const monthFmt = new Intl.DateTimeFormat("en-GB", {
|
||||||
|
month: "short",
|
||||||
|
timeZone: "UTC",
|
||||||
|
});
|
||||||
|
|
||||||
|
const yearFmt = new Intl.DateTimeFormat("en-GB", {
|
||||||
|
year: "numeric",
|
||||||
|
timeZone: "UTC",
|
||||||
|
});
|
||||||
|
|
||||||
|
function sameMonth(a: Date, b: Date) {
|
||||||
|
return (
|
||||||
|
a.getUTCFullYear() === b.getUTCFullYear() &&
|
||||||
|
a.getUTCMonth() === b.getUTCMonth()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLabel(
|
||||||
|
type: ReportData["period"],
|
||||||
|
start: Date,
|
||||||
|
end: Date
|
||||||
|
): string {
|
||||||
|
switch (type) {
|
||||||
|
case "weekly":
|
||||||
|
if (sameMonth(start, end)) {
|
||||||
|
const sDay = start.getUTCDate();
|
||||||
|
const eDay = end.getUTCDate();
|
||||||
|
const m = monthFmt.format(start);
|
||||||
|
return `${sDay} ${m} - ${eDay} ${m}`;
|
||||||
|
}
|
||||||
|
return `${dayFmt.format(start)} - ${dayFmt.format(end)}`;
|
||||||
|
|
||||||
|
case "monthly":
|
||||||
|
if (sameMonth(start, end)) {
|
||||||
|
return `${monthFmt.format(start)} ${yearFmt.format(start)}`;
|
||||||
|
}
|
||||||
|
return `${monthDayFmt.format(start)} - ${monthDayFmt.format(end)}`;
|
||||||
|
|
||||||
|
case "yearly":
|
||||||
|
return yearFmt.format(start);
|
||||||
|
|
||||||
|
case "fyly": {
|
||||||
|
const startY = start.getUTCFullYear();
|
||||||
|
const endY = end.getUTCFullYear();
|
||||||
|
return `FY ${startY}–${String(endY).slice(-2)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "full":
|
||||||
|
return `${monthFmt.format(start)} ${yearFmt.format(start)} - ${monthFmt.format(end)} ${yearFmt.format(end)}`;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return `${monthDayFmt.format(start)} - ${monthDayFmt.format(end)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- MAIN ---------- */
|
||||||
|
|
||||||
|
export function prepareReport(reportData: ReportData): ReportData {
|
||||||
|
return {
|
||||||
|
...reportData,
|
||||||
|
buckets: reportData.buckets.map((p) => ({
|
||||||
|
...p,
|
||||||
|
id: buildPeriodId(reportData.period, p.start, p.end),
|
||||||
|
label: buildLabel(reportData.period, p.start, p.end),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user