## Summary
- Adds `Savings` to the list of account types that support invested
amount tracking, alongside `Investment` and `Retirement`
- Decouples `supportsInvestedAmount()` from
`NON_TRANSACTIONAL_ACCOUNT_TYPES` in TypeScript, since savings accounts
are transactional but now also support invested amount tracking
- Updates unit tests to reflect savings accounts supporting invested
amounts
This enables remunerated savings accounts to track invested money vs.
current balance to visualize gains/losses — the same way investment and
pension accounts already do.
## Why
Investment and retirement accounts show balance over time, but there's
no way to see how much money was actually put in versus how much is
current value. Users can't tell at a glance whether their investments
are up or down.
## What
Adds an "invested amount" tracking system across the full stack:
**Backend**
- New `invested_amount` column on `account_balances` (nullable
bigInteger, cents, per-date)
- Auto-sync from providers: Indexa Capital (instruments_cost +
cash_amount), Bitpanda (fiat deposit/withdrawal history), Binance
(90-day windowed deposit/withdrawal with crypto→fiat conversion)
- Manual input support via Update Balance dialog
- Historical invested amount data in all balance evolution APIs (net
worth, account detail)
**Frontend**
- Dashed line on sparkline charts (dashboard + accounts page) showing
per-point historical invested amount alongside balance
- Dashed line on account detail charts (daily AreaChart + monthly
ComposedChart)
- Tooltips with labeled rows: Balance, Invested, Gain/loss (color-coded)
- Invested amount column in balances history modal
- Invested amount field in balance import wizard (CSV mapping)
- Demo account seeder updated with invested amount data
## Screenshots
<img width="1301" height="750" alt="image"
src="https://github.com/user-attachments/assets/0f05ecd0-8b98-47b4-9fa4-027f0311e3bb"
/>
<img width="744" height="374" alt="image"
src="https://github.com/user-attachments/assets/c4daa816-dee0-4f94-957f-317a13bc80d5"
/>
<img width="1267" height="738" alt="image"
src="https://github.com/user-attachments/assets/21df350c-6954-4ff5-8b3c-b858df3a8b3a"
/>
<img width="1301" height="828" alt="image"
src="https://github.com/user-attachments/assets/16f5f021-a926-4e8e-a999-c4ca32d1ea3d"
/>
<img width="1274" height="845" alt="image"
src="https://github.com/user-attachments/assets/62f2dfc0-04f0-4bdb-b072-cf7cd1be77d3"
/>
## 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"
/>
## Summary
Adds daily granularity support to the dashboard Net Worth Evolution
chart, mirroring the daily balance feature added for individual account
pages in #135.
## Why
The net worth chart only supported monthly granularity, making it
impossible to see short-term net worth fluctuations. Users who track
daily balances need a way to visualize day-over-day net worth changes
across all accounts.
## Screenshots
<img width="1263" height="714" alt="image"
src="https://github.com/user-attachments/assets/646e7e21-5c1b-42dc-9a20-5e9b6d5d72b4"
/>
<img width="1261" height="715" alt="image"
src="https://github.com/user-attachments/assets/b9fe5080-55f3-485a-a566-1d25d83518c7"
/>
## Summary
- Adds a **Monthly / Daily granularity toggle** to the account balance
evolution chart on account pages
- Daily view renders as an **area chart** (line with gradient fill)
showing the last 30 days, while monthly keeps the existing bar chart
- All 3 chart view modes (stacked, period-over-period,
period-over-period %) work in both granularities
- Chart view toggle **tooltips update dynamically** based on granularity
(e.g. "Month over month change" → "Day over day change")
- Backend fills gaps in sparse `account_balances` data by carrying
forward the last known balance day-by-day
- Includes 3 new Pest tests for the daily endpoint (data points,
gap-filling, forbidden access)
## Screenshot
<img width="1271" height="743" alt="image"
src="https://github.com/user-attachments/assets/cca7a7ad-bdcc-45d2-a5e1-47b1cb855d49"
/>
<img width="1274" height="755" alt="image"
src="https://github.com/user-attachments/assets/14e1f2d8-41cd-49cc-b0f4-307edb847af8"
/>
## Summary
- Replace `subMonths()` with `subMonthsNoOverflow()` across controllers,
commands, and tests to prevent date overflow on months with 31 days
- On Jan 31, `subMonths(2)` overflows: Nov 31 doesn't exist → Carbon
rolls forward to Dec 1, producing wrong date ranges and off-by-one month
counts
## Root cause
Carbon's `subMonths()` allows day overflow. When the target month has
fewer days than the source date, it rolls into the next month. For
example:
- `2026-01-31 subMonths(2)` → Nov 31 → **2025-12-01** (expected Nov 30)
- `2026-01-31 subMonths(11)` → Feb 31 → **2025-03-03** (expected Feb 28)
`subMonthsNoOverflow()` clamps to the last valid day of the target month
instead.
## Files changed
- `app/Http/Controllers/Api/CashflowAnalyticsController.php` — cashflow
trend start date calculation
- `app/Console/Commands/ResetDemoAccountCommand.php` — balance history
date generation (was causing duplicate `balance_date` entries)
- `tests/Feature/AccountControllerTest.php` — balance evolution `from`
param
- `tests/Feature/CashflowAnalyticsTest.php` — transaction date
generation in loop
- `tests/Feature/DashboardAnalyticsTest.php` — net worth evolution
`from` param
## Test plan
- [x] `account balance evolution returns data` — expects 3 months, was
getting 2
- [x] `cashflow trend returns monthly data` — expects 3 months, was
getting 2
- [x] `cashflow trend defaults to 12 months` — expects 12 months, was
getting 11
- [x] `net worth evolution returns monthly data points` — expects 3
months, was getting 2
- [x] `demo:reset creates demo user` — was throwing
UniqueConstraintViolation on duplicate balance dates
* Improve net worth chart
* feat: Add current net worth display to dashboard chart
* Handle different currencies
* Add account balance sync service and update SyncProvider
* Remove redundant transaction filters in DashboardAnalyticsController
- Create DashboardAnalyticsController with 6 analytics endpoints:
- Net worth calculation and comparison
- Monthly spending with period comparison
- Cash flow (income vs expenses)
- Net worth evolution over time
- Account balance history
- Top spending categories
- Add PeriodComparator service for date range comparisons
- Update DashboardController to pass categories, accounts, and banks
- Add comprehensive feature tests for all analytics endpoints
- Register analytics routes under /api/dashboard