## Ergebnis: Sprint-15-Inventur **Gebundener Stand:** Commit `7411df5ad88ab4b2bebd2ec5c7008aea18d4d7ca` **Schema:** Version **46**, Name `046_investment_performance_scope_v1` **Arbeitsbaum:** unverändert und sauber. Keine produktive DB, Rohdaten oder Secrets gelesen. ### Vorhandene, direkt wiederverwendbare Basis - **Haushaltskonten:** `budget_accounts` - Schema: `storage/migrations.py::_create_budget_phase1_tables` - Service: `services/budget_accounts.py` - Symbole: `create_budget_account_preview`, `confirm_create_budget_account`, `archive_budget_account`, `list_budget_accounts` - Optionaler Link auf das globale Portfolio-Konto über `linked_account_id`. - **Kategorien und Tags:** `budget_categories`, `budget_tags`, `budget_transaction_tags` - Service: `services/budget_categories.py` - Preview/Confirm für Anlage, Archivierung, Merge, Cleanup und Sortierung. - Kategorien haben semantische Wiederholungsbehandlung in `confirm_create_category`; Tags nicht. - **Haushaltsbuchungen:** `budget_transactions` - Service: `services/budget_transactions.py` - Symbole: `create_budget_transaction_preview`, `confirm_budget_transaction`, `reverse_budget_transaction_preview`, `confirm_reverse_budget_transaction`, `update_budget_transaction`. - Beträge/FX als Decimal-Text; Korrekturen an Beträgen müssen über Reversal/Adjustment erfolgen. - **Transfers:** - Produktive Verbindung: `budget_transfers` - Review-/Abgleichmodell: `budget_transfer_pairs` - Service: `services/transfer_pairing.py` - Kernsymbole: `propose_transfer_pairs`, `list_transfer_pairs`, `confirm_transfer_pair`, `reject_transfer_pair` - Bereits vorhanden: exakter Betrag, Gegenzeichen, gleiche Währung, Datumsfenster, verschiedene eigene Konten, Mehrdeutigkeitsblockade, atomarer Confirm, deterministische IDs, Wiederholungs-Confirm, Konkurrenzschutz und `budget_effect_chf='0'`. - **Import-Sessions und Kandidaten:** - `budget_import_sessions`, `budget_transaction_candidates`, `budget_import_line_items`, `budget_candidate_splits` - Services: - `budget_csv_imports.py` - `budget_imports.py` - `budget_import_upload.py` - `budget_monthly_import.py` - Unterstützte Profile: Visa, Migros, Raiffeisen und AKB. - Upload trennt Dry Run → Kandidatenerzeugung → separaten Buchungs-Confirm. - **Review-Backlog:** keine eigene Tabelle, sondern Projektion - `services/budget_import_status.py::build_review_backlog_dashboard` - API: `GET /api/budget/review-backlog` - Liefert Gruppen für fehlende Kategorien, Abos, Migros, Galaxus/Digitec, Banken, Duplikate, Transfers und Referenzjahr. - **Budgetpläne und Seed-Kandidaten:** - `budget_plan_items` - `budget_seed_candidates` - zusätzlich ältere `budget_excel_seed_dry_runs` und `budget_excel_seed_candidates` - Services: `budget_plans.py`, `budget_seed_review.py` - Preview/Confirm, Übernahme, Ignorieren und Wiederöffnen sind vorhanden. - **Merchant-/Kategorie-Regeln:** - `budget_review_rules`, `budget_import_rules` - `budget_merchants`, `budget_merchant_aliases` - `budget_rule_suggestions` - Kernsymbole in `budget_imports.py`: `create_budget_rule`, `test_budget_rule`, `apply_budget_rules_to_open_candidates`, `create_merchant`, `create_merchant_alias`, `apply_merchant_to_open_candidates`. - Vorschlagsworkflow in `budget_monthly_import.py`. - **API-Verträge:** `api/routers/budget.py` - Relevante Routen: - `/budget/import/upload/{preview,confirm-candidates}` - `/budget/transaction-candidates/...` - `/budget/transfer-pairs/{match,{pair_id}/confirm,{pair_id}/reject}` - `/budget/accounts/{preview,confirm}` - `/budget/plans/{preview,confirm}` - Transfer-Confirm verlangt ausdrücklich `confirm=true`. - HTTP-Schreibschutz in `api/main.py` und `api/security.py`: standardmäßig disabled, ansonsten nur Loopback/Testclient. ### Account-Erstellungspfade Neben `budget_accounts` existieren mehrere globale `accounts`-Writer: - `imports/accounts_importer.py::import_accounts_csv` - `services/manual_entry_service.py::confirm_account` - `services/cash_service.py::ensure_canonical_cash_accounts` - `services/postfinance_service.py::_ensure_efinance_account` - `equity/manage.py::ensure_standard_broker_accounts` Sprint 15 sollte Haushaltskonten nicht erneut als globales Kontomodell erfinden, sondern `budget_accounts.linked_account_id` nutzen. Dafür fehlt derzeit jedoch eine eindeutige, zentral gepflegte Zuordnung. ## Notwendige Boundary-Fixes für Sprint 15 1. **Import-Batch-Vertrag härten** - `budget_import_sessions` ist veränderbar und enthält keinen gebundenen `preview_id + payload_hash + confirmation_id`-Vertrag. - Upload-Preview schreibt bereits Session und archivierte CSV; das ist kein rein read-only Preview. - Für Sprint 15 besser immutable Batch-/Item-Lineage nach dem Muster `portfolio_ingestion_batches/items`, statt neue Kandidatenlogik zu bauen. 2. **Idempotenz vereinheitlichen** - Stark: `confirm_transfer_pair`. - Teilweise: CSV-Kandidaten, Kategorien und Budgetpläne. - Schwach/fehlend: `confirm_budget_transaction`, `confirm_create_budget_account`, Tag-Confirm und normaler `confirm_transaction_candidate`. - Normaler Kandidaten-Confirm antwortet bei Wiederholung mit `409`, statt dieselbe bestätigte Entität zurückzugeben. 3. **Preview an Confirm binden** - Viele Budget-Confirms akzeptieren den Payload erneut, ohne Preview-ID, Hash, Ablauf oder Zustandsrevision zu prüfen. - Batch-Confirm verlangt zwar irgendeine `preview_id`, validiert aber nicht, dass sie zu Kandidaten, Konto und Payload gehört. 4. **Atomarität** - `confirm_transfer_pair` ist sauber atomar. - `confirm_transfer` ruft zweimal `confirm_budget_transaction` auf; diese Funktion committed jeweils selbst. Ein Fehler danach kann Teilbuchungen hinterlassen. - Derselbe Commit-inside-helper-Effekt betrifft Kandidaten-, Split-, Seed- und Category-Review-Kompositionen. 5. **Konto-Mapping normalisieren** - `budget_transaction_candidates.account_source` ist Freitext. - `transfer_pairing::_own_account` löst über ID/Name/Quell-Hinweise auf. - Für Sprint 15 sollte der Importkandidat ein eindeutiges `budget_account_id` beziehungsweise versioniertes Mapping tragen. - `budget_accounts.linked_account_id` ist nicht unique; mehrere Haushaltskonten können dasselbe globale Konto referenzieren. 6. **Schema-/Migrationsnachweis** - `apply_migrations` führt sämtliche historischen Compat-Funktionen bei jedem Start aus. - In `schema_migrations` werden bei einem frischen Schema nur Version **1** und **46** protokolliert. - Der Checksum-Eintrag für 46 hasht nur den Migrationsnamen, nicht die SQL-/Funktionsdefinition. - Sprint-15-Änderungen brauchen daher eine echte neue Version oberhalb 46 mit reproduzierbarer Migration, statt Erweiterung der Compat-Sammelfunktion. - Budgettabellen fehlen außerdem in `storage/schema.py::REQUIRED_TABLES`. 7. **Audit-Vertrag** - `audit/log.py::record_audit_event` ist die gemeinsame Basis. - Für Budgetobjekte fehlen DB-seitige Immutable-/No-delete-Trigger, wie sie neuere Portfolio- und True-Wealth-Verträge besitzen. - Transfer-Audits sind gezielt redigiert; andere Budget-Audits können umfangreiche alte/neue Payloads enthalten. - Zwei Audits werden teils für dieselbe Buchungsübernahme geschrieben; ein einheitlicher Batch-/Confirmation-Audit wäre klarer. 8. **API-Typisierung und Sicherheit** - Budget-Routen verwenden überwiegend rohe `dict`-Payloads statt versionierter Pydantic-Verträge. - `local_only` prüft nur die Client-IP; keine route-spezifische Origin-/CSRF-/Authentisierungsbindung. - Budget-Preview-POSTs sind nicht als read-only allowlisted und werden im Standardmodus blockiert. ## Risikoeinstufung für Sprint 15 - **Grün – direkt wiederverwenden:** Kandidatenmodell, Kategorien/Tags, Budgetpläne, Review-Projektion, generischer Transfer-Matcher, Transfer-Confirm inklusive Audit und Null-Budgeteffekt. - **Gelb – Boundary-Fix erforderlich:** Kontomapping, Batch-Lineage, Preview-Bindung, normale Kandidaten-Idempotenz, API-Schemas, Audit-Minimierung. - **Rot – nicht unverändert für neue produktive Pfade verwenden:** `confirm_transfer`, ungebundene Batch-Confirms, erneutes Erweitern der Compat-Migration und neue Writer mit internem `commit()` in zusammengesetzten Workflows. ### Verifikation - In-Memory-Migration erfolgreich: Schema-Version **46** und alle genannten Budget-/Import-/Transfer-Tabellen vorhanden. - `schema_migrations` enthielt erwartungsgemäß nur `(1, 001_initial_schema)` und `(46, 046_investment_performance_scope_v1)`. - Gezielte Tests konnten nicht gestartet werden: Im Worktree fehlt `.venv`, das verfügbare System-Python enthält kein `pytest`. - **Dateien erstellt/geändert:** keine.