Finanzmanager/PLAN.md
fds2610-bot 2c86e8522b Import und Export in QIF und CSV, Housekeeping für Importdateien
Aus der Kachel QIF-Import wird Import/Export mit je einem Knopf für beide
Richtungen; davor steht die Wahl des Dateiformats (QIF oder CSV), die als
Dialog und ohne Skript als eigene Seite funktioniert.

- CsvBuchungsImport/CsvTabelle: Buchungen samt Splitzeilen, Spalten über die
  Kopfzeile, dieselbe Dublettenerkennung und der Trockenlauf wie beim QIF.
- BuchungsExport/ExportController: Filter nach Zeitraum, Konto, Kategorie und
  Klasse; die Datei entsteht im Speicher, ein Exportverzeichnis gibt es nicht.
- Housekeeping: Hochgeladene Importdateien und data/tmp werden nach der
  eingestellten Frist gelöscht (Vorgabe 14 Tage, 0 schaltet ab).

Migration 013, Test 29 sowie E2E-Spec importexport.spec.js.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 23:01:30 +02:00

406 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Projektplan: Finanzmanager (Web)
> Stand: 12.08.2026 — alle sieben Phasen umgesetzt. Arbeitsgrundlage, gedacht zum Überarbeiten. Änderungen einfach direkt
> in dieser Datei vornehmen; die Umsetzung richtet sich nach dem hier festgehaltenen Stand.
## 1. Kontext
Der Lexware **FinanzManager Deluxe 2026** läuft heute in einer Windows-VM unter QEMU und
verwaltet die privaten Finanzen (Datei `FELIX2018.QDF`). Er soll durch eine selbst gebaute
Web-Anwendung ersetzt werden, die auf einem Debian-Server läuft und vom Browser aus
bedient wird — kein Windows, keine VM, keine Client-Installation.
Randbedingungen aus der Aufgabenstellung:
* **Haushaltsgemeinschaft:** Die Partnerin nutzt den Lexware FinanzManager weiter. Alle
fachlichen Begriffe bleiben deshalb bewusst identisch (Kategorie, Klasse, Splitbuchung,
Umbuchung, Kontengruppe, Budgetplaner, Monatsabschluss, Auswertung, Buchungsvorlage,
Regelmäßige Buchung, Kontoblatt, Cockpit …). Keine eigenen Wortschöpfungen.
* **Optik modern, Layout vertraut:** linke Navigationsleiste mit denselben Menüpunkten,
obere Werkzeugleiste, Tab-Leiste, tabellarische Kontoblätter — nur zeitgemäß gestaltet.
* **Minimale Fremdabhängigkeiten:** Bibliotheken nur, wo sie echte Arbeit sparen, und dann
als eigener Fork unter eigener Kontrolle. Keine Abhängigkeit von fremden Update-Zyklen.
* **Wiederverwendung:** Die FinTS-Anbindung aus dem Parallelprojekt `Steuerkram`
(`/home/fds/projects/Steuerkram`) wird übernommen, nicht neu erfunden.
### Ergebnis der Voranalyse (wichtig für die Planung)
| Befund | Konsequenz |
|---|---|
| `FELIX2018.QDF` ist **vollständig verschlüsselt** (Entropie ≈ 8 bit/Byte über alle 258 Blöcke, keine Klartext-Treffer für Kategorie- oder Kontonamen) | Aus der QDF ist nichts zu holen. Datenquelle ist der **QIF-Export**. |
| `FELIX2018.QSD` enthält im Klartext nur Bankleitzahlen, Spaltenlayouts und Berichtsnamen | Nur als Referenz für Berichtsdefinitionen brauchbar, keine Stammdaten. |
| `Export-Migration-Beispiel.QIF` (1,8 MB, ISO-8859-1, CRLF) enthält alles Nötige: 17 Klassen, 105 Kategorien, 37 Konten, 8780 Buchungen von 2017 bis 2026, 172 Wertpapierbuchungen, 579 Buchungsvorlagen | **Der QIF-Import ist der Migrationsweg.** |
| Der Vollexport reicht bis 2017 zurück; gebraucht wird nur der Zeitraum ab 2026 | **Stichtagsimport**: alles davor wird je Konto zum Anfangssaldo verrechnet (siehe Kapitel 4). Eröffnungssalden stehen als Buchung „Eröffnungssaldo“ mit `L[eigenes Konto]` in der Datei. |
| **Budgets sind im QIF nicht enthalten** | Budgetdaten müssen separat aus dem Budgetplaner exportiert (Export-Symbol oben rechts) oder einmalig neu erfasst werden. |
| `nemiah/php-fints` hängt nur an `psr/log` | Sehr schlank — als Fork gut beherrschbar. |
## 2. Fachliches Datenmodell
Begriffe entsprechen dem FinanzManager. Zwei Auswertungsdimensionen: **Kategorie**
(hierarchisch, `:` als Trenner) und **Klasse** (flach, im QIF mit `/` an die Kategorie
gehängt).
**Stammdaten**
* `konten` — Kontoname, Kontoart, Kontengruppe, Bankname, Kontonummer/IBAN, Beschreibung,
Anfangssaldo, Kreditlimit, aktiv/inaktiv, Sortierung.
Kontoarten aus dem Export (QIF-Typ → Bezeichnung im FinanzManager):
`Bank`→Bank, `CCard`→Kreditkarte, `Cash`→Bar, `Oth T`→Tagesgeld/Sparkonto,
`Oth A`→Vermögen, `Oth L`→Verbindlichkeit, `Oth P`→Rentenversicherung,
`Oth E`/`Oth F`→Sonstiges Vermögen, `Port`/`Invst`→Depot (Wertpapier).
* `kontengruppen` — z. B. „Anlage", „Nicht gruppiert" (Gruppierung der Kontenübersicht).
* `kategorien` — Name, Elternkategorie (Selbstreferenz), Art (`einnahme`/`ausgabe`),
steuerrelevant (QIF-Feld `T`), Beschreibung, aktiv. Konten erscheinen zusätzlich als
**Umbuchungskonten** in der Kategorieauswahl — das wird in der UI abgebildet, nicht als
Datensatz dupliziert.
* `klassen` — Name, Beschreibung.
**Bewegungsdaten**
* `buchungen` — Datum, Konto, Betrag, Zahlungsempfänger (`P`), Buchungstext/Notiz (`M`),
Nummer (`N`), Status abgeglichen (`C`), Kategorie **oder** Gegenkonto (Umbuchung),
Klasse, Quelle (manuell/QIF-Import/FinTS), externe Referenz für die Dublettenerkennung.
* `buchung_splits` — Kategorie, Klasse, Betrag, Notiz je Splitzeile (QIF `S`/`E`/`$`).
Eine Buchung ist entweder einfach kategorisiert oder gesplittet; die Summe der Splits
muss dem Buchungsbetrag entsprechen (Prüfung im Model + Test).
* `buchungsadressen` — die bis zu 6 `A`-Zeilen einer Buchung (Empfängeranschrift).
**Planung**
* `budgets` — Name (z. B. „2026-ecclesia"), Jahr, Beschreibung. Mehrere Budgets pro Jahr
sind möglich, eines ist aktiv.
* `budget_positionen` — Budget, Kategorie, Monat (1–12), Betrag. Der Budgetplaner rechnet
Ist-Werte, Saldo und Jahressumme zur Laufzeit aus den Buchungen.
* `monatsabschluesse` — Jahr, Monat, Zeitpunkt, Ersteller, Status, JSON-Snapshot der
Kontosalden und Kategoriesummen. Ein abgeschlossener Monat sperrt Buchungen in diesem
Zeitraum (Wiedereröffnung möglich, wird protokolliert).
**Online-Banking** (Struktur aus `Steuerkram` übernommen)
* `bank_providers`, `bank_sources` (Zugangsdaten AES-verschlüsselt, `Crypto`-Klasse),
`bank_transactions` (Rohumsätze vor dem Abgleich), `abgleich_regeln` (Regelverwaltung:
Muster auf Empfänger/Verwendungszweck → Kategorie/Klasse/Split).
**Wertpapiere**
* `wertpapiere` — Name (`Y`), WKN (`~`), ISIN (`@`), Typ (`&`), Währung.
* `depot_buchungen` — Datum, Valuta (`V`), Vorgang (`N`: Kauf, Verkauf, DivIn, ReinvDiv …),
Stück (`Q`), Kurs (`I`), Betrag (`U`), Gebühren/Steuern (`O`, 12 Felder, `|`-getrennt),
Verrechnungskonto (`L[Konto]`).
* `kurse` — Wertpapier, Datum, Kurs.
**Nebenmodule**
* `buchungsvorlagen` (QIF `!Type:Memorized`), `regelmaessige_buchungen`, `erinnerungen`,
`adressen`, `vertraege`, `berichte` (gespeicherte Auswertungen mit Filter-JSON).
## 3. Technische Architektur
Bewusst nah an `Steuerkram`, damit Muster, Erfahrung und Code direkt übertragbar sind —
aber mit **nginx statt Apache**.
### Verbindliche Betriebsregeln
* **Keine lokale Installation ohne Rückfrage.** Alles läuft und wird getestet in
Docker-Compose-Containern — kein PHP, kein Composer, kein MariaDB auf dem Host.
* **Erreichbarkeit lokal: `http://localhost:8000`** (nginx-Port-Mapping `8000:80`).
* **Produktionsserver zieht den Code aus dem Upstream-Repository.** Er läuft isoliert,
ohne Zugriff von außen und ohne Zugriff durch Claude Code. Deployment ist dort
ausschließlich `git pull` + Update-Skript, niemals ein direkter Eingriff von hier.
* **Update-Prozess wie in `Steuerkram`**: `scripts/update.sh` (git pull → Backup →
Composer → Image-Rebuild bei Bedarf → Migrationen → Rechte → optionale Tests) plus
`scripts/backup.sh` mit Rotation (7 Tage / 4 Wochen / 12 Monate).
* **Ein-/ausschaltbares Debug-Log** zur Fehlerrekonstruktion: `src/Services/DebugLog.php`,
Schalter in den Einstellungen, Ausgabe nach `data/logs/debug-JJJJ-MM-TT.log` mit
Kontext (Request, Nutzer, SQL-Dauer, FinTS-Dialogschritte). Standardmäßig aus,
automatische Rotation, Zugangsdaten und PINs werden maskiert.
* **Einstellungen zentral in einem einzigen Dialog** mit Reitern (Allgemein, Darstellung,
Buchhaltung, Online-Banking, Import/Export, Wertpapiere, Diagnose/Debug) statt über die
Module verstreut. Persistenz in einer Tabelle `einstellungen` (Schlüssel/Wert), gelesen
über `Setting::get()/set()`.
* **Ein Benutzer, aber mandantenfähiges Datenmodell.** Kernobjekte (Konten, Kategorien,
Klassen, Buchungen, Budgets) führen von Anfang an `benutzer_id`. Die Oberfläche ist
einbenutzerig; die spätere Mitnutzung durch die Partnerin bleibt so ohne Migration möglich.
### Bausteine
* **PHP 8.3**, `declare(strict_types=1)`, PSR-4, Namespace `Finanzmanager\`, kein Framework.
* **Eigenes MVC**: `src/Controllers/`, `src/Models/`, `src/Services/`, `src/Views/`,
zentrale `Router.php`, `Database.php` (PDO, ausschließlich Prepared Statements über
`fetchAll/fetchOne/execute`), `Auth.php`, `View.php`, `Config.php`.
* **Docker Compose, drei Services:**
* `nginx` (Alpine) — Reverse Proxy auf php-fpm, `public/` als Document Root,
Port-Mapping **`8000:80`**
* `php` (`php:8.3-fpm`) — Anwendungscode, Composer, Cron für geplante Abrufe
* `db` (`mariadb:11`) — Daten unter `data/mariadb`
* **Migrationen** als nummerierte SQL-Dateien in `migrations/` (`001_…`), idempotent,
eingespielt von `scripts/migrate.php` beim Containerstart — genau wie in Steuerkram.
* **Frontend ohne Framework:** serverseitig gerendertes HTML, Vanilla JS für
Tabellen-Interaktion, Splitbuchungsmaske und Inline-Bearbeitung im Budgetplaner.
CSS mit Custom Properties (Light/Dark), Layout: feste linke Navigation, obere
Werkzeugleiste, Tab-Leiste, dichte Datentabellen.
* **Diagramme als selbst erzeugtes SVG** (Balken, Linie, Torte) in
`src/Services/ChartRenderer.php` — keine Chart-Bibliothek. Deckt Vermögensentwicklung,
Einnahmen/Ausgaben und Budgetgrafik ab.
### Umgang mit Bibliotheken
| Zweck | Vorgehen |
|---|---|
| FinTS-Bankabruf | `nemiah/php-fints` (nur Abhängigkeit `psr/log`) **auf Codeberg forken** (BOT-Account), in `composer.json` als VCS-Repository auf den Fork zeigen, Version fest gepinnt. `psr/log` ist ein reines Interface-Paket und wird direkt in den Fork übernommen. |
| PDF-Export von Berichten | Vorerst **nicht**. Berichte gehen als CSV/HTML raus, Papier über die Druckfunktion des Browsers. mPDF (mit ~10 Transitiv-Abhängigkeiten) nur, falls sich das als unzureichend erweist — dann ebenfalls als Fork. |
| QIF-Parser, CSV-Import, Charts, Auth, Routing, Templates | Eigenimplementierung. Alles überschaubar, kein Bibliotheksbedarf. |
## 4. Der QIF-Import (kritischster Baustein)
Das Lexware-QIF ist erweitertes Quicken-QIF. Fallstricke, die der Parser beherrschen muss:
1. **Zustandsbehaftet und sequenziell.** `!Option:AutoSwitch` leitet die Kontenliste ein,
`!Clear:AutoSwitch` beendet sie. Danach wechseln sich `!Account` (genau ein Konto) und
`!Type:…` (dessen Buchungen) ab. Die Datei darf **nicht** nach Sektionstypen gruppiert
gelesen werden — der Kontobezug steckt allein in der Reihenfolge.
2. **Datumsformat `MDY`** laut Kopfzeile: `D3.18.26` = 18.03.2026, zweistelliges Jahr.
3. **Beträge im US-Format**: `-1,241.04`. Es gibt `T` und `U` (identischer Wert, `U` mit
höherer Genauigkeit) — `U` gewinnt.
4. **Zeichensatz ISO-8859-1**, Zeilenenden CRLF → Konvertierung nach UTF-8.
5. **Kategorie/Klasse in einer Zeile**: `L400 - Leben:B-Essen alleine/Privat`.
Umbuchung: `L[DKB Girokonto alt]`, ebenfalls mit optionaler Klasse.
6. **Splitbuchungen** als Tripel `S` (Kategorie), `E` (Notiz), `$` (Betrag) — mehrfach je
Buchung.
7. **Umbuchungen stehen nur einmal in der Datei** — in dem Konto, in dem sie erfasst
wurden. Die Gegenseite ist allein durch `L[Zielkonto]` benannt und **muss beim Import
erzeugt werden**; sonst fehlt sie auf dem Gegenkonto und dessen Saldo stimmt nicht.
(Am Vollexport verifiziert: von 965 Umbuchungen hat keine ein zweites Vorkommen; mit
erzeugter Gegenseite stimmen acht geprüfte Kontosalden auf den Cent mit dem
FinanzManager überein.) Auch **Splitzeilen** können ein Umbuchungsziel tragen
(`S[Konto]`) — dann entsteht die Gegenbuchung je Splitzeile.
Eine Buchung, die auf **ihr eigenes Konto** verweist (`L[eigenes Konto]`, Empfänger
„Eröffnungssaldo“), ist die Eröffnungsbuchung und bekommt keine Gegenseite.
8. **Wertpapierbuchungen** (`!Type:Invst`) mit `Y`/`~`/`@`/`&`/`I`/`Q`/`O`/`B`; `O` ist ein
Array aus 12 `|`-getrennten Beträgen (Gebühren, Steuern, Zuschläge). Das
Verrechnungskonto steht in `L|[Konto]`, der dort gebuchte Betrag in `$`.
9. **Idempotenz:** Wiederholter Import derselben Datei darf keine Dubletten erzeugen
(Hash aus Konto + Datum + Betrag + Nummer + Buchungstext).
10. **Das Feld `C` ist der Abgleichstatus** (`X` = abgeglichen), **kein** Kennzeichen für
vorausberechnete Buchungen: sämtliche Wertpapierbuchungen führen es nie. Wer geplante
Buchungen aussortieren will, filtert über das Datum.
Der Import läuft als CLI-Skript **und** über eine Weboberfläche mit Vorschau, Trockenlauf
und Protokoll (importiert / übersprungen / fehlerhaft je Zeile). Nach dem Import wird der User pro Konto gefragt, ob der jeweilige Eröffnungssaldo beibehalten oder geändert werden soll. Der User kann den Saldo jeweils eintragen oder übernehmen, wie er aktuell ist.
### Stichtag statt Vollhistorie
Übernommen wird nur der Zeitraum **ab dem 01.01.2026**. Alles davor fließt nicht als
Buchung ein, sondern wird je Konto zum **Anfangssaldo** aufsummiert — inklusive der
erzeugten Gegenbuchungen aus Umbuchungen. Die Salden stimmen dadurch exakt, ohne dass
neun Jahrgänge mitgeschleppt werden.
Ebenfalls übersprungen werden **vorausberechnete Buchungen**, also solche mit einem Datum
nach dem Importtag: sie stammen nicht von der Bank, sondern aus der Vorausschau des
FinanzManagers.
Was damit entfällt: Auswertungen über die Jahre vor 2026. Was bleibt: sämtliche Salden,
das komplette Jahr 2026 und die Budgetplanung für 2027. Für die **Depots** reicht ein
Saldo nicht — dort werden Bestand und Einstandswert je Wertpapier zum Stichtag gebildet
(Phase 6).
## 5. Phasen
### Phase 0 — Projektgerüst *(erledigt)*
* Repository auf Codeberg (Upstream persönlicher Account, Fork BOT-Account, PR-Workflow
gemäß globaler Regeln), `.gitignore`, `LICENSE`, `README.md`, `CLAUDE.md`, `Changes.md`.
* Docker Compose mit nginx (Port 8000) + php-fpm + MariaDB, `docker-entrypoint.sh`,
`scripts/migrate.php`, `scripts/update.sh`, `scripts/backup.sh`.
* Grundgerüst: `Router`, `Database`, `Config`, `View`, `Auth` (Login, Session, CSRF-Token,
Passwort-Hash), Basis-Layout mit linker Navigation und Werkzeugleiste.
* **Zentraler Einstellungsdialog** mit Reitern und Tabelle `einstellungen` — von Anfang an,
damit spätere Module ihre Optionen dort einhängen statt eigene Dialoge zu bauen.
* **Debug-Log** (`DebugLog`, Schalter im Reiter Diagnose, Rotation, Maskierung von Secrets).
* Test-Harness nach Steuerkram-Vorbild (`tests/run.php`, `TestCase` mit Transaktions-Rollback).
* **e2e-runner einbinden** (`tests/e2e/`, Playwright im Container) — ab jetzt bekommt jedes
Feature mit UI einen E2E-Test.
* *Ergebnis:* Login funktioniert, leere Anwendung ist erreichbar, `./scripts/run-tests.sh` läuft grün.
### Phase 1 — Stammdaten und QIF-Stammdatenimport *(erledigt)*
* Migrationen für `konten`, `kontengruppen`, `kategorien`, `klassen`.
* Verwaltung Kategorien (Baumansicht mit Registern *Alle / Einnahmen / Ausgaben /
Umbuchungskonten*, Anlegen, Bearbeiten, Verschieben, Ersetzen, Zusammenführen).
* Verwaltung Klassen, Kontengruppen, Konten (Anlegen/Bearbeiten, Kontoarten, Anfangssaldo).
* **Kontenübersicht** mit Gruppierung, Spalten Online-Saldo / Aktueller Saldo /
Erwarteter Saldo, Summenzeile.
* `src/Services/QifParser.php` + `QifStammdatenImport.php`, zunächst für `!Type:Class`,
`!Type:Cat`, `!Account` — mit Trockenlauf, Protokoll und `scripts/qif-import.php` für
die Kommandozeile.
* *Ergebnis:* 17 Klassen, 105 Kategorien und 37 Konten sind aus dem Export übernommen und
in der Oberfläche gepflegt. Einzelheiten in `docs/stammdaten.md` und `docs/import-export.md`.
### Phase 2 — Buchungen *(erledigt)*
* Migrationen `buchungen`, `buchung_splits`, `buchungsadressen`.
* **Kontoblatt** je Konto: Tabelle mit Datum, Nummer, Empfänger, Buchungstext, Kategorie,
Klasse, Betrag, Saldo, Abgleich-Status; Sortierung, Filter, Volltextsuche.
* Buchungsmaske: Einnahme / Ausgabe / Umbuchung, **Splitbuchung** mit Restbetrags-Anzeige,
Kategorie- und Klassenauswahl mit Typeahead, Empfänger-Autovervollständigung.
* Saldenberechnung (aktuell / erwartet / abgeglichen), Umbuchungen als eine Buchung mit
zwei Sichten.
* QIF-Import erweitert um `!Type:Bank|CCard|Cash|Oth *` inkl. Splits, Umbuchungs-Deduplizierung
und Import-Oberfläche mit Trockenlauf.
* **Eröffnungssaldo-Dialog** im Anschluss an den Import: je Konto den errechneten Saldo
anzeigen, übernehmen oder überschreiben; die Differenz wird als Anfangssaldo gebucht.
* *Ergebnis:* Der vollständige Altbestand ist migriert, Salden stimmen mit dem
FinanzManager überein (Abgleich gegen die Kontenübersicht als Test).
### Phase 3 — Budget und Monatsabschluss *(erledigt)*
* Migrationen `budgets`, `budget_positionen`, `monatsabschluesse`.
* **Budgetplaner** wie im Original: Zeilen = Kategoriebaum (auf-/zuklappbar), Spalten =
Gesamt-Saldo, abgelaufene Monate zusammengefasst, aktueller Monat mit Budget/Ist/Saldo,
Folgemonate mit Budget. Inline-Bearbeitung, Wert auf alle Folgemonate übertragen,
Kopieren eines Budgets ins neue Jahr, Alarm bei Überschreitung, Gesamtzeile.
* **Monatsabschluss**: Prüflauf (nicht kategorisierte Buchungen, nicht abgeglichene Umsätze,
Splitsummen-Differenzen), Snapshot der Salden, Sperre des Zeitraums, Wiedereröffnung.
* Entschieden während der Umsetzung: Budgetwerte tragen das Vorzeichen der Buchhaltung
(Ausgaben negativ), eingegeben wird der nackte Betrag. Saldo ist **Ist minus Budget** –
positiv heißt besser als geplant. Übergeordnete Kategorien summieren ihre Nachfahren und
werden nicht selbst beplant.
* *Ergebnis:* Jahresbudget zum Jahreswechsel planbar, Monatsabschluss durchführbar.
Einzelheiten in `docs/planung.md`.
### Phase 4 — Auswertungen und Cockpit *(erledigt)*
* Berichtsmodul mit gespeicherten Berichten und Berichtsgruppen (wie „Bankgeschäfte",
„Planung", „Steuern"): **Summenbericht, Buchungsbericht, Vergleichsbericht,
Kontostandsbericht, Budgetbericht**, jeweils mit Zeitraum, Spalten-Aufteilung,
Zeilen-Summe (Kategorie/Klasse/Konto/Empfänger), Filter auf Konten/Kategorien/Klassen.
*(erledigt — `docs/auswertungen.md`)*
* Export CSV; Drilldown von der Berichtszeile in die zugrunde liegenden Buchungen.
*(erledigt)*
* Entschieden während der Umsetzung: Umbuchungen zählen in den Auswertungen nicht mit
(weder Einnahme noch Ausgabe, wie im Budgetplaner); allein der Buchungsbericht führt sie
auf Wunsch mit. Unkategorisierte Buchungen erscheinen als eigene Zeile, statt aus der
Summe zu fallen. Im Ausgabenabschnitt steht der Betrag ohne Minus wie im Original, die
Schlusszeile rechnet mit den echten Vorzeichen. Der Budgetbericht rechnet über volle
Monate — ein halber Monat hat kein halbes Budget.
* Grafiken als SVG: Vermögensentwicklung (gestapelte Balken je Konto), Einnahmen/Ausgaben
je Monat (Balkenpaare mit Saldolinie), Ausgaben nach Kategorie (Ring auf der obersten
Kategorieebene). *(erledigt)*
* **Cockpit** mit konfigurierbaren Kacheln (Vermögensentwicklung, Einnahmen/Ausgaben,
Meine Ausgaben, Kontostände, offene Vorgänge). Die Auswahl steht im Einstellungsdialog;
abgewählte Kacheln werden nicht berechnet. *(erledigt)*
* *Ergebnis:* Die bisher genutzten Berichte sind nachgebaut und liefern dieselben Zahlen,
das Cockpit beantwortet die täglichen Fragen auf einen Blick. Einzelheiten in
`docs/auswertungen.md` und `docs/cockpit.md`.
### Phase 5 — Online-Banking (FinTS) *(erledigt)*
* Fork von `nemiah/php-fints` auf Codeberg (`fds2610-bot/phpFinTS`, Version `4.1.1`),
Einbindung über VCS-Repository. `psr/log` ist im Fork direkt eingebaut — der
Finanzmanager hat damit genau eine Abhängigkeit, und die steht unter eigener Kontrolle.
* Übernahme aus Steuerkram: `Crypto`, `BankSource` (verschlüsselte Zugangsdaten,
`computeStartDate`, `account_filter`), `BankProvider`, FinTS-Abrufdienst,
Abruf-Protokoll.
* **TAN-Handling** wird aus Steuerkram übernommen, wo es bereits läuft — es wird schon für
den reinen Kontoabruf gebraucht (nicht erst für Überweisungen): TAN-Verfahren je Zugang,
Dialog-Fortsetzung über mehrere Requests, pushTAN/photoTAN/chipTAN-Anzeige.
* **Verwaltung → Bankverbindungen**: Zugänge anlegen, Konten zuordnen, Abrufzeitraum.
* Umsatzabruf, Dublettenerkennung, **Abgleich** abgerufener Umsätze gegen vorhandene
Buchungen (Vorschlagsliste: übernehmen / neu anlegen / verwerfen).
* **Regelverwaltung**: Muster auf Empfänger/Verwendungszweck → Kategorie, Klasse oder
festes Split-Schema; Regeln greifen beim Abruf und nachträglich auf Bestandsbuchungen.
* Entschieden während der Umsetzung: Der FinTS-Dialog wird über die **Sitzung** fortgesetzt
(`FinTs::persist()` plus serialisierte Aktion), nicht über einen wartenden Prozess — mit
php-fpm wäre das der falsche Weg. Abgerufene Umsätze werden **nicht sofort gebucht**,
sondern gehen durch die Vorschlagsliste; wer selbst erfasst, hätte sonst alles doppelt.
* *Ergebnis:* Umsätze kommen per Knopfdruck rein und sind großteils automatisch
kategorisiert. Einzelheiten in `docs/banking.md`.
### Phase 6 — Wertpapiere und Depots *(erledigt)*
* Migrationen `wertpapiere`, `depot_buchungen`, `kurse`.
* Depotübersicht, Bestände, Einstandswerte, aktueller Wert, Wertentwicklung.
* Depotbuchungen (Kauf, Verkauf, Dividende, Reinvestition) inkl. Verrechnungskonto-Buchung.
* QIF-Import für `!Type:Invst`, Depotabruf per FinTS.
* **Kursabruf**: Bestände und Kurse kommen primär aus dem FinTS-Depotabruf. Ergänzend ein
Kursdienst nach `Aktienkurse.md` — Vorzug **Leeway.tech** (direkte ISIN-Abfrage,
50 Abrufe/Tag) mit **Financial Modeling Prep** als Alternative (250/Tag). Umgesetzt als
`src/Services/Kursabruf.php` mit austauschbarem Anbieter, API-Key und Anbieterwahl im
zentralen Einstellungsdialog, täglicher Lauf per Cron, Ablage in `kurse`. Bei ~20
Positionen bleibt der Bedarf im Freikontingent.
* Auswertungen: Depotübersicht, Wertpapierentwicklung, Veräußerungsgeschäfte, Erträge.
* Entschieden während der Umsetzung: `betrag` einer Depotbuchung ist der **Bruttowert**,
Gebühren und Steuern stehen daneben — nur so enthält der Einstand die Nebenkosten genau
einmal. Der Einstand folgt dem **gleitenden Durchschnitt** wie im FinanzManager (für die
Steuer gilt FIFO, dafür ist die Bank zuständig). Ohne Kurs wird eine Position nicht
bewertet, statt den Einstand als Ersatzwert auszugeben.
* Der **Depotabruf per FinTS** ist nicht umgesetzt: Bestände und Geschäfte kommen aus dem
QIF-Import und den erfassten Depotbuchungen, Kurse vom Kursdienst. Ein Abruf der
Depotaufstellung ließe sich ohne echte Bankverbindung nicht verlässlich prüfen und wäre
gegenüber dem Kursabruf ein geringer Zugewinn.
* *Ergebnis:* Die Depots sind abgebildet, bewertet und in der Kontenübersicht enthalten.
Einzelheiten in `docs/wertpapiere.md`.
### Phase 7 — Nebenmodule *(erledigt)*
* **Buchungsvorlagen** (`!Type:Memorized`, 579 Stück im Export) inkl. Import.
* **Regelmäßige Buchungen** mit Fälligkeitslauf und Finanzkalender.
* **Erinnerungen**, **Adressen**, **Verträge**, **Sparziele**, **Kredite** (Tilgungsplan).
* **Zahlungsverkehr** (fest eingeplant): Überweisungsformular mit Auftragskonto, Empfänger
aus dem Adressbuch, IBAN/BIC-Prüfung, Verwendungszweck, Kategorie- und Klassenzuordnung,
Termin- und Einzelüberweisung. Absenden über FinTS mit dem in Phase 5 übernommenen
TAN-Handling; Vorgangsliste „Zahlungsvorgänge / Terminüberweisungen" wie im Original.
* Entschieden während der Umsetzung: Der Tilgungsplan und die Termine des Finanzkalenders
werden **gerechnet, nicht gespeichert** — vorausberechnete Tabellen wären bei jeder
Änderung neu zu erzeugen. Die pain.001-Nachricht für SEPA-Überweisungen entsteht in
`Services\SepaXml` selbst, statt dafür eine weitere Bibliothek einzubinden.
* *Ergebnis:* Funktionsgleichstand mit dem bisher genutzten Umfang. Einzelheiten in
`docs/nebenmodule.md`.
## 6. Was du beisteuern musst
1. ~~**Vollständiger QIF-Export** über alle Jahre~~ — liegt vor (8780 Buchungen, 2017–2026).
2. ~~**Anfangssalden je Konto**~~ — entfällt: sie werden aus dem Zeitraum vor dem Stichtag
berechnet und stimmen gegen den FinanzManager auf den Cent. Nachträglich änderbar bleibt
jeder Wert im Kontoformular.
3. **Σ-Kennzeichen je Konto prüfen**: Welche Konten in die Summe der Kontostände zählen,
steht nicht im Export. Nach dem Import einmal durch die Kontenübersicht gehen (Vorgabe:
alle zählen mit).
4. **Kontengruppen anlegen und Konten zuordnen**: Ebenfalls nicht im Export enthalten —
ein `!Account`-Eintrag führt nur Name, Kontoart, Beschreibung und Kreditlimit, und auch
die `QSD`-Datei nennt keine Gruppen. Nach dem Import stehen deshalb alle Konten unter
„Nicht gruppiert“. Laut Screenshot der Kontenübersicht gibt es genau eine Gruppe
(„Anlage“); sie wird unter *Verwaltung → Kontengruppen* angelegt, die Konten dort in
einem Zug angekreuzt.
5. **Budgetdaten**: Export aus dem Budgetplaner (Symbol oben rechts in der Werkzeugleiste),
sonst wird das Jahresbudget 2027 einmalig im Budgetplaner erfasst (Phase 3 steht).
6. Zugangsdaten der Bankverbindungen (erst in Phase 5, Eingabe direkt in der Anwendung).
7. **API-Key für den Kursdienst** (Leeway.tech oder FMP, erst in Phase 6, Eingabe im
Einstellungsdialog).
## 7. Verifikation
Je Phase, nicht erst am Ende:
* **PHP-Tests** `docker compose exec -u www-data php php tests/run.php` — Rechenlogik:
Saldenberechnung, Splitsummen, Budget-Ist-Werte, Monatsabschluss-Snapshot, QIF-Parser
gegen Ausschnitte des Beispielexports als Fixture.
* **E2E-Tests** `tests/e2e/run.sh` — Login, Buchung anlegen mit Split, Kategorie zuordnen,
Budgetwert inline ändern, Monatsabschluss durchführen, Bericht aufrufen.
* **Migrations-Abgleich als Härtetest:** Nach dem Import müssen die Kontosalden exakt den
Werten aus der Kontenübersicht des FinanzManagers entsprechen. Geprüft an acht Konten,
deren Werte aus den Screenshots vorliegen — sie stimmen auf den Cent (Stand Phase 2).
Die **Summe der Kontostände** (503.430,75 € laut Screenshot) ist seit Phase 6
vergleichbar: Depots gehen mit ihrem bewerteten Bestand in die Kontenübersicht ein. Es
fehlen nur noch die Kurse — solange für eine Position keiner vorliegt, weist die
Übersicht sie ausdrücklich als unvollständig aus. Offen bleibt außerdem das
Σ-Kennzeichen je Konto (im Export nicht enthalten, Vorgabe: alle zählen mit).
Die Jahressummen Einnahmen/Ausgaben (Cockpit 2026: 148.060,18 € / 58.457,07 €) hängen an
denselben Voraussetzungen und lassen sich über den Summenbericht abgleichen.
* **Manuell**: Anwendung unter `http://localhost:8000` aufrufen, Kontenübersicht,
Kontoblatt, Budgetplaner und Summenbericht mit den Screenshots aus `Altdaten/` vergleichen.
## 8. Entschiedene Punkte
Die vormals offenen Fragen sind geklärt und in den Plan eingearbeitet:
| Frage | Entscheidung | Steht in |
|---|---|---|
| Mehrbenutzerbetrieb | Ein Benutzer in der Oberfläche, aber `benutzer_id` von Anfang an im Datenmodell | Kapitel 3, Betriebsregeln |
| Zahlungsverkehr | Wird umgesetzt, Phase 7, mit dem TAN-Handling aus Steuerkram | Phase 5 (TAN) und Phase 7 |
| Wertpapierkurse | FinTS-Depotabruf plus Kursdienst nach `Aktienkurse.md` (Leeway.tech, ersatzweise FMP) | Phase 6 |
| Eröffnungssalden | Dialog je Konto direkt nach dem Import | Kapitel 4 |
| Lokaler Port | `http://localhost:8000` | Kapitel 3 |