Quick Categorize Wizard — follow-up fixes (#1393)

* Extract Entry.uncategorized_transactions scope, remove Family#uncategorized_transaction_count

Adds a single Entry.uncategorized_transactions scope containing the
shared conditions (transactions join, active accounts, category nil,
not transfer kinds, not excluded). All callers now use this scope:

- Entry.uncategorized_matching builds on it
- Transaction::Grouper::ByMerchantOrName#uncategorized_entries uses it
- categorizes_controller#uncategorized_entries_for uses it (also fixes
  missing status/excluded filters that were silently absent before)
- Both controllers replace Current.family.uncategorized_transaction_count
  with Current.accessible_entries.uncategorized_transactions.count so
  the button count and wizard count both respect account sharing

Family#uncategorized_transaction_count removed as it is now unused and
was family-scoped rather than user-scoped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Scope assign_entry write to Current.accessible_entries

Replaces unscoped Entry.where(id:) with Current.accessible_entries.where(id:)
so the write path is consistent with the find above it. Not exploitable
given the find would 404 first, but removes the pattern inconsistency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add privacy-sensitive class to amounts in categorize wizard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Extract uncategorized_count helper in CategorizesController

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix comment on uncategorized_transactions scope to mention draft accounts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Use uncategorized_count helper in assign_entry action

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikael Møller
2026-04-07 19:17:37 +08:00
committed by GitHub
parent cc8d6ca2a0
commit 5cb474d61c
8 changed files with 21 additions and 42 deletions

View File

@@ -18,7 +18,7 @@ class Transactions::CategorizesController < ApplicationController
@group = groups.first
@categories = Current.family.categories.alphabetically
@total_uncategorized = Entry.uncategorized_count(Current.accessible_entries)
@total_uncategorized = uncategorized_count
end
def create
@@ -60,7 +60,7 @@ class Transactions::CategorizesController < ApplicationController
end
streams << turbo_stream.replace("categorize_remaining",
partial: "transactions/categorizes/remaining_count",
locals: { total_uncategorized: Entry.uncategorized_count(Current.accessible_entries) })
locals: { total_uncategorized: uncategorized_count })
streams << turbo_stream.replace("categorize_group_summary",
partial: "transactions/categorizes/group_summary",
locals: { entries: remaining_entries })
@@ -98,7 +98,7 @@ class Transactions::CategorizesController < ApplicationController
all_entry_ids = Array.wrap(params[:all_entry_ids]).reject(&:blank?)
remaining_ids = all_entry_ids - [ entry.id.to_s ]
Entry.where(id: entry.id).bulk_update!({ category_id: category.id })
Current.accessible_entries.where(id: entry.id).bulk_update!({ category_id: category.id })
remaining_entries = uncategorized_entries_for(remaining_ids)
remaining_ids = remaining_entries.map { |e| e.id.to_s }
@@ -109,7 +109,7 @@ class Transactions::CategorizesController < ApplicationController
else
streams << turbo_stream.replace("categorize_remaining",
partial: "transactions/categorizes/remaining_count",
locals: { total_uncategorized: Entry.uncategorized_count(Current.accessible_entries) })
locals: { total_uncategorized: uncategorized_count })
streams << turbo_stream.replace("categorize_group_summary",
partial: "transactions/categorizes/group_summary",
locals: { entries: remaining_entries })
@@ -119,13 +119,16 @@ class Transactions::CategorizesController < ApplicationController
private
def uncategorized_count
Current.accessible_entries.uncategorized_transactions.count
end
def uncategorized_entries_for(ids)
return [] if ids.blank?
Current.accessible_entries
.excluding_split_parents
.where(id: ids)
.joins("INNER JOIN transactions ON transactions.id = entries.entryable_id AND entries.entryable_type = 'Transaction'")
.where(transactions: { category_id: nil })
.uncategorized_transactions
.to_a
end
end