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

@@ -136,13 +136,22 @@ class Balance::ChartSeriesBuilder
LIMIT 1
) last_bal ON TRUE
LEFT JOIN LATERAL (
SELECT er.rate
FROM exchange_rates er
WHERE er.from_currency = accounts.currency
AND er.to_currency = :target_currency
AND er.date <= d.date
ORDER BY er.date DESC
LIMIT 1
SELECT COALESCE(
(SELECT er.rate
FROM exchange_rates er
WHERE er.from_currency = accounts.currency
AND er.to_currency = :target_currency
AND er.date <= d.date
ORDER BY er.date DESC
LIMIT 1),
(SELECT er.rate
FROM exchange_rates er
WHERE er.from_currency = accounts.currency
AND er.to_currency = :target_currency
AND er.date > d.date
ORDER BY er.date ASC
LIMIT 1)
) AS rate
) er ON TRUE
WHERE accounts.id = ANY(array[:account_ids]::uuid[])
GROUP BY d.date