# Budget Phase 1.10 — 2026 core cleanup pattern

Use this reference when the user asks to make the Budget/Cashflow module usable for a specific active budget year after import-review/category/workflow drift.

## Scope boundaries

- This is a core-correctness sprint, not a new importer or analytics expansion.
- No new import sources.
- No real CSV/XLS/XLSX/SQLite/runtime DB files in Git.
- Do not expose real amounts/IDs in chat; report counts/statuses only.
- Before any runtime mutation, create an external runtime backup and report the backup path only.
- Do not mass-confirm real candidates during implementation unless the user gives explicit current-turn approval.

## Active-year enforcement

For active budget year 2026:

- Default every budget user-facing query to `year=2026` / `budget_year=2026`:
  - Import Review candidates
  - Effective Expenses
  - Effective Income
  - Budgetstatus
  - Charts / cashflow summaries
- Keep UI year-selectable, but default to 2026.
- Hide 2025 candidates from default active review.
- Mark 2025 candidates as `reference_2025` or `archived_reference` when they are historical/planning references.
- Block confirm when `candidate.transaction_date[:4] != budget_year`.
- Include `budget_year` in single Confirm, Batch Preview, and Batch Confirm payloads.
- Make Review Summary year-aware too; otherwise the table can look fixed while summary cards still count old-year rows.

## Category cleanup pattern

Simplify category display around logical buckets derived from `category_type`; buckets do not need to be real categories.

Canonical income leaves:

- Lohn Marcel
- Lohn Melanie
- Gratifikation
- Sonstige Einnahmen
- Rückerstattungen

Canonical expense leaves:

- Essen & Haushalt
- Wohnen
- Versicherungen
- Gesundheit / Medizin
- Mobilität / Auto / Transport
- Kinder / Familie
- Freizeit / Abos / Ausflüge
- Hausrat / Möbel & Garten
- Shopping / Kleidung & Elektronik
- Haustiere
- Steuern / Behörden
- Sonstiges / Administration
- Review nötig

Cleanup service should:

- ensure canonical leaves exist and are active;
- flatten unnecessary parent categories for Budget UI purposes;
- merge `Sonstiges` into `Sonstiges / Administration`;
- archive/deactivate UAT/test/dummy categories rather than hard-deleting;
- move transactions/plans/candidates before archiving a non-empty category;
- write audit events for every merge/archive/ensure action.

Pitfall: `confirm_create_category` may normalize category_type unexpectedly for older IDs/categories. For canonical ensured categories, check by canonical ID first and insert/update directly if needed, with audit.

## Bulk review and confirm pattern

Import Review must support:

- checkbox per candidate;
- Select All for current filtered view;
- visible selected count;
- bulk category dropdown;
- category assignment;
- Preview-gated Batch Confirm;
- bulk ignore/reopen/transfer/covered actions;
- per-candidate errors surfaced in the UI.

Backend batch function should return counters plus `errors: [{candidate_id, error}]`. Do not silently skip. For category assignment, update `proposed_category_id/proposed_category_name`, clear review when appropriate, refresh UI, and audit each candidate or the batch with details.

## Budgetstatus/monthly matrix pattern

Budgetstatus rows should be by category and include:

- Budget Monat
- Budget Jahr
- Ist Jahr bisher
- monthly_actuals for `01` through `12`
- Ist Ø Monat
- Prognose Jahr
- Abweichung Jahr
- Status/Ampel

Actuals must come only from `budget_transactions.status='confirmed'`, the selected year, and relevant transaction types. Candidates never count as actuals.

Forecast:

- monthly plan: `budget_year = monthly * 12`
- annual plan: `budget_month = annual / 12`
- `actual_avg_month = actual_year_to_date / denominator`
- `forecast_year = actual_avg_month * 12`

Ampel:

- Expenses: Grün `<=90%`, Gelb `90–100%`, Rot `>100%`, Kritisch `>115%`.
- Income: Grün `>=100%`, Gelb `90–100%`, Rot `<90%`.
- With no actuals or no budget, show neutral/empty instead of fake precision.

Charts can be MVP-light:

- income/expense/net by month;
- budget vs actual/forecast by category;
- forecast overrun list;
- clear empty state when no confirmed rows exist.

## Runtime cleanup/verifications

After code/tests pass and before runtime mutations:

1. Copy runtime DB to external backup path.
2. Apply migrations.
3. Run category cleanup.
4. Archive 2025 reference candidates.
5. Archive/deactivate UAT/test/dummy/synthetic/dry-run rows from user views with audit; do not delete.
6. Verify:
   - open 2025 candidates in default review = 0;
   - bulk confirms executed = 0 unless explicitly approved;
   - default user views query 2026 only;
   - schema version expected by tests matches migration version.

## Verification gates

- `python -m compileall src tests`
- full `pytest -q`
- frontend `npm test -- --run`
- frontend `npm run build`
- `git diff --check`
- changed-file source secret scan
- frontend build secret scan using real secret-shaped patterns, not broad words like “secret” in UI copy
- changed/untracked data-artifact scan for `.csv/.xls/.xlsx/.sqlite/.sqlite3/.db`
- commit, push, and verify remote branch hash through authenticated URL when normal origin hash lookup cannot authenticate

Pitfall: Full Git scan may find already tracked synthetic fixtures. Distinguish pre-existing tracked fixtures from newly changed/untracked real data artifacts; do not stage new data files.
