Fix contribution compounding: monthly contributions are now added to
asset values before each GBM step so they grow with market returns,
rather than being summed as a static lump at each period.
Add year-by-year milestone table below the fan chart showing P10/P50/P90
portfolio values at each annual checkpoint up to the selected horizon.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces unused Prophet dependency (unrunnable without cmdstan) with
SARIMA (statsmodels SARIMAX) as the primary spending forecast algorithm.
Strategy: SARIMA(1,1,1)(1,0,1,12) for 12+ months of data, ARIMA(1,1,1)
for 6-11 months, Holt-Winters for 3-5 months, simple average below that.
Adds 95% confidence bands (1.96σ) alongside existing 80% (1.28σ).
Extends forecast horizon from 3 to 6 months and actuals display from
6 to 12 months. Each category now carries an algorithm field surfaced
as a badge in the UI. Frontend chart shows both confidence tiers as
stacked bar overlays with a 3-month summary grid below.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 1 — Budget Forecast: adds a dedicated first-class tab showing all
monthly budgets with velocity (£/day), forecast month-end total, dual
progress bars, and colour-coded overspend probability badges. Summary
bar shows budgets tracked / at-risk count / total forecast overspend.
Removes the old BudgetAlerts widget embedded in the Spending tab.
Phase 2 — Cash Flow: incorporates known recurring transactions into the
30-day projection (outflows hit on their predicted dates rather than
being smeared as averages), adds sqrt-of-time confidence bands to the
chart, and shows an upcoming recurring payments list with at-risk
highlighting for payments falling on or after the first negative-balance day.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a full pensions feature: SIPP/workplace DC/LISA account metadata,
contribution recording with relief-at-source/net-pay/salary-sacrifice
gross calculations, state pension tracker, annual allowance monitor,
and LISA summary. Pension contributions feed into the tax report
(RAS gross totals, allowance used). Includes two Alembic migrations,
backend service/schema/API, and full frontend pensions page with
cards for allowance, state pension, LISA, and retirement projection.
Also fixes CSRF cookie secure flag (must be false for HTTP deployments)
and extends tax schemas/service to expose pension data in the report.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The dashboard had a 'Set up 2FA' banner link to /security/totp that
bypassed the settings button guard entirely. Three fixes:
- Dashboard: hide the 2FA nudge banner completely in demo mode
- TwoFactorSetupPage: redirect to /settings on mount if isDemo, and
disable the setup query so no API call fires even briefly
- This covers both the UI entry point and direct URL navigation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
nginx only proxied /api/ — so /demo/status returned the SPA HTML,
causing useDemoMode() to always resolve to false and all demo guards
to silently fail. Add /demo/ and /health proxy blocks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
getDemoStatus was using the /api/v1 axios client, so it called
/api/v1/demo/status which 404s. The endpoint is at /demo/status with
no prefix. Switch to plain axios so useDemoMode() returns true correctly
and all demo guards (2FA, password, backups) actually work.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows the 2FA section as normal but disables both the 'Set up 2FA' and
'Disable 2FA' buttons with opacity and cursor-not-allowed, plus a small
amber note explaining why, matching the pattern used for password changes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract shared TOOLTIP_STYLE and ACTIVE_DOT to utils/chartTheme.ts so
all four chart files use one source of truth
- itemStyle now uses card-foreground (not muted-foreground) — guarantees
tooltip text is readable on all themes including Terminal, Vault, Synthwave
- cursor now uses primary at 12% opacity — always visible and thematic,
replaces near-invisible muted-foreground at 8% opacity
- activeDot is now explicit on every Line/Area — prevents Recharts default
white dot breaking dark themes
- Terminal: muted-foreground bumped 38%→55% lightness, border lightened,
warning brightened, text-shadow scoped to headings/labels/table cells
(was applying to every p and span, causing tooltip glow bleed)
- Synthwave: muted-foreground bumped 56%→68% lightness for legibility
- Vault: muted-foreground bumped 52%→60% lightness
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DEMO_MODE=true env flag: disables password changes and backup endpoints (403),
exposes GET /demo/status for frontend detection
- Auto-seed on first startup: creates demo user (demo@mymidas.app / demo123)
with 6 months of transactions, investments, budgets, subscriptions, and tax
payslips; takes a pg_dump snapshot immediately after for hourly restore
- Hourly reset: resetter Alpine container with cron restores DB from snapshot
and purges uploaded attachments every hour on the hour
- Frontend: amber demo banner on all pages, login page shows credentials,
password change disabled with notice, backups section replaced with notice
- demo/ directory: self-contained docker-compose.yml (ports 4001/8091),
.env.example, reset.sh, and step-by-step Portainer DEPLOY.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Recurring service: auto-detects direct debits/subscriptions from CSV imports
using frequency analysis; manual toggle in transaction detail drawer
- Subscriptions page (/subscriptions): groups recurring payments with monthly
cost equivalents, next-payment badges, and re-scan trigger
- UK Tax page (/tax): payslips/P60 entry, income tax + NI + CGT + dividend tax
calculations, configurable rate tables per tax year (pre-seeded 2024/25 and
2025/26), editable in-app so Budget changes need no rebuild
- Migration 0006: tax_rate_configs, tax_profiles, payslips, manual_cgt_disposals
with RLS; seeds 2025/2026 rate configs for existing users
- Chart tooltip fix: all Recharts tooltips now use TOOLTIP_STYLE constant so
they render correctly across all dark/light themes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Recharts axes: add fill to tick objects so labels are visible on dark themes
- Recharts axes: wrap stroke/gridcolor in hsl() so var() resolves to valid colour
- Chart primary lines/gradients: replace hardcoded #6366f1 with hsl(var(--primary))
so charts adopt each theme's accent (gold on Vault, green on Terminal, etc.)
- Plotly charts: add cssVar() helper (reads getComputedStyle) to pass actual
computed colour strings instead of unresolved var() references
- Budget radial gauge: use hsl(var(--destructive/success/warning)) for SVG colours
- Add --warning CSS variable to all 7 themes with per-theme appropriate values;
wire into Tailwind config as themed colour
- Replace all text-yellow-500 / text-orange-500 / bg-yellow-500 with text-warning
/ bg-warning across Dashboard, Budget, Account, Predictions, Settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Users can now create, edit, and delete custom categories from Settings → Categories. System categories (45 built-in) are shown read-only. Backend adds update_category() and delete_category() service functions; frontend has a new categories API module and a full CRUD section in SettingsPage with filter tabs, colour picker, and delete confirmation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add budget editing: updateBudget() API, edit button on budget cards,
BudgetFormModal adapted for create/update (category locked on edit)
- Remove permanently-broken POST /auth/totp/verify stub and its unused
TOTPVerifyRequest schema
- Wire getHoldingTransactions() to AssetDetail page — transaction history
table now shows above the candlestick chart, sorted newest-first
- Fix multi-currency net worth in account_service: account balances are
now converted to base_currency via ExchangeRate table before summing
- Replace silent bare pass exception handlers with logger.warning() in
transactions.py (OCR/AI pipeline) and price_feed_service.py (search)
— ValueError in date/number regex parsing left silent (control flow)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Net worth report, balance sheet, and daily snapshots now add holding
market values (falling back to cost basis) to investment-type account
balances (investment, pension, stocks_shares_isa, crypto_wallet)
- Accounts list shows total value for investment accounts with a
breakdown line ("£X cash + £Y holdings") when both are non-zero
- Add Holding modal gains a "Debit account for this purchase" toggle
that creates a matching withdrawal transaction, enabling proper cash
flow tracking for users who fund their brokerage via transfer first
- Both simple (holdings-only) and full cash-flow workflows produce
correct net worth figures without double-counting
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add four portfolio charts: allocation donut by holding, allocation
donut by asset type, cost basis vs current value bar, return % bar
- Fix asset search to use yf.Search() full-text instead of ticker-only
lookup — name searches like "vanguard ftse all world" now work
- Fix holding creation double-quantity bug: holdings now created with
quantity=0 so buy transaction is sole source of quantity/cost basis
- Add per-share / total price toggle in Add Holding modal with live
calculated equivalent shown as you type
- Add ErrorBoundary in AppShell so render errors show a message instead
of a blank page
- Fix donut charts using || instead of ?? when falling back from
current_value to cost_basis_total (0 was not falling through ??)
- Allow HoldingCreate.quantity >= 0 (was gt=0) to support zero-init
- Fix error display for Pydantic v2 array-of-objects validation errors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- OCR pipeline: Tesseract (images) + pdfplumber (PDFs) → AI text prompt →
rule-based regex fallback; works with any text model, not just vision models
- Scan Receipt toolbar button parses a photo and pre-fills the transaction form;
receipt image is automatically attached to the created transaction
- AI settings page: provider, API key (AES-256-GCM encrypted), custom URL,
model, and per-user debug toggle that gates the OCR/AI debug panel
- Fix CSRF cookie secure=False so HTTP deployments work; add 7-day max_age
- Fix attachment_refs missing from _to_response (attachments never appeared in UI)
- Fix multipart boundary lost when Content-Type was set manually in axios calls
- nginx: raise client_max_body_size to 15 MB, add 120s proxy timeout for OCR
- Migration 0005: add ai_debug boolean to users table
- Update README and CLAUDE.md with AI scanning docs and architecture notes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend returns raw AI text alongside parsed fields
- Drawer shows expandable "Raw AI response" section when result has empty fields
- Scan Receipt shows raw response inline if no fields were extracted
- Helps diagnose model output issues without needing server logs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- POST /settings/ai/test sends a minimal text prompt to verify the provider
- Returns ok/message — maps HTTP status codes to human-readable explanations
- 405 → "URL pointing at wrong place, check custom URL has no path suffix"
- 401 → invalid key, 404 → wrong path, 429 → rate limited, etc.
- Test button appears once a key is saved; result shown inline below buttons
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New backend endpoint POST /transactions/parse-receipt (file upload, no existing txn needed)
- Refactored AI call logic into shared _call_ai_parse helper (no duplication)
- Scan Receipt button in transactions toolbar → file picker → AI parse → pre-filled form
- TransactionFormModal accepts initialValues prop to pre-populate fields from receipt
- "Fields pre-filled from receipt" banner shown in form when AI-populated
- Scan error displayed inline with dismiss button
- Supports JPEG, PNG, WebP, PDF (Anthropic) or images (OpenAI)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Settings → AI: optional base URL and model name fields
- Defaults to Anthropic/OpenAI public APIs when left blank
- Custom URL enables Open WebUI, LM Studio, Ollama, and any OpenAI-compatible endpoint
- Parse endpoint uses custom base URL and model if configured
- Migration 0004: ai_base_url + ai_model columns on users
- OpenAI provider label updated to "OpenAI-compatible"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Settings → AI: configure Anthropic or OpenAI provider with encrypted API key
- Sparkle button on each attachment in transaction drawer sends image/PDF to AI
- AI extracts merchant, amount, date, description, category hint
- "Apply to transaction" button patches the transaction with parsed fields
- Anthropic supports images and PDFs; OpenAI supports images only
- API key stored AES-256-GCM encrypted in users table (migration 0003)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 3 — Investments:
- Multi-currency support: holdings track purchase currency, FX rates convert to base for totals
- Capital gains report using UK Section 104 pool method, grouped by tax year
- Capital Gains tab added to Reports page
Phase 5 — Polish & Hardening:
- Mobile-responsive layout: bottom nav, sidebar hidden on mobile, logo in TopBar, compact header buttons, hover-only actions now always visible on touch
- Backup system: encrypted GPG backups via backup.sh, nightly scheduler job, admin API (list/trigger/download/restore), Settings UI with drag-to-restore confirmation
- Docker entrypoint with gosu privilege drop to fix bind-mount ownership on fresh deployments
- OWASP fixes: refresh token now bound to its session (new refresh_token_hash column + migration), CSRF secure flag tied to environment, IP-level rate limiting on login, TOTPEnableRequest Pydantic schema replaces raw dict
- AES-256-GCM key rotation script (rotate_keys.py) with dry-run mode and atomic DB transaction
- CLAUDE.md added for AI-assisted development context
- README updated: correct reverse proxy port, accurate backup/restore commands, key rotation instructions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace invalid currency="%"/currency="" StatCard calls that caused
Intl.NumberFormat to throw RangeError and blank the page
- Render 30d change % and snapshot count as plain inline cards
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The axios client already has baseURL="/api/v1" so API calls must use
short paths (/reports/..., /budgets, /investments/...) not the full
/api/v1/... prefix. This was causing all report, budget, and investment
requests to 404.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New first-tab report showing a full breakdown of where money sits:
- Three KPI cards: total assets, total liabilities, net worth
- Proportional stacked bars showing asset and liability composition
- Side-by-side account lists grouped by type (Cash, Savings, ISAs,
Investments, Pension, Crypto vs Credit Cards, Loans, Mortgages)
- Backend endpoint GET /api/v1/reports/balance-sheet with typed schema
- Balance Sheet is now the default tab on the Reports page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>