fix(reports): exclude investment_contribution from expense totals (#1750)#1999
fix(reports): exclude investment_contribution from expense totals (#1750)#1999Rene0422 wants to merge 3 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR adds ChangesInvestment contribution exclusion from budget analytics
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/models/transaction.rb`:
- Around line 83-90: Update the stale enum comment for the transaction kind
`investment_contribution` so it matches current behavior where such transactions
are excluded from budgets via BUDGET_EXCLUDED_KINDS and handled by
IncomeStatement::Totals (i.e., not treated as a budget expense); locate the
enum/kind documentation near the transaction kind definitions in transaction.rb
(where `investment_contribution` is declared) and rewrite the inline comment to
state that investment_contribution is excluded from budget calculations and
handled separately by tax‑advantaged account logic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b9513e9f-2ae8-4646-92b6-9b807d719bd4
📒 Files selected for processing (3)
app/models/income_statement/totals.rbapp/models/transaction.rbtest/models/income_statement_test.rb
|
@jjmata please review this PR, and let me know the result. |
|
Code review — PR #1999 The fix is well-reasoned and the implementation is clean. A few things worth considering before merge: ✅ What looks good
1. The PR explicitly calls this out but I think it deserves explicit sign-off: after this change, a transfer to an investment account is "not an expense", but a transfer to a loan account is still "an expense". Both move money between accounts the user owns. The distinction makes sense from a net-worth perspective (loan repayments reduce a liability, so they're directionally real cash flow), but from a cash-flow budgeting perspective users may expect symmetry. Fine to keep as-is, but worth confirming the team is aligned so a follow-up issue (#1313 per CodeRabbit) doesn't create confusion. 2. Interaction with PR #1995 PR #1995 (open, ~13 h older) also touches reports and 3. One-sided provider import edge case The second test creates only the investment-account leg of a contribution ( However, if a user also manually tagged the payroll line as a transfer (creating an Overall this is a solid fix for the reported issue. The three points above are questions/risks rather than blockers — happy to see this land once the Generated by Claude Code |
fdc76d2 to
72129bb
Compare
94d6b0c to
398d3b4
Compare
398d3b4 to
525df92
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
test/models/income_statement_test.rb (1)
305-320:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTest name contradicts assertions — rename or remove.
The test is named
"includes investment_contribution transactions as expenses..."but the assertions at lines 317-319 verify thatinvestment_contributionis excluded (transaction count stays at 4, expense stays at 900). The duplicate test at lines 322-340 correctly names this behavior.Either rename this test to reflect exclusion or remove it since lines 322-340 cover the same scenario with correct naming.
Suggested fix: rename to match behavior
- test "includes investment_contribution transactions as expenses in income statement" do + test "excludes investment_contribution transactions from income statement totals (outflow leg)" doOr remove this test entirely and keep only lines 322-340.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/models/income_statement_test.rb` around lines 305 - 320, The test named "includes investment_contribution transactions as expenses..." contradicts its assertions (it actually asserts exclusion) — either rename the test string to reflect exclusion (e.g., "excludes investment_contribution transactions from income statement") or remove this duplicate test; locate the block that calls create_transaction(..., kind: "investment_contribution") and constructs IncomeStatement.new(`@family`) and adjust the test name or delete the entire test to avoid duplicating the correct test that asserts exclusion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test/models/income_statement_test.rb`:
- Line 321: Remove the trailing whitespace in the test file by editing the
IncomeStatementTest (income_statement_test.rb) and deleting the extra space
characters at the end of the flagged line so the line ends cleanly with no
trailing spaces; save the file and re-run static checks.
---
Outside diff comments:
In `@test/models/income_statement_test.rb`:
- Around line 305-320: The test named "includes investment_contribution
transactions as expenses..." contradicts its assertions (it actually asserts
exclusion) — either rename the test string to reflect exclusion (e.g., "excludes
investment_contribution transactions from income statement") or remove this
duplicate test; locate the block that calls create_transaction(..., kind:
"investment_contribution") and constructs IncomeStatement.new(`@family`) and
adjust the test name or delete the entire test to avoid duplicating the correct
test that asserts exclusion.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c390a87d-2b1d-433f-b3de-ed890bb348cf
📒 Files selected for processing (3)
app/models/income_statement/totals.rbapp/models/transaction.rbtest/models/income_statement_test.rb
🚧 Files skipped from review as they are similar to previous changes (1)
- app/models/transaction.rb
ff53608 to
5119e91
Compare
5119e91 to
70abde0
Compare
|
Hey @Rene0422 , looks like you have done some good work on this. Any idea when it might get merged in, ready for a release? |
|
Please add this change. I would love to turn this off. My Sankey is totally broken. |
|
Triage note: CI is green and the scope is tight, but @jjmata's review questions from 2026-05-27 (loan_payment/investment_contribution asymmetry sign-off, potential conflict with #1995, and whether Plaid tags both legs of a contribution as investment_contribution) appear unanswered by the author. Meanwhile several users (including one reporting a broken Sankey chart) have asked for this to be merged. Flagging for maintainer attention since it's otherwise ready modulo those open questions. Generated by Claude Code |
|
This fix is clean and CI is green, but jjmata's review from 05-27 raised three questions that don't appear to have been answered: is the Generated by Claude Code |
Summary
Closes #1750.
When a user explicitly marks a transaction as a Transfer to an investment account, the outflow leg gets the
investment_contributionkind. The Reports page was still counting that leg as an expense, inflating the spending total. The reporter's case (Mutual Fund / Other Investment, self-hosted, transaction explicitly marked as Transfer with From/To fields) reproduces the bug exactly.Root cause
Transaction::BUDGET_EXCLUDED_KINDSlistedfunds_movement,one_time, andcc_payment— the kinds that get filtered out of expense totals.investment_contributionwas deliberately not in the list, and the inline comment said so:That's one valid framing, but it conflicts with the user's mental model (they classified the transaction as a transfer, not an expense) and with how regular
funds_movementtransfers are already handled. Tax-advantaged investment accounts (Roth IRA, 401k, HSA) are already excluded by a separate path (tax_advantaged_account_ids), which shows the system already recognises "money into an investment account is not necessarily spending."This PR flips the choice for
investment_contributiononly.loan_paymentkeeps its current treatment — reducing loan principal is a different conversation and wasn't reported.Changes
app/models/transaction.rb—BUDGET_EXCLUDED_KINDSnow includesinvestment_contribution. Comment rewritten to explain the rationale and to call out thatloan_paymentis intentionally still treated as an expense.app/models/income_statement/totals.rb— both SQL queries hadCASE WHEN at.kind IN ('investment_contribution', 'loan_payment') THEN 'expense' …to forceinvestment_contributionto classify as expense regardless of entry sign. Since it's now filtered out by theWHERE NOT INclause before reaching theCASE, the branch is dead code; simplified toCASE WHEN at.kind = 'loan_payment'(also in the matchingGROUP BY).test/models/income_statement_test.rb— flipped both"includes investment_contribution … as expenses"tests to assert exclusion. One covers an outflow on the source side; the other covers a provider-imported negative-amount inflow on the destination side. The negative-sign case is important because it confirms the kind is the load-bearing signal, not the entry sign.Propagation / call sites checked
The change propagates automatically through every site that already filters on
BUDGET_EXCLUDED_KINDS:BudgetCategoriesController(budget category totals)ReportsController#build_transactions_breakdownReportsController#build_transactions_breakdown_for_exportReportsController#build_monthly_breakdown_for_exportSo the breakdown table, CSV export, and monthly breakdown all stay consistent with the totals.
Transaction::SearchusesTRANSFER_KINDS(notBUDGET_EXCLUDED_KINDS) and already excludedinvestment_contributionfrom the "expense" filter —test/models/transaction/search_test.rb:262locks that in. No change needed there.Tax-advantaged account exclusion is independent (it filters on
accounts.id NOT IN (:tax_advantaged_account_ids)) and is unaffected; its tests usestandard-kind transactions.Test plan
bin/rails test test/models/income_statement_test.rbbin/rails test test/models/transaction/search_test.rbbin/rails test test/controllers/reports_controller_test.rbbin/rails testbin/rubocop -f github -abundle exec erb_lint ./app/**/*.erb -abin/brakeman --no-pagerSummary by CodeRabbit
Bug Fixes
Tests