Skip to content

fix(transactions): include one-time in reports, keep out of budget medians#1995

Open
glorydavid03023 wants to merge 17 commits into
we-promise:mainfrom
glorydavid03023:fix/one-time-transactions-in-reports
Open

fix(transactions): include one-time in reports, keep out of budget medians#1995
glorydavid03023 wants to merge 17 commits into
we-promise:mainfrom
glorydavid03023:fix/one-time-transactions-in-reports

Conversation

@glorydavid03023

@glorydavid03023 glorydavid03023 commented May 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #1994

Summary

  • Adds Transaction::REPORT_EXCLUDED_KINDS (transfers + CC payments only)
  • Historical totals, reports, and default expense_totals / income_totals include one-time transactions
  • Budget medians, budget category actuals, and for_budget: true period totals still exclude one-time
  • Budget#actual_income now uses budget-scoped income_totals (matches actual_spending)

Test plan

  • bin/rails test test/models/income_statement_test.rb
  • bin/rails test test/models/budget_test.rb:457

Summary by CodeRabbit

  • Bug Fixes

    • Fixed transaction-kind filtering so reports, exports, and budget calculations use the correct exclusion set, improving accuracy of breakdowns and exported aggregates.
  • Refactor

    • Totals now distinguish budget vs. report modes so cached totals and period calculations reflect the proper exclusion rules.
  • Documentation

    • Updated description of one-time transactions in historical reports and budget medians.
  • Tests

    • Added tests ensuring one-time transactions are included in standard totals but excluded for budget-scoped totals.

Review Change Stack

@superagent-security superagent-security Bot added the contributor:verified Contributor passed trust analysis. label May 25, 2026
@superagent-security superagent-security Bot added the pr:verified PR passed security analysis. label May 25, 2026
@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Split excluded transaction kinds into REPORT_EXCLUDED_KINDS vs BUDGET_EXCLUDED_KINDS, thread a new for_budget flag through IncomeStatement/Totals (memoized by period+mode), adjust Budget to use budget-mode totals, and make ReportsController use report-mode exclusions.

Changes

One-Time vs Excluded Transaction Separation

Layer / File(s) Summary
Transaction Kind Exclusion Constants
app/models/transaction.rb
Define REPORT_EXCLUDED_KINDS (funds_movement, cc_payment) and refactor BUDGET_EXCLUDED_KINDS to be computed from REPORT_EXCLUDED_KINDS plus one_time. Update enum comment for one_time.
IncomeStatement Dual-Mode Calculation Support
app/models/income_statement.rb
Public methods expense_totals, income_totals, net_category_totals accept for_budget: (default false) and memoize per [period, for_budget]. build_period_total and totals_query accept/exercise excluded_kinds; cache key version bumped to include excluded kinds.
IncomeStatement::Totals Excluded Kinds Parameterization
app/models/income_statement/totals.rb
Totals#initialize gains excluded_kinds (default REPORT_EXCLUDED_KINDS) and uses it to build excluded_kinds_sql for SQL NOT IN (...) clauses in transaction subqueries.
Budget Integration with Dual-Mode Totals
app/models/budget.rb
Budget memoized helpers (net_totals, expense_totals, income_totals) call income_statement(..., for_budget: true); actual_income reads income_totals.total from the memoized helper.
ReportsController Report Exclusion Alignment
app/controllers/reports_controller.rb
build_transactions_breakdown, build_transactions_breakdown_for_export, and build_monthly_breakdown_for_export updated to exclude REPORT_EXCLUDED_KINDS so one-time transactions are present in reports/exports.
Test Coverage and Documentation Updates
test/models/income_statement_test.rb, test/models/budget_test.rb, config/locales/views/transactions/en.yml
New tests assert one-time transactions are included in standard totals and excluded under for_budget: true; budget test verifies actual_spending/actual_income exclude one-time entries; locale string updated.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ReportsController
  participant IncomeStatement
  participant Totals
  participant DB
  User->>ReportsController: request report/export
  ReportsController->>DB: query transactions excluding REPORT_EXCLUDED_KINDS
  ReportsController-->>User: report (includes one-time)
  User->>IncomeStatement: expense_totals(period, for_budget:true/false)
  IncomeStatement->>Totals: totals_query(excluded_kinds)
  Totals->>DB: SQL using excluded_kinds_sql
  DB-->>Totals: aggregated totals
  Totals-->>IncomeStatement: totals (cached by period + for_budget)
  IncomeStatement-->>User: memoized totals
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

