# Budget Import Upload UX & Candidate Count Consistency

Use this reference when the user asks to replace Google-Drive-first Budget imports with dashboard file upload, or when Budget Import / Buchungen prüfen / Command Center candidate counts disagree.

## User workflow

Primary User Mode workflow is:

1. Upload CSV in the dashboard.
2. Auto-detect import profile or let the user choose one.
3. Show Dry Run summary only.
4. Create review candidates only after explicit user action.
5. Open Buchungen prüfen filtered by import session/source/year/status.
6. Confirm remains a separate Preview → Confirm → Audit action.

Supported upload profiles for this class of Budget import work:

- VISA CSV
- Migros CSV
- Raiffeisen CSV
- AKB CSV

Do not add new import sources, OCR/camera, Excel budget-tool import, portfolio import, or automatic mass booking in this workflow unless the user explicitly opens a separate sprint.

## Google Drive workflow placement

Google Drive scanning may remain as Admin/Debug/Fallback, but not as the normal User Mode path. User-facing copy should say something like:

- `Budget Import`
- `Datei hochladen → Dry Run → Kandidaten prüfen → Confirm`

Avoid prominent User Mode language like `Google-Drive-CSV Workflow: Scan → Dry Run → Kandidaten → Review → Confirm → Audit`.

## Count policy: one truth

Define and reuse one function/service for user-relevant candidate counts. Budget Übersicht, Budget Import, Buchungen prüfen, Command Center and Data Quality hints must not each hand-roll their own SQL/status filters.

Normal user-open count includes candidates that require user decision, e.g.:

- `pending`
- `auto_categorized`
- `needs_review`
- `transfer_candidate`
- income-like open candidates if represented in the current schema (`possible_income`, `income_candidate`)

Count separately, not in normal open total:

- `duplicate` / `duplicate_candidate` / `possible_duplicate`
- `covered_by_source` / `covered_by_migros`
- `already_processed`
- `auto_ignored_duplicate`

Exclude from User Mode open KPIs:

- `confirmed`
- `ignored`
- `superseded`
- archived/reference/old-year statuses such as `archived_reference`, `reference_2025`
- old-year candidates when the active view is a different budget year
- technical import line items/detail rows

If the dashboard shows numbers such as Budget Import open candidates ≫ Buchungen prüfen open total, first inspect whether legacy, superseded, covered, duplicate, already-processed, import-line, or prior-year rows are being counted by one surface but not another.

## Upload endpoint/service semantics

A clean implementation can expose or adapt these endpoints:

- `POST /api/budget/import/upload/preview`
- `POST /api/budget/import/upload/confirm-candidates`
- `GET /api/budget/import/sessions`
- `GET /api/budget/import/sessions/{id}`
- `GET /api/budget/transaction-candidates?import_session_id=...&source_type=...&year=...&status=open`

Safety requirements:

- CSV only, with a bounded file size.
- Known encodings only, e.g. UTF-8 BOM, CP1252, Latin-1.
- Do not log secret-bearing filenames or raw rows.
- Store temporary/archive files only under runtime, never under the repo.
- Use source hash/fingerprint for idempotency.
- Same file upload must not create duplicate candidates.
- Upload/preview/confirm-candidates must not create productive budget transactions.

Dry-run summaries should be aggregate-only and avoid amounts in reports/chat:

- file/profile detected
- period
- rows total
- new candidates
- already processed
- possible/safe duplicates
- invalid rows
- transfer/income/expense/covered counts

## Review deep link

After candidate creation, build a direct Review link with query filters such as:

```text
/planning/budget/expenses/review?import_session_id=<session>&source_type=<profile>&year=2026&budget_year=2026&status=open
```

Ensure the central URL-filter composable/schema accepts `import_session_id`, and Review translates `status=open` to the shared open-count policy rather than a raw DB status.

## UI help text

Add compact help/tooltip text for potentially confusing counts:

- Offen total: candidates that still need a user decision.
- Review nötig: candidates without a safe category/type.
- Duplikate: possible duplicates, reviewed separately and not normally confirmable.
- Covered by Source: e.g. credit-card lines covered by a more authoritative source CSV; not counted as expense.
- Bereits verarbeitet: already confirmed/ignored/fingerprinted and not part of normal open work.

## Verification checklist

Minimum gate:

- Backend tests for each supported profile preview.
- Unknown CSV returns a clear error.
- Reupload/idempotency test.
- Count-policy tests excluding confirmed/ignored/superseded/covered/old-year rows.
- UI tests for upload page, dry-run aggregate display, create-candidates button and Review link.
- Test that Google Drive import copy is no longer prominent in User Mode.
- Python compile, full pytest, frontend tests/build, source/build secret scans, Git-safety, browser sanity for Import/Review/Overview/Command Center, `git diff --check`, commit, push, remote hash.

## Pitfall

Do not validate only the new Import page. Command Center/monthly-close cards often have their own legacy SQL. Browser-sanity Command Center and compare its candidate number to the canonical summary endpoint before declaring count consistency.
