# Vue/FastAPI functional dashboard actions: Preview → Confirm → Audit

Use this reference for FinanceManager sprints that turn previously read-only or decorative Vue dashboard controls into real user actions, especially manual Position/Cash/Crypto/Equity/Budget flows.

## Scope guard

- Keep provider/import/OCR/rule-engine/trading expansion out of scope unless explicitly requested.
- Normal dashboard render must read local FastAPI/runtime DTOs only; external provider calls remain explicit-click/CLI/schedule.
- Every visible write button must either:
  - perform a real Preview → Confirm → Audit flow,
  - route to an existing real flow with context prefilled, or
  - be disabled with a plain reason.
- Do not ship fake-enabled controls. If a domain action lacks a safe backend write path, leave it disabled.
- Do not commit runtime DBs, generated frontend build output, real financial data, API keys, token-bearing remotes, or provider secrets.

## Backend pattern

1. Add typed request/response DTOs first.
2. Add `/preview` endpoint that performs validation and returns user-readable summary/warnings without writes.
3. Add `/confirm` endpoint that requires:
   - `preview_id`,
   - `confirm=true`,
   - non-empty note when adjustment/removal/storno semantics need provenance.
4. Confirm writes the domain event/ledger row and an `audit_log` row in the same transaction scope.
5. Add regression tests proving preview is no-write and confirm writes exactly the intended rows plus audit.
6. Keep Decimal values as strings at API boundaries when exactness matters; parse with Python `Decimal`, not float.

## Cash action pitfalls

- UI should send a positive amount for withdrawals; backend transaction type decides withdrawal semantics. Do not send a negative amount to a validator that requires positive decimals.
- Map German labels explicitly:
  - `Einzahlung` → `cash_deposit`
  - `Auszahlung` → `cash_withdrawal`
  - `Korrektur` → `manual_cash_correction`
- Cash compatibility/control rows may have uniqueness constraints by account/date/currency/source. If multiple same-day manual cash actions are allowed, use a unique source/source_id for each compatibility snapshot write; the authoritative transaction/audit must still be recorded.

## Crypto action pitfalls

- Crypto `set_zero` / `Auf 0 setzen` must allow quantity `0` in preview/confirm; generic positive-decimal validators will reject it.
- Keep crypto quantities and fees as exact Decimal/TEXT strings; never float.
- For crypto dashboard actions, support MVP operations through audited `crypto_transactions`/adjustments rather than direct silent `crypto_holdings` mutation:
  - increase,
  - decrease,
  - correction,
  - set zero,
  - transfer.
- Crypto transfer requires:
  - different source and target wallets,
  - quantity > 0,
  - fee >= 0,
  - source balance sufficient for `quantity + fee`,
  - source wallet reduced by `quantity + fee`,
  - target wallet increased by `quantity`,
  - total asset quantity reduced only by fee,
  - no buy/sell/P&L semantics.
- Store transfer provenance in a single auditable event/transaction if that is the existing model; tests should assert source/target wallet fields, quantity, fee, resulting wallet allocation, and audit.

## Equity/ETF sell and dividend MVP

### Sell

- Sell must use Preview → Confirm → Audit and reject quantity <= 0 or selling more than current quantity.
- Use Weighted Average Cost when cost basis is available:
  - proportional cost removed = average cost per unit × sold quantity,
  - realized P&L = net proceeds - proportional cost removed,
  - full sell leaves quantity 0 and keeps the position historically visible or archived by the existing model.
- If a cash account is provided, book net cash proceeds with a unique source/source_id so same-day multiple sells do not collide in cash snapshot/control tables.
- Fees reduce net proceeds; do not double-count fees if the UI already sends net proceeds rather than gross/fee.

### Dividend / distribution

- Dividend must create an income/dividend transaction and cash net amount when a cash account is provided.
- Quantity/cost basis stay unchanged.
- Capture gross, currency, Swiss withholding tax, foreign withholding tax, other deductions, net amount, payment date, account and note as supported by the existing model. If the model lacks separate tax tables, persist tax detail in transaction metadata/notes/audit rather than inventing a new tax subsystem during an action sprint.
- FX handling should follow the existing preview semantics: resolve when available, otherwise surface a warning/status rather than inventing a rate.

## Vue implementation pattern

- Keep API clients thin and typed (`previewX`, `confirmX`).
- UI forms should show Preview output before Confirm is enabled.
- Show audit ID/status after Confirm.
- After Confirm, reload the relevant read model/detail drawer.
- Use asset-specific transaction-type options. Do not show Cash options in Crypto mode or Equity options in Cash mode.
- `Kaufen / Erhöhen` may be either a compact drawer form or a route into the existing Position Wizard, but if routed it must pass enough context (`instrument_id`, `account_id`, asset type) so the user does not search again.
- Inline Konto/Wallet creation inside the Position Wizard should call the existing Preview/Confirm/Audit account/wallet endpoints, then refresh the dropdown and select the newly created entity.
- PrimeVue `Toast` and `ConfirmDialog` can be registered centrally in `main.ts`/`App.vue`; however, do not claim every action has global toast semantics until each action actually calls the service. Inline visible success/error/audit feedback is acceptable when global service migration is partial.
- For Tailscale/iPhone browser use, FastAPI CORS must include the browser-visible frontend origin, e.g. `http://<tailscale-ip>:5173` and tailnet hostnames when used.

## Budget Review UAT pattern

- Verify `Buchungen prüfen` through both backend tests and browser sanity:
  - Einzelconfirm,
  - Batch Confirm Preview,
  - Batch Confirm,
  - Kategorie ändern,
  - Ignore,
  - Reopen,
  - Split Preview/Confirm when supported.
- If the runtime has no open candidates, report that browser UAT was route/control sanity plus test coverage, not a real live confirm.
- Keep real batch confirm/mass confirm behind current-turn user approval; tests should use synthetic/test DB data.

## Verification checklist

- Targeted backend tests for each new action.
- Full backend pytest and Python compile.
- Targeted frontend tests for changed pages.
- Full frontend vitest.
- Frontend build.
- `git diff --check`.
- Git-safety test.
- Diff/staged secret scan.
- Frontend build secret scan with real secret patterns (e.g. `sk-[A-Za-z0-9]{20,}`), not naive substrings that match normal CSS/JS words like `mask-`.
- Runtime DB-in-Git check.
- Restart backend/frontend from the current checkout, kill stale listeners first if needed.
- Browser/Tailscale sanity for affected routes.
- Browser console clean on changed pages.
- Click/open representative drawer/action controls and verify they are visible and not stale.
- Commit, push, and verify remote hash with authenticated `ls-remote` if normal fetch cannot authenticate. Never print or store token-bearing remotes.