contributor:verified, pr:verified

Suggested reviewers

  • jjmata
  • sokie
  • gariasf

"I nibbled through the totals, found the one-time seeds,
Reports keep their history, budgets prune their weeds,
Memoized caches know which mode to keep,
Tests hop in tidy rows, no secrets to sweep,
Hooray — two paths, both awake, both sweet!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: one-time transactions are now included in reports while remaining excluded from budget medians.
Linked Issues check ✅ Passed The PR fully addresses all coding requirements from issue #1994: excluded transactions are omitted from all totals/reports/analytics/budgets, while one-time transactions now appear in historical reporting but are excluded from budget predictions and recurring forecasts.
Out of Scope Changes check ✅ Passed All changes directly support the core objective of differentiating excluded vs one-time transactions; no unrelated modifications detected beyond the scope of issue #1994.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
app/controllers/reports_controller.rb (1)

359-360: ⚡ Quick win

Update stale exclusion comments to match REPORT_EXCLUDED_KINDS.

The three comments still state one-time transactions are excluded, but the new filter now includes one-time transactions in reports. Please update the comments to avoid confusion.

✏️ Suggested comment-only patch
-      # Exclude transfers, one-time, and CC payments (matching income_statement logic)
+      # Exclude transfers and CC payments (reports include one-time transactions)
-      # Exclude transfers, one-time, and CC payments (matching income_statement logic)
+      # Exclude transfers and CC payments (reports include one-time transactions)
-      # Exclude transfers, one-time, and CC payments (matching income_statement logic)
+      # Exclude transfers and CC payments (reports include one-time transactions)

Also applies to: 365-365, 663-664, 669-669, 700-701, 706-706

