* feat(balance): incremental ForwardCalculator — only recalculate from changed date forward
When a Sync record carries a window_start_date, ForwardCalculator now
seeds its starting balances from the persisted DB balance for
window_start_date - 1, then iterates only from window_start_date to
calc_end_date. This avoids recomputing every daily balance on a
long-lived account when a single transaction changes.
Key changes:
- Account::Syncer passes sync.window_start_date to Balance::Materializer
- Balance::Materializer accepts window_start_date and forwards it to
ForwardCalculator; purge_stale_balances uses opening_anchor_date as the
lower bound in incremental mode so pre-window balances are not deleted
- Balance::ForwardCalculator accepts window_start_date; resolve_starting_balances
loads end_cash_balance/end_non_cash_balance from the prior DB record and
falls back to full recalculation when no prior record exists
- Tests added for incremental correctness, fallback behaviour, and purge safety
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
# Conflicts:
# app/models/balance/materializer.rb
* Enhance fallback logic on ForwardCalculator and Materializer
* fix(balance): address CodeRabbit review issues on incremental ForwardCalculator
- materializer.rb: handle empty sorted_balances in incremental mode by still
purging stale tail balances beyond window_start_date - 1, preventing orphaned
future rows when a transaction is deleted and the recalc window produces no rows
- materializer_test.rb: stub incremental? alongside calculate in the incremental
sync test so the guard in ForwardCalculator#incremental? doesn't raise when
@fell_back is nil (never set because calculate was stubbed out)
- materializer_test.rb: correct window_start_date in the fallback test from
3.days.ago to 2.days.ago so window_start_date - 1 hits a date with no
persisted balance, correctly triggering full recalculation instead of
accidentally seeding from the stale wrong_pre_window balance
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(balance): multi-currency fallback to full recalculation and add corresponding tests
* address coderabbit comment about test
* Make the foreign-currency precondition explicit in the test setup.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
* Fix linked account balance currency mismatch
When linking accounts from providers (Lunchflow, SimpleFIN, Enable Banking),
the initial sync was creating balances before the correct currency was known.
This caused:
1. Opening anchor entry created with default currency (USD/EUR)
2. First sync created balances with wrong currency
3. Later syncs created balances with correct currency
4. Both currency balances existed, charts showed wrong (zero) values
Changes:
- Add `skip_initial_sync` parameter to `Account.create_and_sync`
- Skip initial sync for linked accounts (provider sync handles it)
- Add currency filter to ChartSeriesBuilder query to only fetch
balances matching the account's current currency
* Add migration script and add tests
* Update schema.rb
---------
Signed-off-by: soky srm <sokysrm@gmail.com>
Co-authored-by: sokie <sokysrm@gmail.com>
* Balance reconcilations with new components
* Fix materializer and test assumptions
* Fix investment valuation calculations and recon display
* Lint fixes
* Balance series uses new component fields