mirror of
https://github.com/we-promise/sure.git
synced 2026-07-20 08:45:22 +00:00
perf(reports): avoid residual category lazy loads (#2255)
* perf(reports): avoid residual category lazy loads * test(reports): reuse SQL query capture helper Move the reports SQL capture regression helper into test/support and document the category loading and budget reload choices called out in review.
This commit is contained in:
@@ -90,7 +90,9 @@ class Budget < ApplicationRecord
|
||||
end
|
||||
|
||||
def sync_budget_categories
|
||||
current_category_ids = family.categories.pluck(:id).to_set
|
||||
# Category changes can leave the association memoized before this sync runs.
|
||||
current_categories_by_id = family.categories.reload.index_by(&:id)
|
||||
current_category_ids = current_categories_by_id.keys.to_set
|
||||
existing_budget_category_ids = budget_categories.pluck(:category_id).to_set
|
||||
categories_to_add = current_category_ids - existing_budget_category_ids
|
||||
categories_to_remove = existing_budget_category_ids - current_category_ids
|
||||
@@ -98,7 +100,7 @@ class Budget < ApplicationRecord
|
||||
# Create missing categories
|
||||
categories_to_add.each do |category_id|
|
||||
budget_categories.create!(
|
||||
category_id: category_id,
|
||||
category: current_categories_by_id.fetch(category_id),
|
||||
budgeted_spending: 0,
|
||||
currency: family.currency
|
||||
)
|
||||
|
||||
@@ -134,7 +134,8 @@ class IncomeStatement
|
||||
NetCategoryTotals = Data.define(:net_expense_categories, :net_income_categories, :total_net_expense, :total_net_income, :currency)
|
||||
|
||||
def categories
|
||||
@categories ||= family.categories.all.to_a
|
||||
# Keep Category#subcategory?'s parent-based orphan semantics without lazy loads.
|
||||
@categories ||= family.categories.includes(:parent).to_a
|
||||
end
|
||||
|
||||
def period_cache_key(period)
|
||||
|
||||
@@ -193,6 +193,40 @@ class ReportsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_select ".text-center.py-8.text-subdued", { text: /No spending data/, count: 0 }, "Should not show 'No spending data' message when transactions exist"
|
||||
end
|
||||
|
||||
test "index avoids residual category lazy loads" do
|
||||
account = accounts(:depository)
|
||||
|
||||
4.times do |idx|
|
||||
parent = @family.categories.create!(
|
||||
name: "Reports Parent #{idx}",
|
||||
color: "#000000",
|
||||
lucide_icon: "folder"
|
||||
)
|
||||
child = @family.categories.create!(
|
||||
name: "Reports Child #{idx}",
|
||||
color: "#111111",
|
||||
lucide_icon: "folder",
|
||||
parent: parent
|
||||
)
|
||||
entry = account.entries.create!(
|
||||
name: "Reports transaction #{idx}",
|
||||
date: Date.current,
|
||||
amount: 10 + idx,
|
||||
currency: "USD",
|
||||
entryable: Transaction.new(
|
||||
category: child,
|
||||
kind: "standard"
|
||||
)
|
||||
)
|
||||
assert entry.persisted?
|
||||
end
|
||||
|
||||
queries = capture_sql_queries { get reports_path(period_type: :monthly) }
|
||||
|
||||
assert_response :ok
|
||||
assert_empty queries.grep(/SELECT "categories"\.\* FROM "categories" WHERE "categories"\."id" = \$1 LIMIT \$2/)
|
||||
end
|
||||
|
||||
test "export transactions with API key authentication" do
|
||||
# Use an active API key with read permissions
|
||||
api_key = api_keys(:active_key)
|
||||
|
||||
@@ -39,6 +39,17 @@ class IncomeStatementTest < ActiveSupport::TestCase
|
||||
assert_equal expected_total_expense, expense_totals.category_totals.find { |ct| ct.category.id == @food_category.id }.total
|
||||
end
|
||||
|
||||
test "orphaned categories still count as root category totals" do
|
||||
orphan_category = @family.categories.create! name: "Orphaned Category"
|
||||
orphan_category.update_column(:parent_id, SecureRandom.uuid)
|
||||
create_transaction(account: @checking_account, amount: 123, category: orphan_category)
|
||||
|
||||
expense_totals = IncomeStatement.new(@family).expense_totals(period: Period.last_30_days)
|
||||
|
||||
assert_equal 200 + 300 + 400 + 123, expense_totals.total
|
||||
assert_equal 123, expense_totals.category_totals.find { |ct| ct.category.id == orphan_category.id }.total
|
||||
end
|
||||
|
||||
test "memoizes expense and income period totals across repeated calculations" do
|
||||
income_statement = IncomeStatement.new(@family)
|
||||
period = Period.last_30_days
|
||||
|
||||
Reference in New Issue
Block a user