whisper-money/app
Víctor Falcón 952ca41eb1
refactor(analytics): dedupe the two account balance evolution endpoints (#766)
## Why

We just landed the CRAP and DRY checks. `DashboardAnalyticsController`
was the worst offender after the trial-experiment code (which #762
deletes anyway):

- `accountBalanceEvolution` — cyclomatic complexity **30**
- `accountDailyBalanceEvolution` — cyclomatic complexity **12**

And the two were near-identical copies of each other, so it was a
duplication problem wearing a complexity costume.

## What changed

The monthly and daily series shared everything except the dates they
iterate and the projections the monthly one appends: authorization,
range validation, per-point assembly (balance, invested amount, linked
mortgage, display-currency equivalents) and the response envelope. Those
are now shared methods:

| new method | what it owns |
|---|---|
| `authorizedDateRange` | 403 guard + `from`/`to` validation + parsing |
| `displayCurrencyFor` | the user currency, or null when it matches the
account |
| `balanceLookupFor` | the `BalanceLookup` over the account and its
linked loan |
| `balancePointAt` | one point: value, invested amount, mortgage,
display fields |
| `projectedPoints` → `loanProjection` / `realEstateProjection` /
`projectedPoint` | the future months |
| `evolutionResponse` | the `data` + `account` + `display_currency_code`
envelope |

Also collapsed the three byte-identical "sum transactions joined to a
category of type X" queries in `calculateSpending`/`calculateCashFlow`
behind `sumByCategoryType`.

## Behaviour

No change. One thing worth naming: the daily series used to read the
balance at **start** of day and the invested amount at **end** of day
(`endOfDay()` mutates the Carbon instance mid-method). It now reads both
at end of day — same result, because `BalanceLookup` compares
`toDateString()`, i.e. by day.

## Metrics

| | before | after |
|---|---|---|
| methods over complexity 10 | 35 | 33 |
| `accountBalanceEvolution` | 30 | 4 |
| `accountDailyBalanceEvolution` | 12 | 4 |
| duplicated lines (php) | 5.92% | 5.68% |
| duplicated lines (total) | 5.34% | 5.25% |

## Testing

`DashboardAnalyticsTest` (41), `LoanTest` + `AccountControllerTest` (68)
— all green, unchanged. Between them they cover both projection paths,
both currency-conversion paths and the mortgage overlay, so the refactor
is verified by the existing suite; no new tests were needed.

## Noticed, not changed

`display_invested_amount` converts **from** the user currency **to** the
account currency, while every sibling field converts the other way.
Looks like an inverted argument pair, but fixing it changes numbers on
the invested-amount chart, so it stays out of a no-behaviour-change PR.
Worth a follow-up.
2026-08-11 14:36:52 +02:00
..
Actions fix(auth): make build deterministic when REGISTRATION_ENABLED=false (#720) 2026-07-22 07:04:20 +00:00
Ai/Agents feat(stats): post the Discord stats reports in Spanish, opened by an AI summary (#752) 2026-08-10 10:13:40 +02:00
Console/Commands ci: add duplication and complexity quality checks (#765) 2026-08-11 13:29:37 +02:00
Contracts feat(connections): manage which accounts a bank connection syncs (#558) 2026-06-18 16:22:49 +02:00
Enums feat(transactions): add a monthly trend view to the analysis drawer (#736) 2026-07-26 17:03:52 +02:00
Events Add Budgeting Feature to Track and Manage Spending (#36) 2026-01-21 15:25:50 +01:00
Exceptions/Banking fix(banking): stop unclassified bank responses from silently killing a connection (#742) 2026-08-09 17:26:34 +02:00
Features feat(mcp): read-only MCP server for Pro accounts (#689) 2026-07-17 16:54:15 +02:00
Http refactor(analytics): dedupe the two account balance evolution endpoints (#766) 2026-08-11 14:36:52 +02:00
Jobs fix(banking): stop Wise outages from silently parking a bank connection (#757) 2026-08-10 16:40:09 +02:00
Listeners feat: reuse the upgrade modal at more upsell points and attribute revenue (#699) 2026-07-18 12:53:20 +00:00
Mail feat(budgets): add per-budget email notifications (#731) 2026-07-24 12:52:03 +02:00
Mcp refactor(mcp): dedupe record resolution and field updates in the write tools (#767) 2026-08-11 14:35:01 +02:00
Models feat(mcp): record MCP tool usage and report it with stats:mcp-usage (#760) 2026-08-11 10:27:32 +02:00
Notifications refactor: remove HIDE_AUTH_BUTTONS launch gate and waitlist apparatus (#717) 2026-07-22 08:51:48 +02:00
Policies refactor(policies): extract HandlesUserOwnership trait (#478) 2026-06-03 17:43:30 +02:00
Providers fix(auth): make build deterministic when REGISTRATION_ENABLED=false (#720) 2026-07-22 07:04:20 +00:00
Rules feat: parent/child category tree (#474) 2026-06-03 19:30:12 +02:00
Services fix(binance): value past days from Binance's own snapshot total (#763) 2026-08-11 11:04:06 +00:00
Support ci: add duplication and complexity quality checks (#765) 2026-08-11 13:29:37 +02:00