whisper-money/app/Models
Víctor Falcón b743cad803
feat: Add multi-currency conversion for net worth charts (#138)
## Why

### Problem

Users with accounts in multiple currencies see raw balances on the net
worth chart without any conversion. A user with a USD-denominated
profile who also holds EUR and GBP accounts sees misleading totals — the
chart simply sums cents across currencies as if they were the same unit.

## What

### Changes

- **`ExchangeRateService`** — New service that wraps
`CurrencyConversionService` with DB-level caching. Checks the
`exchange_rates` table first, falls back to the external CDN API on
cache miss, and stores the full rate blob for future lookups.
- **`exchange_rates` migration + model** — New table with
`(base_currency, date)` unique constraint storing JSON rate blobs.
Includes factory.
- **`CurrencyConversionService`** — Made `getRatesForCurrency()` public
so `ExchangeRateService` can delegate to it.
- **`DashboardAnalyticsController`** — `netWorthEvolution` and
`netWorthDailyEvolution` endpoints now convert every account balance
from `account.currency_code` to `user.currency_code`. Response includes
`currency_code` field and `_original` data for foreign-currency
accounts.
- **Future date capping** — `ExchangeRateService::getRates()` caps any
future date to today, since the currency API only serves historical
rates. Prevents 500 errors when the chart requests end-of-month rates
for the current (incomplete) month.
- **Frontend charts** — Simplified to single-currency display. Removed
multi-currency `TotalDisplay` component; the chart header and tooltip
now show amounts in the user's currency. Foreign-currency accounts show
their original amount inline in the tooltip (e.g., "€2,000.00 →
$2,222.22").
- **`valueFormatter` type fix** — Changed return type from `string` to
`React.ReactNode` in chart component interfaces to match actual usage
with `AmountDisplay`.

## Screenshot
<img width="1266" height="649" alt="image"
src="https://github.com/user-attachments/assets/6f661840-3f65-49fe-9948-d4cfd6c6ef0e"
/>
2026-02-20 10:45:23 +01:00
..
Account.php feat: Integrate EnableBanking as open banking provider (#106) 2026-02-12 09:09:28 +01:00
AccountBalance.php Migrate ID's to UUIDv7 2025-11-15 21:25:33 +01:00
AutomationRule.php feat: add transaction labels feature (#24) 2025-12-13 13:02:19 +01:00
AutomationRuleLabel.php feat: add transaction labels feature (#24) 2025-12-13 13:02:19 +01:00
Bank.php Migrate ID's to UUIDv7 2025-11-15 21:25:33 +01:00
BankingConnection.php feat: Add Bitpanda exchange integration (#132) 2026-02-19 09:26:31 +01:00
Budget.php fix: Budget period not found on last day of period (#91) 2026-02-01 11:33:10 +01:00
BudgetPeriod.php feat: Load transactions history on budget created (#72) 2026-01-22 11:10:15 +01:00
BudgetTransaction.php Add Budgeting Feature to Track and Manage Spending (#36) 2026-01-21 15:25:50 +01:00
Category.php Add type enum to categories 2025-12-01 10:29:57 +01:00
EncryptedMessage.php Migrate ID's to UUIDv7 2025-11-15 21:25:33 +01:00
ExchangeRate.php feat: Add multi-currency conversion for net worth charts (#138) 2026-02-20 10:45:23 +01:00
Label.php feat: add transaction labels feature (#24) 2025-12-13 13:02:19 +01:00
LabelTransaction.php feat: add transaction labels feature (#24) 2025-12-13 13:02:19 +01:00
Transaction.php Move transaction filtering from client-side to server-side (#126) 2026-02-16 12:49:04 +01:00
User.php feat: Integrate EnableBanking as open banking provider (#106) 2026-02-12 09:09:28 +01:00
UserLead.php Merge pull request #1 from whisper-money/landing-page 2025-11-24 12:11:35 +01:00
UserMailLog.php feat: Send custom emails to users (#52) 2026-01-09 09:33:19 +01:00