🤖 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 `@app/controllers/reports_controller.rb` around lines 359 - 360, The comment
that reads "Exclude transfers, one-time, and CC payments (matching
income_statement logic)" is stale because REPORT_EXCLUDED_KINDS no longer
excludes one-time transactions; update the comment near the Transaction query to
reflect current behavior (e.g., "Exclude transfers and CC payments; one-time
transactions are included per REPORT_EXCLUDED_KINDS") and make the same change
for the other occurrences noted (the other comment instances around the
Transaction/query blocks). Ensure you reference REPORT_EXCLUDED_KINDS in the
updated comment so readers know the exclusion set is driven by that constant.
🤖 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/budget.rb`:
- Around line 326-327: actual_income is still calling
family.income_statement.income_totals(period: self.period).total and thus
bypasses the budget-scoped totals; update the actual_income method to use the
budget-scoped totals by calling income_statement.income_totals with for_budget:
true (or reuse the existing income_totals helper) e.g. replace the direct
family.income_statement.income_totals(...) call with
income_statement.income_totals(period: period, for_budget: true).total or call
the model's income_totals to ensure one-time income is excluded.

In `@app/models/income_statement/totals.rb`:
- Around line 164-165: The manual string interpolation in excluded_kinds_sql is
brittle; change it to use the DB adapter's quoting (e.g. connection.quote or
ActiveRecord::Base.connection.quote) when building the list so each kind is
safely quoted (e.g. map each `@excluded_kinds` through connection.quote and join
with ", "), preserving the memoization of `@excluded_kinds_sql` in the
excluded_kinds_sql method.

---

Nitpick comments:
In `@app/controllers/reports_controller.rb`:
- Around line 359-360: The comment that reads "Exclude transfers, one-time, and
CC payments (matching income_statement logic)" is stale because
REPORT_EXCLUDED_KINDS no longer excludes one-time transactions; update the
comment near the Transaction query to reflect current behavior (e.g., "Exclude
transfers and CC payments; one-time transactions are included per
REPORT_EXCLUDED_KINDS") and make the same change for the other occurrences noted
(the other comment instances around the Transaction/query blocks). Ensure you
reference REPORT_EXCLUDED_KINDS in the updated comment so readers know the
exclusion set is driven by that constant.
🪄 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: 96228cc0-d661-45c4-ab7a-2530d49b2bb1

📥 Commits

Reviewing files that changed from the base of the PR and between d8a12ad and 3abc0d4.

📒 Files selected for processing (7)
  • app/controllers/reports_controller.rb
  • app/models/budget.rb
  • app/models/income_statement.rb
  • app/models/income_statement/totals.rb
  • app/models/transaction.rb
  • config/locales/views/transactions/en.yml
  • test/models/income_statement_test.rb

Comment thread app/models/budget.rb
Comment thread app/models/income_statement/totals.rb Outdated
…dians


Split transfer exclusions from one-time: historical totals, reports, and period expense/income use REPORT_EXCLUDED_KINDS; budget medians and budget category actuals still use BUDGET_EXCLUDED_KINDS.

Fixes we-promise#1994
@glorydavid03023
glorydavid03023 force-pushed the fix/one-time-transactions-in-reports branch from 3abc0d4 to 51eb140 Compare May 26, 2026 00:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
app/controllers/reports_controller.rb (3)

359-359: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the comment to reflect the new exclusion behavior.

The comment states "Exclude transfers, one-time, and CC payments" but with REPORT_EXCLUDED_KINDS, only transfers and CC payments are excluded. One-time transactions are now included in reports per the PR objectives.

📝 Proposed fix
-      # Exclude transfers, one-time, and CC payments (matching income_statement logic)
+      # Exclude transfers and CC payments (matching income_statement report logic)
🤖 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 `@app/controllers/reports_controller.rb` at line 359, Update the outdated
comment that currently reads "Exclude transfers, one-time, and CC payments" to
reflect the actual exclusion logic: mention only transfers and CC payments are
excluded. Locate the comment near the REPORT_EXCLUDED_KINDS usage in
reports_controller (the block that references REPORT_EXCLUDED_KINDS) and change
it to something like "Exclude transfers and CC payments (matching
income_statement logic)" so it matches the implemented behavior.

700-700: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the comment to reflect the new exclusion behavior.

The comment states "Exclude transfers, one-time, and CC payments" but with REPORT_EXCLUDED_KINDS, only transfers and CC payments are excluded. One-time transactions are now included in monthly breakdown exports per the PR objectives.

📝 Proposed fix
-      # Exclude transfers, one-time, and CC payments (matching income_statement logic)
+      # Exclude transfers and CC payments (matching income_statement report logic)
🤖 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 `@app/controllers/reports_controller.rb` at line 700, The comment above the
monthly breakdown export no longer matches behavior: update the comment that
currently reads "Exclude transfers, one-time, and CC payments (matching
income_statement logic)" to accurately describe that only transfers and CC
payments are excluded based on REPORT_EXCLUDED_KINDS; remove reference to
one-time transactions. Locate the comment near the REPORT_EXCLUDED_KINDS usage
in reports_controller (e.g., around the monthly breakdown/export logic) and
change the text to something like "Exclude transfers and CC payments (matching
income_statement logic)" so it reflects the actual exclusion rules.

663-663: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the comment to reflect the new exclusion behavior.

The comment states "Exclude transfers, one-time, and CC payments" but with REPORT_EXCLUDED_KINDS, only transfers and CC payments are excluded. One-time transactions are now included in exported reports per the PR objectives.

📝 Proposed fix
-      # Exclude transfers, one-time, and CC payments (matching income_statement logic)
+      # Exclude transfers and CC payments (matching income_statement report logic)
🤖 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 `@app/controllers/reports_controller.rb` at line 663, Update the comment that
currently reads "Exclude transfers, one-time, and CC payments (matching
income_statement logic)" to reflect the current behavior: only transfers and CC
payments are excluded via REPORT_EXCLUDED_KINDS; one-time transactions are
included in exported reports. Edit the comment located next to
REPORT_EXCLUDED_KINDS / the related filtering logic in ReportsController
(reports_controller.rb) to state "Exclude transfers and CC payments; one-time
transactions are included."
♻️ Duplicate comments (1)
app/models/budget.rb (1)

326-327: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Budget income actuals still bypass the budget-scoped totals

Line 294 still calls non-budget income totals directly, so one-time income can leak into budget metrics despite this helper now using for_budget: true.

Suggested fix
 def actual_income
-  family.income_statement.income_totals(period: self.period).total
+  income_totals.total
 end
🤖 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 `@app/models/budget.rb` around lines 326 - 327, The code is still calling
income_statement.income_totals without scoping to the budget, allowing one-time
income to leak; update the direct call (the other location that invokes
income_statement.income_totals) to either use the budget-scoped helper
income_totals or pass for_budget: true to income_statement.income_totals so the
totals are computed with for_budget: true; locate the direct call and replace it
with a call to income_totals or income_statement.income_totals(period: period,
for_budget: true) to ensure budget-scoped totals are used everywhere.
🤖 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.

Outside diff comments:
In `@app/controllers/reports_controller.rb`:
- Line 359: Update the outdated comment that currently reads "Exclude transfers,
one-time, and CC payments" to reflect the actual exclusion logic: mention only
transfers and CC payments are excluded. Locate the comment near the
REPORT_EXCLUDED_KINDS usage in reports_controller (the block that references
REPORT_EXCLUDED_KINDS) and change it to something like "Exclude transfers and CC
payments (matching income_statement logic)" so it matches the implemented
behavior.
- Line 700: The comment above the monthly breakdown export no longer matches
behavior: update the comment that currently reads "Exclude transfers, one-time,
and CC payments (matching income_statement logic)" to accurately describe that
only transfers and CC payments are excluded based on REPORT_EXCLUDED_KINDS;
remove reference to one-time transactions. Locate the comment near the
REPORT_EXCLUDED_KINDS usage in reports_controller (e.g., around the monthly
breakdown/export logic) and change the text to something like "Exclude transfers
and CC payments (matching income_statement logic)" so it reflects the actual
exclusion rules.
- Line 663: Update the comment that currently reads "Exclude transfers,
one-time, and CC payments (matching income_statement logic)" to reflect the
current behavior: only transfers and CC payments are excluded via
REPORT_EXCLUDED_KINDS; one-time transactions are included in exported reports.
Edit the comment located next to REPORT_EXCLUDED_KINDS / the related filtering
logic in ReportsController (reports_controller.rb) to state "Exclude transfers
and CC payments; one-time transactions are included."

---

Duplicate comments:
In `@app/models/budget.rb`:
- Around line 326-327: The code is still calling income_statement.income_totals
without scoping to the budget, allowing one-time income to leak; update the
direct call (the other location that invokes income_statement.income_totals) to
either use the budget-scoped helper income_totals or pass for_budget: true to
income_statement.income_totals so the totals are computed with for_budget: true;
locate the direct call and replace it with a call to income_totals or
income_statement.income_totals(period: period, for_budget: true) to ensure
budget-scoped totals are used everywhere.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cb07d7ee-3468-4704-aeef-f5bb07acb01e

📥 Commits

Reviewing files that changed from the base of the PR and between 3abc0d4 and 51eb140.

📒 Files selected for processing (7)
  • app/controllers/reports_controller.rb
  • app/models/budget.rb
  • app/models/income_statement.rb
  • app/models/income_statement/totals.rb
  • app/models/transaction.rb
  • config/locales/views/transactions/en.yml
  • test/models/income_statement_test.rb
✅ Files skipped from review due to trivial changes (1)
  • config/locales/views/transactions/en.yml

jjmata commented May 26, 2026

Copy link
Copy Markdown
Collaborator

The REPORT_EXCLUDED_KINDS / BUDGET_EXCLUDED_KINDS split is clean. Defining BUDGET_EXCLUDED_KINDS = REPORT_EXCLUDED_KINDS + %w[one_time] makes the superset relationship explicit and removes any ambiguity about which kinds land where.

A few notes:

Cache key bump: Bumping from "v2""v3" in totals_query is necessary here. Any in-flight Rails.cache entries keyed on "v2" would otherwise serve stale results (one-time transactions excluded from reports) after deploy. Good catch.

kinds_key in cache array: Including kinds_key = excluded_kinds.join(",") in the cache key means budget and non-budget calls on the same IncomeStatement instance correctly get separate cache entries. No cross-contamination risk.

Budget memoization: Budget#income_statement returns a fresh IncomeStatement instance, so even though Budget now always passes for_budget: true, there's no risk of a cached non-budget result being served to a budget caller through the instance-level @expense_totals_by_period hash.

Updated locale string: The new description ("One-time transactions appear in historical reports and totals but are excluded from budget medians and recurring spending estimates") is clearer than the old one and accurately reflects the behavior after this change. Good.

Test naming: Splitting into "includes one-time transactions in income statement totals and reports" and "excludes one-time transactions from budget-scoped period totals" makes the before/after contract explicit and will be easy to understand if either test regresses.


Generated by Claude Code

@jjmata
jjmata requested a review from gariasf May 26, 2026 07:54
Comment thread app/models/budget.rb

def income_totals
@income_totals ||= income_statement.income_totals(period: period)
@income_totals ||= income_statement.income_totals(period: period, for_budget: true)

@gariasf gariasf May 27, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Budget#actual_income bypasses this private helper — asymmetric with actual_spending / estimated_income.

# line 294
def actual_income
  family.income_statement.income_totals(period: self.period).total
end

actual_spending (line 230) goes through net_totalsfor_budget: true and correctly excludes one-time. actual_income calls the public income_totals directly, hits the new default for_budget: falseREPORT_EXCLUDED_KINDSone-time income is now included.

Concrete effect: estimated_income is the median (no one-time). A one-time bonus inflates actual_income, actual_income_percent, remaining_expected_income, surplus_percent. Same-magnitude one-time expense does not show in actual_spending. This is the same defect the PR fixes — just on the income side.

Fix — route through the private memoized helper so it matches actual_spending:

def actual_income
  income_totals.total
end

Also worth adding a Budget-level test pinning both actual_spending and actual_income against a kind: "one_time" transaction. Would catch this and future drift.

…ping


Route Budget#actual_income through budget-scoped income_totals, update report
exclusion comments, harden excluded_kinds_sql quoting, and add budget tests.
@glorydavid03023
glorydavid03023 force-pushed the fix/one-time-transactions-in-reports branch from dd99336 to 0afb039 Compare May 27, 2026 16:29
@jjmata
jjmata requested a review from gariasf May 27, 2026 20:07
@superagent-security superagent-security Bot removed contributor:verified Contributor passed trust analysis. pr:verified PR passed security analysis. labels May 29, 2026

@gariasf gariasf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed locally — the logic is correct and well-designed, but there's a blocker: a test fails on the current head.

Failing test: test/models/income_statement_test.rb"memoizes expense and income period totals across repeated calculations" (≈ line 42).

The mocks are:

income_statement.expects(:build_period_total).with(classification: "expense", period: period)
income_statement.expects(:build_period_total).with(classification: "income",  period: period)

but build_period_total now always receives for_budget:, so the expectations never match:

unexpected invocation: build_period_total(classification: "expense", period: …, for_budget: false)
unsatisfied:           build_period_total(classification: "expense", period: …)

Result: 1 failure / 58 runs. (The PR rewrote the one-time tests but missed this memoization mock — the [x] income_statement_test.rb box no longer holds.)

Fix: add for_budget: false to both .with(...) expectations.

Everything else checks out:

  • The BUDGET_EXCLUDED_KINDS = REPORT_EXCLUDED_KINDS + %w[one_time] superset is the right call — the untouched median path (FamilyStats/CategoryStats) references BUDGET_EXCLUDED_KINDS, so medians correctly keep excluding one-time. Verified.
  • Cache key bump v2 → v3 + kinds_key cleanly separates budget vs report totals.
  • actual_income → budget-scoped income_totals is a good consistency fix.
  • Reports and Budgets both render fine in the browser.

Minor: dead local key = period_cache_key(period) left in expense_totals (income_totals correctly dropped it).

glorydavid03023 and others added 4 commits June 2, 2026 20:59
Signed-off-by: glorydavid03023 <glorydavid03023@gmail.com>
…tations

CI test_unit failures were all introduced by this branch's own changes:

- IncomeStatement#build_period_total gained a `for_budget:` kwarg, but two
  new memoization tests (IncomeStatementTest and PagesControllerTest) still
  mocked build_period_total with the old `(classification:, period:)`
  signature, so the real `for_budget: false` call matched no expectation
  ("invoked never"). Updated both mocks to expect `for_budget: false`.

- "reuses totals_query for income and expense in the same period" expected a
  single totals_query call, but build_period_total fetched totals directly and
  ran the query once per classification (2 calls). The branch already added a
  `totals_for_period` helper for exactly this sharing but never used it, and it
  ignored the budget/report kind scoping. Wired build_period_total through
  totals_for_period and parameterized it by `for_budget` (memoized per
  period+scope), so income and expense reuse one query while budget vs report
  exclusions stay correct. No change to computed totals.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@gariasf gariasf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed after 666710c2. The blocker from my last review is resolved: both mock sites now pass for_budget: false (including the pages_controller_test.rb one I had missed), and CI is fully green on head, test_unit and test_system included.

The fix also went further than the test alignment, and the refactor is a real improvement: build_period_total now routes through a shared totals_for_period(period, for_budget:), so income and expense for the same period+scope share one fetch. I checked the things that could have gone wrong with it:

  • All four instance memo layers are keyed [period_cache_key, for_budget] consistently (@expense_totals_by_period, @income_totals_by_period, @net_category_totals_by_period, @totals_for_period), so budget and report totals can't cross-contaminate.
  • totals_for_period has exactly one caller, and it builds the same transactions_scope the inline version used. No semantic drift.
  • Call sites land where they should: dashboard, reports, and the AI assistant function get report scope; budget internals all pass for_budget: true; actual_income goes through the private helper now.
  • The median path (FamilyStats/CategoryStats) still references BUDGET_EXCLUDED_KINDS, so one-time stays out of medians.
  • The budget-level regression test I asked for is in (budget_test.rb), pinning both actual_spending and actual_income against one-time noise, with the memo-busting reload. Thanks for that.
  • Bonus: Totals#excluded_kinds_sql switching from raw interpolation to connection.quote is a nice hardening touch.

Two small leftovers inline, neither blocking: the dead local from my last review survived, and the non-English locales now describe the old behavior. Worth a final cosmetic commit, but I'm approving as is.

Comment on lines 33 to 34
# Memoized per instance so callers that also invoke `net_category_totals`
key = period_cache_key(period)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover from my last review: key is assigned but never used (the memo builds its own cache_key below), and the comment above it ends mid-sentence.

Suggested change
# Memoized per instance so callers that also invoke `net_category_totals`
key = period_cache_key(period)
# Memoized per instance so repeat callers (e.g. net_category_totals) reuse the same build

activity_type_description: Type of investment activity (Buy, Sell, Dividend, etc.). Auto-detected or set manually.
one_time_title: One-time %{type}
one_time_description: One-time transactions will be excluded from certain budgeting calculations and reports to help you see what's really important.
one_time_description: One-time transactions appear in historical reports and totals but are excluded from budget medians and recurring spending estimates.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The English copy is now correct, but the other locales (es, ca, de, hu, pl, and friends) still say one-time transactions are "excluded from certain budgeting calculations and reports", which describes the pre-PR behavior and is now wrong for non-English users. Worth updating them in the same pass, per the repo's i18n guideline of keeping locale files in sync with changed copy.

Signed-off-by: glorydavid03023 <glorydavid03023@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

Excluded versus one-time transactions

4 participants