feat(budget): report spend the budget view does not count - #40
Open
ryakel wants to merge 1 commit into
Open
Conversation
Budget card actuals are SUM(expense_budget_links.amount), so an expense with no link row counts against no budget at all, and flight cost columns (fuel_cost, landing_fees, instructor_cost, rental_cost, other_costs) are never read by the card system. Both push reported actuals below true spend, so the budget view reads healthier than reality. Adds GET /api/user/budget-cards/summary/uncounted reporting: - unlinked expenses: count, total, and the rows themselves - partially linked expenses: where links do not cover the full amount, the shortfall counts against no budget either - flight cost totals, itemised, kept as a separate figure The two gaps stay distinct deliberately. An unlinked expense is fixed by linking it; flight costs are a modelling question about whether they should become expenses at all. Blending them into one number would imply a single remedy that does not exist. Surfacing only - what actual_amount counts is unchanged. SQL validated against the real Postgres grammar via pglast (the test suite mocks the database, so no test executes these statements). Ticket: FATH-10 Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ticket: FATH-10 (backend half — the UI is still to come)
The problem
Budget card
actual_amountis computed asSUM(expense_budget_links.amount)— the link amount, notexpenses.amount:So an expense with no link row counts against no budget at all. It shows in the expenses list, feeds
/api/user/expenses/summary, and is absent from every budget figure. Linking is optional on every write path —CreateExpenseModallinks only if a card was picked, and CSV import links only when the row carried abudget_card_id.Separately,
flightscarriesfuel_cost,landing_fees,instructor_cost,rental_cost,other_costs. The budget-card system never reads them; only the deprecatedbudgetsendpoint sums them.Both gaps push reported actuals below true spend, so the budget view reads healthier than the bank account.
What this adds
GET /api/user/budget-cards/summary/uncounted:The two gaps are deliberately kept distinct. An unlinked expense is fixed by linking it. Flight costs are a modelling question — should they become expenses at all? Blending them into one number would imply a single remedy that doesn't exist.
Surfacing only. What
actual_amountcounts is unchanged.Testing
.pre-commit-config.yaml— all cleanOne caveat worth stating plainly: the test suite mocks Postgres entirely (
tests/conftest.pypatchespostgres_db.connect), so no test executes the SQL in this PR. The new tests exercise the endpoint contract and the response shape, not the queries. To get some real assurance I parsed all four statements withpglast(libpg_query — the actual Postgres grammar); all four parse. That validates syntax, not that the joins return what I think they do.Given this repo has now shipped two bugs in files no test executes, that gap seems worth naming rather than glossing.
Still to come on FATH-10
The budget view UI — surfacing these figures next to the totals they contradict, and letting an unlinked expense be linked to a card without leaving the page (reusing the existing
link-expenseendpoint andexpenseStore.linkToBudgetCard).Note
@tanstack/react-queryis a dependency with zero usages insrc/— the frontend is Zustand + plainfetchthroughout, and the UI half will follow that rather than introduce a second data-fetching pattern.Generated by Claude Code