Skip to content

fix(reports): make a drill-down explain the figure that opened it - #150

Merged
KenTaniguchi-R merged 1 commit into
mainfrom
fix/144-drill-down-total
Sep 2, 2026
Merged

fix(reports): make a drill-down explain the figure that opened it#150
KenTaniguchi-R merged 1 commit into
mainfrom
fix/144-drill-down-total

Conversation

@KenTaniguchi-R

Copy link
Copy Markdown
Owner

Closes #144.

Clicking a spending row opened a sheet whose total disagreed with the row, for three separate reasons.

1. The sheet summed only the page it had

drill-down-sheet.tsx added up rows, which is capped at DRILL_DOWN_LIMIT = 50. Any category with more transactions than that showed a total short of the row above it, silently.

getDrillDownTransactions now returns total and matchCount computed over the whole matching population. The page is still 50 rows and now says so: "Showing the most recent 50 of 120".

2. The list was a different population from the figure

The query passed category + dates only, so a list explaining a spending number also contained transfers, pending rows and refunds — exactly the rows that figure excludes.

It now builds on spendingBaseConditions (the definition #142 made canonical), or incomeBaseConditions when an income node was clicked. Both are exported from spending-helpers rather than restated at the call site. The income rule matches the Sankey's income nodes and the Income vs Expense table — deliberately not the Total Income tile, whose extra uncategorized credits are the open gross-vs-net question in #146.

3. The sheet and the page disagreed about the date range

The page resolves its range from the preset; the sheet read useSearchParamFilters().dateRange, which falls back to 2000-01-01→today. On a bare /reports the URL carries no dates at all, so the tab showed three months while the drill-down queried all time — a $1,891.26 Groceries row opening a $3,746.58 sheet.

The resolved dateFrom / dateTo / accountIds are now passed down from the page through ReportTabs to each tab, so the two can no longer diverge. This also fixes the account filter, which the drill-down previously ignored entirely.

Refactor

getTransactions keeps its filter-shaped API. The page fetch underneath is extracted as fetchTransactionPage(householdId, conditions, …) so the report query can supply its own conditions without duplicating the split/row enrichment.

Tests

New tests/integration/report-drill-down.test.ts (6 tests), each pinning the drill-down against the tab figure that opens it:

  • total equals the Spending tab row
  • lists only the transactions the figure counted (refund, pending, transfer, paired leg and out-of-range rows all excluded)
  • total covers all 120 matches while the page holds 50
  • uncategorized drill-down matches its Spending row
  • honours the account filter
  • income drill-down total equals the Cash Flow income node

Verification

Full suite: 1061 passed, 1 failed — investment-queries.test.ts > returns dayChange from holdings_history, which is pre-existing hardcoded-date rot on clean main.

Checked live against demo data: Groceries reads $1,891.26 / 24 transactions on both the row and the sheet; Cash Flow's Salary node reads $23,100.00 / 6 on both.

🤖 Generated with Claude Code

Clicking a spending row opened a sheet whose total disagreed with the row,
for three separate reasons.

The sheet added up the page of rows it had been handed. That page is capped
at 50, so any category with more transactions than that displayed a total
short of the row above it, with no indication anything was missing. The
total and the match count now come from the server, computed over the whole
matching population; the page is still 50 rows, and now says so honestly
("Showing the most recent 50 of 120").

The query behind the sheet filtered on category and dates alone, so a list
meant to explain a spending figure also contained the transfers, pending
rows and refunds that figure had deliberately excluded. It now builds on
the same `spendingBaseConditions` every Reports tab agrees on, or on its
income counterpart when an income node was clicked — both exported from
`spending-helpers` rather than restated here.

Third, the sheet took its date range from the URL, where the page took its
own from the resolved preset. On a bare `/reports` the URL carries no dates
at all, so the tab showed three months while the drill-down queried
all-time: clicking a $1,891.26 Groceries row opened a sheet reading
$3,746.58. The resolved range and account filter are now passed down from
the page, so the two can no longer diverge.

`getTransactions` keeps its filter-shaped API; the page fetch underneath it
is extracted as `fetchTransactionPage` so the report query can supply its
own conditions without duplicating the row enrichment.

Verified against demo data: Groceries reads $1,891.26 / 24 transactions on
both the row and the sheet, and the Cash Flow Salary node reads $23,100.00
/ 6 on both.

Closes #144
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Spending drill-down total contradicts the row that opened it

1 participant