financial page with bank data
All checks were successful
Deploy / build-deploy (push) Successful in 1m48s

This commit is contained in:
Jorijn van der Graaf 2026-08-14 04:14:13 +02:00
commit 33c68c2f44
11 changed files with 394 additions and 87 deletions

View file

@ -298,14 +298,18 @@ export struct Financials {
// empty = nothing published yet
std::int64_t donationCount = 0;
std::int64_t donationsMinor = 0;
std::vector<FinCategory> recurring; // insurance, hosting, …
std::vector<FinCategory> single; // inventory, fees, tax, …
// ONE flat list of expense categories. This carried a recurring/one-off
// split until it was removed deliberately: the page publishes LIFETIME
// running totals, and a lifetime total cannot express a rate. "Telecom
// €158.18" says nothing about whether that is monthly or once ever, so
// the grouping conveyed no information while inviting the reader to infer
// one. Rate belongs to a cash-flow view this page is not.
std::vector<FinCategory> expenses;
bool Loaded() const { return !asOf.empty(); }
std::int64_t ExpensesMinor() const {
std::int64_t sum = 0;
for (const FinCategory& c : recurring) sum += c.totalMinor;
for (const FinCategory& c : single) sum += c.totalMinor;
for (const FinCategory& c : expenses) sum += c.totalMinor;
return sum;
}
};
@ -322,9 +326,7 @@ export Financials LoadFinancials(std::string_view json) {
out.donationCount = d->Int("count");
out.donationsMinor = d->Int("total_minor");
}
auto categories = [](const Json::Value* arr) {
std::vector<FinCategory> cats;
if (!arr || !arr->IsArray()) return cats;
if (const Json::Value* arr = doc->Find("expenses"); arr && arr->IsArray()) {
for (const Json::Value& v : arr->array) {
if (!v.IsObject()) continue;
FinCategory c;
@ -333,12 +335,9 @@ export Financials LoadFinancials(std::string_view json) {
// A category with no label has nothing to render as; dropping it
// beats an anonymous row that looks like a redaction.
if (c.label.empty()) continue;
cats.push_back(std::move(c));
out.expenses.push_back(std::move(c));
}
return cats;
};
out.recurring = categories(doc->Find("recurring"));
out.single = categories(doc->Find("single"));
}
return out;
}