Fix foreign currency accounts using wrong exchange rate in balance sheet totals (#1010)

Balance sheet totals and accountable type summaries used a SQL JOIN on
exchange_rates matching only today's date, which returned NULL (defaulting
to 1:1) when no rate existed for that exact date. This caused foreign
currency accounts to show incorrect totals.

Changes:
- Refactor BalanceSheet::AccountTotals to batch-fetch exchange rates via
  ExchangeRate.rates_for, with provider fallback, instead of a SQL join
- Refactor Accountable.balance_money to use the same batch approach
- Add ExchangeRate.rates_for helper for deduplicated rate lookups
- Fix net worth chart query to fall back to the nearest future rate when
  no historical rate exists for a given date
- Add composite index on accounts (family_id, status, accountable_type)
- Reuse nearest cached exchange rate within a 5-day lookback window
  before calling the provider, preventing redundant API calls on
  weekends and holidays when providers return prior-day rates

https://claude.ai/code/session_01GyssBJxQqdWnuYofQRjUu8

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Mikael
2026-02-20 02:07:47 +09:00
committed by GitHub
parent a63e1c5a89
commit cc20e2c19c
8 changed files with 132 additions and 37 deletions

View File

@@ -0,0 +1,9 @@
class AddCompositeIndexOnAccountsFamilyStatusType < ActiveRecord::Migration[7.2]
disable_ddl_transaction!
def change
add_index :accounts, [ :family_id, :status, :accountable_type ],
name: "index_accounts_on_family_id_status_accountable_type",
algorithm: :concurrently
end
end

3
db/schema.rb generated
View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2026_02_11_120001) do
ActiveRecord::Schema[7.2].define(version: 2026_02_17_120000) do
# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
enable_extension "plpgsql"
@@ -56,6 +56,7 @@ ActiveRecord::Schema[7.2].define(version: 2026_02_11_120001) do
t.index ["currency"], name: "index_accounts_on_currency"
t.index ["family_id", "accountable_type"], name: "index_accounts_on_family_id_and_accountable_type"
t.index ["family_id", "id"], name: "index_accounts_on_family_id_and_id"
t.index ["family_id", "status", "accountable_type"], name: "index_accounts_on_family_id_status_accountable_type"
t.index ["family_id", "status"], name: "index_accounts_on_family_id_and_status"
t.index ["family_id"], name: "index_accounts_on_family_id"
t.index ["import_id"], name: "index_accounts_on_import_id"