# Änderungen ## Rahmen, Schatten und Tabs im Design (01.10.2026) - Tabellen (`table.liste`), Kacheln und breite Formulare (`.formular.breit`, u. a. Einstellungen) erhalten in beiden Themen einen 1px breiten hellblauen Rand (`--bereichsrand`) und einen leichten Schatten (`--bereichsschatten`) für mehr Kontrast zwischen den Bereichen. - Titelleiste: Linie im Stil des Bereichsrands, dazu ein schmaler Schatten nach unten. Tab-Leiste: ebenso, die Linie ist im dunklen Thema dezenter (`--tablinie`). - Aktiver Tab und rechtsbündiger Tab („Kacheln wählen“, „Regeln“) haben im dunklen Thema einen abgesetzten blauen Rand (`--tabrand`). ## Automatische Buchungsnummer (01.10.2026) - Neue Buchungen ohne Nummer erhalten beim Anlegen die nächste laufende Nummer ihres Kontos (höchste rein numerische Nummer + 1, `Buchung::naechsteNummer()`). Vorgegebene Nummern (QIF, CSV, Formular) bleiben, bestehende Buchungen werden nicht verändert; die interne ID bleibt verborgen. - Test: `53_BuchungsnummerTest.php`. ## Regelmäßige Buchungen: Buchen zum gewählten Datum (01.10.2026) - „Buchen“ in „Jetzt fällig“ schlug mit einer Fehlermeldung fehl, weil das Formular ein leeres Datum schickte. Jetzt ist „Fällig am“ per Klick editierbar; „Buchen“ bucht zu diesem Datum. Die Änderung gilt nur für diese Buchung und verändert die Reihe nicht. Ein leeres Datum fällt auf den Termin zurück, ein ungültiges wird abgewiesen. ## „+ Neu“ öffnet das Formular als Dialog (01.10.2026) - Das Formular zur Neuanlage stand in elf Ansichten unter der Liste. Jetzt öffnet der Knopf „+ Neu“ es als modalen Dialog (`app.js`, `.neudialog`); nach dem Speichern erscheint die Liste wieder, mit dem neuen Eintrag. Betroffen: Klassen, Buchungsvorlagen, Regelmäßige Buchungen, Erinnerungen, Verträge, Adressen, Sparziele, Kredite, Abgleichregeln, Budgets, Berichtsgruppen. - E2E: `neudialog.spec.js`; bestehende Specs öffnen den Dialog zuerst. ## Regelmäßige Buchungen: Klick zum Bearbeiten (01.10.2026) - In „Alle Reihen“ erscheinen alle Felder zunächst als Text und werden erst beim Anklicken zum Eingabefeld (`reihenliste.js`). Ja/Nein-Felder schalten per Klick um, „Speichern“ erscheint erst nach einer Änderung. Konto und Klasse stehen untereinander, die Spalte Name/Buchungstext nimmt den freien Platz. ## Klasse und nächster Termin in „Alle Reihen“ (01.10.2026) - Unter der Kontoauswahl gibt es eine Klassenauswahl, der „Nächste Termin“ ist ein Datumsfeld und lässt sich von Hand verschieben (`RegelmaessigeBuchung::speichern()`, ungültige Angaben werden ignoriert). ## Buchungstext bei regelmäßigen Buchungen (01.10.2026) - Das Feld „Buchungstext“ fehlte im Formular für neue regelmäßige Buchungen und war in der Liste nur ein verstecktes Feld. Jetzt beim Anlegen vorhanden und in der Liste unter dem Namen bearbeitbar (`termine/index.php`). ## Fälligkeit folgt der Buchung (01.10.2026) - Eine per Fälligkeitslauf oder „Buchen“ erzeugte Buchung merkt sich jetzt ihre regelmäßige Buchung und den Soll-Termin (`buchungen.regelmaessige_id`, `faelligkeit`, Migration 023). Wird sie gelöscht, rückt die Fälligkeit auf diesen Termin zurück, eine ausgelaufene Reihe wird wieder aktiv, `zuletzt_am` folgt der jüngsten verbleibenden Buchung (`RegelmaessigeBuchung::terminWiederOeffnen()`). Bereits vorhandene Buchungen sind nicht verknüpft; bei ihnen bleibt es beim bisherigen Verhalten. ## Darkmode-Farben nach Mockup (01.10.2026) - Dunkle Palette bläulicher (Navy) statt grau-blau, Seitenleiste, Flächen, Ränder und Akzent angepasst. Der aktive Menüeintrag ist wie im hellen Modus blau hinterlegt (`--navi-aktiv`), der Balken links bleibt weiß (neue Variable `--navi-balken`). Nur Farben, nur im Darkmode (`app.css`). - Blaue Schrift (Links, Überschriften, Marken) besser lesbar: im Darkmode heller, im Hellmodus dunkler. Eigene Variable `--akzent-schrift`, Knöpfe und Rahmen bleiben unverändert. ## Favicon in hellem und dunklem Modus (01.10.2026) - Das Favicon war ein schwarzes „€“ ohne Hintergrund und im Darkmode kaum zu erkennen. Jetzt weißes „€“ auf blauer Kachel – in beiden Modi gut sichtbar (`kopf.php`, `schlicht.php`). ## Offener Vorgang für fehlgeschlagene Bargeld-Tool-Prüfungen (30.09.2026) - Die Kachel „Offene Vorgänge“ im Cockpit zählt jetzt auch Buchungen, deren letzter Abgleich mit dem Bargeld-Tool an einem Abrufproblem gescheitert ist (`bargeld_status = 'fehler'`) – nicht zu verwechseln mit „im Bargeld-Tool nicht gefunden“. Ein Klick führt auf die neue Liste unter Verwaltung → Bargeld-Tool → fehlgeschlagene Prüfungen (`/verwaltung/bargeld/fehler`), die diese Buchungen kontoübergreifend mit Konto, Betrag und Fehlermeldung auflistet (`Buchung::mitBargeldFehler()`). Fix #102. ## phpFinTS auf v4.1.6 (30.09.2026) - Fork neu auf Upstream vom 22.09.2026 aufgebaut (psr/log und Depotaufstellung v6, #598, bleiben als eigene Commits). Der VOP-Patch entfällt, #584 ist upstream gemergt. Neu von Upstream u. a.: gespeicherte BPD werden beim Laden neu geparst (#595), MT940-Schlusssalden (#593). - Abrufe (Umsätze, Depot) und eine Echtzeitüberweisung bei der DKB laufen unverändert durch. ## Stichtag beim Einbuchen des Depotbestands (30.09.2026) - „Bestand einbuchen“ (Wertpapiere → Bestand laut Bank) fragt in einem modalen Dialog nach dem Stichtag. Er ist das Datum aller Ein- und Auslieferungen des Laufs; vorbelegt ist der Stand des Bankabrufs. Die Notiz nennt weiterhin den Bankstand. ## Zuordnungsliste: Depots nur noch als Wertpapiergeschäft (30.09.2026) - Wo die Gruppe „Wertpapiergeschäft im Depot“ steht (Kontoblatt, Buchungsmaske), fehlen die Depots in der Gruppe „Umbuchung auf ein Konto“. Die fast gleich benannten Einträge führten dazu, dass ein Kauf als schlichte Umbuchung ohne Stückzahl, Kurs und Nebenkosten gespeichert wurde. In den Splitzeilen bleibt die Umbuchung ins Depot erhalten. ## Depotabruf per FinTS und Nachweis zur Überweisung (30.09.2026) - Das Depot (Kontoart 30, ohne IBAN) fehlte in der Kontenabfrage, weil `HKSPA` nur Konten mit IBAN liefert. Es steht in den Kontostammdaten der Bank (`HIUPD`) mit erlaubtem `HKWPD`. - Neuer Modus `depot` in `FinTsAbruf`: Anmeldung, Depots aus der UPD, je Depot `GetDepotAufstellung` (gleiche TAN-Behandlung und Protokollierung wie der Umsatzabruf). Der Knopf sitzt unter Wertpapiere → *Bestand laut Bank*, nicht im Umsatzabruf: andere Daten, eigene TAN. - Das Depot im Finanzmanager wird über die Kontonummer gefunden (Kontenverwaltung). `Depotabgleich::uebernehmen()` speichert den Stand des letzten Abrufs (`depot_bankbestaende`, Migration 021) und trägt die Bewertungskurse bekannter Wertpapiere ein. Gebucht wird nichts; `Depotabgleich::vergleich()` zeigt Abweichungen im Bestand. - Nachweis zur Überweisung: `bank_protokolle.pruefsumme` (SHA-256 des Protokolls, beim Speichern gebildet). Im Protokoll einer Überweisung lädt *Nachweis herunterladen* eine Textdatei mit Auftrag, allen Durchläufen im vollen Wortlaut und den Prüfsummen; ein nachträglich geändertes Protokoll fällt dort als ABWEICHUNG auf. - phpFinTS auf v4.1.5: Die DKB meldet die Depotaufstellung nur in Version 6 (`HKWPD`/`HIWPD`/`HIWPDS`); Upstream-PR nemiah/phpFinTS#598. - Zuordnung des Depots der Bank zu einem Depot im Finanzmanager (Migration 022, `bank_konten.art`): Das Depot steht als eigene Zeile bei den Bankkonten des Zugangs und wird beim Kontenabruf (ohne zusätzliche TAN) oder beim Depotabruf erkannt. Passt die Kontonummer zu einem eigenen Depot, wird gleich zugeordnet; sonst unter Online-Banking → Konten zuordnen (dort auch „neues Konto anlegen“). Der Umsatzabruf sieht nur Konten (`Bankzugang::konten()`), Depots über `Bankzugang::depots()`. - „Bestand einbuchen“ über der Tabelle unter Wertpapiere → Bestand laut Bank (`Depotabgleich::einbuchen()`): Mehrbestand wird als Einlieferung, Minderbestand als Auslieferung gebucht (ohne Geldseite), unbekannte Wertpapiere werden angelegt. Kurs der Einlieferung: Einstandskurs der Bank, sonst Bewertungskurs. - Spalte „Einstandskurs“ (soweit die Bank ihn meldet) in der Tabelle Bestand laut Bank. - Test 51. ## Buchungsmaske: Splitzeilen bleiben bei falscher Summe erhalten (29.09.2026) - Scheiterte das Speichern einer Buchung an der Summenprüfung der Splitzeilen (Fehlermeldung „Die Splitzeilen ergeben …, die Buchung lautet über …“), leitete `BuchungenController` bisher mit einer Flash-Meldung auf eine frische Maske weiter – die gerade erfassten Splitzeilen (und alle anderen Feldwerte) waren damit weg, obwohl in der Datenbank nichts geändert wurde: Die Validierung läuft schon vor der Transaktion, es geht nichts kaputt, nur die Eingabe. - `BuchungenController::anlegen()`/`speichern()` zeichnen die Maske jetzt bei einem Validierungsfehler direkt neu, mit den zuletzt abgeschickten Werten (`$_POST`) statt der gespeicherten – die Fehlermeldung erscheint wie gewohnt über `View::flash()`, die eigenen Eingaben bleiben stehen und lassen sich korrigieren. `View::render()` erlaubt dafür jetzt, dass ein Aufruf zusätzlich zur vorgemerkten Flash-Meldung eine eigene mitgibt, statt sie zu überschreiben. - Neu: `tests/e2e/specs/buchungen.spec.js` prüft, dass eine unstimmige Splitsumme die Eingaben (Datum, Betrag, Empfänger, beide Splitzeilen) unverändert lässt und sich nach einer Korrektur wie gewohnt speichern lässt. ## Splitbuchungen: Summenzeile zeigt den Restbetrag, Speichern nur bei Null (29.09.2026) - Die Summenzeile der Splitbuchungen (Buchungsmaske und Splitdialog im Kontoblatt) zeigte bisher die Summe der erfassten Zeilen, mit „(Rest X)“ als Zusatz. Jetzt steht dort direkt der noch offene Restbetrag zum Buchungswert (`Buchungswert - Summe der Zeilen`) – die gemeinsame Rechenfunktion `FmBetrag.splitsumme()` in `betrag.js` liefert ihn beiden Masken. - „Speichern“ (bzw. „Splitbuchung speichern“) bleibt gesperrt, solange mindestens eine Splitzeile einen Betrag trägt und der Restbetrag nicht Null ist. Ohne Splitzeilen – egal ob unbelegt oder mit direkter Kategorie/Gegenkonto zugeordnet – bleibt der Knopf wie bisher an; das Model prüft die Summe der Splitzeilen ohnehin schon serverseitig. - Der bisherige E2E-Test, der eine unstimmige Splitsumme über eine serverseitige Fehlermeldung prüfte, prüft jetzt die neue Sperre im Browser; die serverseitige Prüfung selbst deckt weiterhin Test 09 (PHP) ab. ## Regel direkt aus einer Buchung anlegen (29.09.2026) - Neuer Toggle „Regel anlegen“ in der Buchungsmaske, standardmäßig aus; das bisherige Verhalten bleibt unverändert, solange er aus ist. - Ist er beim Speichern an, entsteht aus Empfänger (ersatzweise Buchungstext), Kategorie, Klasse, Gegenkonto und Splits der Buchung eine neue Regel (`AbgleichRegel::ausBuchung()`). Ohne Empfänger und Buchungstext gibt es kein sinnvolles Muster – dann bleibt es beim gewöhnlichen Speichern ohne Regel. - Das Kontoblatt zeigt die neue Regel danach in einem modalen Dialog (`regeldialog.js`, natives `` wie beim Splitdialog) zum Prüfen und Anpassen – mit denselben Feldern wie „Neue Regel“ in den Bankregeln (ausgelagert nach `regelfelder.php`). - „Abbruch“ löscht die gerade erst angelegte Regel wieder (über das bestehende Lösch-Formular der Regelliste, nur mit Rücksprung zum Kontoblatt statt zur Regelliste); die Buchung bleibt gespeichert. Die Escape-Taste verhält sich genauso, statt den Dialog nur zu schließen und eine verwaiste Regel zurückzulassen. - „Speichern“ übernimmt Änderungen und lässt die Regel in der Liste stehen. - Neu: Test 51 (PHP) und `tests/e2e/specs/regelausbuchung.spec.js`. ## Umbuchungen: beide Seiten finden zueinander (29.09.2026) - Eine Umbuchung erscheint auf zwei Kontoauszügen, wurde aber nicht als eine Buchung erkannt: Der zweite Abruf legte die Gegenseite als eigene Buchung an, der Betrag stand doppelt im Kontoblatt. Jetzt gilt (`Bankumsatz::gegenbuchung()`): anderes Konto, Betrag mit umgekehrtem Vorzeichen, Buchungstag höchstens `banking_abgleich_tage` entfernt, keine Kategorie, keine Splitbuchung, genau ein Umsatz – und die IBANs dürfen sich nicht widersprechen (Gegen-IBAN muss eine IBAN des anderen Kontos sein). Unter mehreren Kandidaten gewinnt der zeitlich nächste. - Konten ohne IBAN (PayPal): Sind die IBANs nicht zu prüfen, wird nur bei einem eindeutigen Treffer automatisch verbunden; bei mehreren bleibt es ein Vorschlag in der Liste. - Fall 1: Der zweite Umsatz wird mit der Buchung der Gegenseite verbunden; sie bekommt das Gegenkonto und wird zur Umbuchung (`Bankumsatz::verbinden()`). - Fall 2: Haben beide Seiten schon eine eigene Buchung, führt der Abgleich sie zusammen (`Bankumsatz::umbuchungenZusammenfuehren()`, am Ende von `Bankabgleich::laufenLassen()`). Die ältere Buchung wird zur Umbuchung, die jüngere fällt weg. Nur Buchungen ohne Kategorie und Klasse; ein abgeschlossener Monat landet im Bericht. - Auch beim Zuordnen: Wählt man in der Schnellzuordnung oder im Buchungsformular ein Gegenkonto, oder setzt eine Regel beim Übernehmen eines Umsatzes eines, sucht das Programm die Gegenseite (`Bankumsatz::gegenseiteZusammenfuehren()`). Sie darf eine einzeln gebuchte Buchung (ohne Kategorie und Klasse) oder eine Umbuchung in Gegenrichtung sein; beide werden zusammengelegt, der Umsatz wandert an die verbleibende Buchung. Bei mehreren Kandidaten gewinnen bestätigte IBANs, dann der zeitlich nächste. - `passendeBuchung()` lässt je Kontoseite einen Umsatz zu. - Neu: Test 50; Test 16 erweitert. ## Bekannte Umsätze bekommen IBAN und Bank der Gegenseite nachgetragen (29.09.2026) - Der Tooltip im Kontoblatt blieb bei älteren Umsätzen leer: IBAN und Bank der Gegenseite werden erst seit dem 28.09. gespeichert, und ein erneuter Abruf übersprang bekannte Umsätze ganz. Jetzt trägt `Bankumsatz::anlegen()` bei einem bekannten Umsatz nach, was ihm fehlt (`Bankumsatz::ergaenzen()`), und gibt es an die verbundene Buchung weiter. Nur leere Felder werden gefüllt, vorhandene bleiben. - Auch `Bankumsatz::verbinden()` überträgt die Angaben auf eine von Hand erfasste Buchung. - Neu: Test 49. ## E2E-Läufe lassen die Einstellungen unverändert (29.09.2026) - Die Browser-Tests laufen gegen die echte Instanz; der Bargeld-Test hatte dabei die Adresse des Bargeld-Tools geleert. `tests/e2e/run.sh` sichert jetzt die ganze Tabelle `einstellungen` vor dem Lauf und stellt sie danach wieder her (auch bei Abbruch mit Strg+C). Eine nach hartem Abbruch liegengebliebene Sicherung wird vor dem nächsten Lauf eingespielt. - Neu: `Services/E2EEinstellungen`, `scripts/e2e-einstellungen.php sichern|wiederherstellen`, Test 48. Die Sicherung liegt unter `data/e2e/` (nicht im Repository). ## Kippschalter statt Ankreuzfeldern (29.09.2026) - Alle Ankreuzfelder erscheinen als Kippschalter im blauen Design des Projekts: aus = graue Fläche mit ✕, an = Akzentfarbe mit Häkchen (helles und dunkles Thema). Reine CSS- Änderung in `public/static/app.css`; die Elemente bleiben echte Ankreuzfelder. - Ausgenommen ist die Zeilenauswahl in den Listen (Kontoblatt, Depotbuchungen, Umsätze inkl. „alle wählen“). ## Regeln: Summe der Anteile in den Splittabellen (29.09.2026) - Die Spalte „Anteil %“ heißt in den Splittabellen der Regeln nur noch „Anteil“, da die Anteile zueinander gelten und keine Prozentwerte sein müssen. - Unter den Anteilen steht eine Summenzeile, die sich mit jeder Eingabe in derselben Tabelle neu berechnet (`public/static/app.js`). ## Zahlungsverkehr: Echtzeitüberweisung scheiterte am SEPA-Format der Bank (28.09.2026) Eine Echtzeitüberweisung schlug mit „Für Echtzeitüberweisungen nimmt die Bank nur SEPA-Formate an, die der Finanzmanager nicht erzeugt: …pain.001.001.09…“ fehl; ohne das Echtzeit-Häkchen kam stattdessen die rohe Bibliotheksmeldung „Combined VOP match confirmation and TAN request“ (#89). Zwei unabhängige Ursachen: - `SepaXml` erzeugte ausschließlich pain.001.001.03 und das DK-Format pain.001.003.03. Banken, die inzwischen nur noch das neuere ISO-Format **pain.001.001.09** führen, wiesen jede Überweisung schon vor der TAN ab. `SepaXml` erzeugt es jetzt mit: Der Ausführungstermin steht in einem eigenen `Dt`-Element (`ReqdExctnDt/Dt`), die Bankkennung heißt `BICFI` statt `BIC`, und jede Zahlung trägt eine `UETR` (eine UUID, die das SEPA-Instant-Regelwerk seit November 2019 verlangt und die für eine Echtzeitüberweisung ohnehin nötig ist). `SepaXml::schemaWaehlen()` bevorzugt es, wenn die Bank es meldet, vor dem älteren pain.001.001.03. - Bestätigt die Bank den Namensabgleich mit einem Volltreffer und verlangt im selben Antwortsatz schon die TAN, kennt `nemiah/php-fints` (Stand v4.1.4) diese Kombination noch nicht und wirft dafür eine rohe `UnsupportedException` – obwohl der Vorgang beide Angaben zu diesem Zeitpunkt schon erhalten hat. `FinTsUeberweisung` fängt genau diese Ausnahme jetzt ab und lässt den ohnehin schon vorhandenen automatischen Volltreffer-Namensabgleich (siehe unten) den Rest erledigen, statt den Auftrag mit der englischen Bibliotheksmeldung scheitern zu lassen. Erweiterte Tests in `tests/39_SepaSchemaTest.php` und `tests/43_EchtzeitueberweisungTest.php` für das neue Format, neuer Test in `tests/40_NamensabgleichTest.php` für die abgefangene Ausnahme. ## Buchungsmaske: Splitbuchung und direkte Zuordnung sperren sich gegenseitig (28.09.2026) Splitzeilen und die Felder *Kategorie / Gegenkonto* und *Klasse* ließen sich in der Buchungsmaske gleichzeitig ausfüllen (#77). - Sobald in einer Splitzeile etwas eingetragen ist, sind *Kategorie / Gegenkonto* und *Klasse* sofort gesperrt; eine Kategorie oder Klasse sperrt umgekehrt alle Splitzeilen. - Neuer Knopf *Splitbuchungen löschen* rechts über der Splittabelle: leert alle Splitzeilen und gibt Kategorie und Klasse wieder frei. - E2E-Prüfung in `buchungen.spec.js`. ## Kontoblatt: Klassen der Splitzeilen in der Klassenspalte (28.09.2026) Die Klassenspalte des Kontoblatts zeigte bei einer Splitbuchung nur die Klasse der Buchung selbst, nicht die ihrer Splitzeilen (#76). - Neu `Buchung::splitKlasse()`: Tragen alle Splitzeilen dieselbe Klasse, steht sie in der Spalte. Unterscheiden sie sich, steht dort die Marke *Splitbuchung*, der Tooltip nennt je Splitzeile Kategorie und Klasse. Ohne Klasse in den Zeilen bleibt es bei der Klasse der Buchung. - Ein Klick auf die Klassenzelle einer Splitbuchung öffnet den Splitdialog, in dem die Klassen der Zeilen geändert werden. - Neuer Test `tests/47_SplitKlasseTest.php`, E2E-Prüfung in `buchungen.spec.js`. ## Online-Banking: Alle Umsätze abrufen (28.09.2026) Über der Tabelle der Bankverbindungen steht ein Knopf *Alle Umsätze abrufen*, der jede aktive Bankverbindung der Reihe nach einzeln abruft (#75). - Neuer Dienst `Services\Sammelabruf` führt die Warteschlange; sie liegt während einer TAN-Runde in der Sitzung, nach der Freigabe geht es mit der nächsten Bank weiter. - Die TAN-Maske zeigt im Sammelabruf, welche Bankverbindung gerade fragt, und bietet *Diese Bank überspringen* neben *Abruf abbrechen*. - Ein Fehler bei einer Bank hält die übrigen nicht auf; am Ende steht je Bankverbindung eine eigene Meldung. Dafür neu: `View::flash()` für mehrere Meldungen auf einmal. - Neuer Test `tests/46_SammelabrufTest.php`, E2E-Prüfung in `banking.spec.js`. ## Einnahme/Ausgabe entscheidet der Geldfluss, nicht die Kategorie-Art (28.09.2026) Cockpit und Berichte zählten eine Bewegung nach der Art ihrer Kategorie zu Einnahmen oder Ausgaben – nicht nach ihrem Vorzeichen. Eine Kategorie wie eine Rückstellung, die in beide Richtungen bebucht wird (z. B. „010 - Rückst. USt“, angelegt als Einnahme), ließ dadurch auch negative Beträge in den Einnahmen auftauchen (n.n., Buchung 29285). - `Auswertung::artDerBewegung()` entscheidet jetzt allein über das Vorzeichen des Betrags. Die zugewiesene Kategorie-Art spielt für die Auswertung keine Rolle mehr – sie bleibt reine Verwaltungs-Metadaten (Reiter in der Kategorienverwaltung, Vorzeichen im Budgetplaner). - `Auswertung::kategoriezeilen()` baut das Zeilengerüst von Summen- und Vergleichsbericht jetzt aus allen Kategorien statt nur denen der passenden Art – sonst wäre eine Bewegung wie oben zwar in der Abschnittssumme, aber in keiner Zeile aufgetaucht. - `Auswertung::budgetbericht()` summiert den Ist-Wert einer Kategorie jetzt aus beiden Töpfen (Einnahme und Ausgabe), da eine Kategorie nun in beiden auftauchen kann. - Betroffen waren dadurch alle Cockpit-Kacheln, die auf dem Summenbericht aufsetzen (*Einnahmen und Ausgaben*, *Meine Ausgaben*) sowie deren Legenden-Drilldown, Summen-, Vergleichs- und Budgetbericht unter *Auswertungen*. - Neuer Test `summenbericht_geldflussEntscheidet()` in `tests/14_AuswertungTest.php` mit einer Kategorie, die in beide Richtungen bebucht wird. ## Cockpit: Legendeneinträge öffnen die Buchungen dahinter (28.09.2026) In allen Cockpit-Kacheln mit Legende führt ein Klick auf einen Legendeneintrag zu den Buchungen, aus denen sich der angezeigte Wert zusammensetzt (#84). - *Meine Ausgaben*: jede Kategorie, „Nicht kategorisiert“ und „Übrige (n)“ öffnen die Ausgaben des laufenden Jahres dieser Kategorien (samt Unterkategorien). - *Einnahmen und Ausgaben*: „Einnahmen“, „Ausgaben“ und „Saldo“ öffnen die Buchungen der letzten zwölf Monate. - *Vermögensentwicklung*: ein Kontoname öffnet das Kontoblatt. - Ziel ist der bestehende Drilldown der Auswertungen mit dem Filter der Kachel; `Auswertung::zeilenbuchungen()` kennt dafür neu einen Art-Filter (Einnahme/Ausgabe), „alle Bewegungen“ und mehrere Kategorien auf einmal. Aus dem Cockpit geht es zurück ins Cockpit. Die Summe der Liste entspricht dem Legendenwert. - Tests in `tests/15_CockpitTest.php` und `tests/e2e/specs/module.spec.js`. ## IBAN und Bank der Gegenseite im Kontoblatt (27.09.2026) Bei einer Buchung aus dem Bankabruf ließ sich oft nicht mehr erkennen, woher sie stammt – der Empfängername allein reicht nicht immer (z. B. bei Überweisungen zwischen eigenen Konten verschiedener Banken). - `Services\FinTsAbruf` löst IBAN/Kontonummer und Bankleitzahl bzw. BIC der Gegenseite schon beim Abruf gegen das Bankleitzahlen-Verzeichnis auf (neu: `Bankinstitut::findeNachBic()` / `findeNachBicOderBlz()`) und schreibt beides nach `bank_umsaetze.gegen_iban` / `gegen_bankname`. - `Bankumsatz::uebernehmen()` trägt beides auf die neue Buchung ein (`buchungen.iban`, `buchungen.bankname`), Migration `020_buchung_herkunft.sql`. - Im Kontoblatt zeigt der Verwendungszweck beim Mouseover IBAN und Bank, sofern bekannt (`kontoblatt.js`, `data-herkunft`). - Gilt nur für künftige Bankabrufe – bereits vorhandene Buchungen aus früheren Abrufen (auch die den Anstoß gebende Buchung 31178) kennen die Gegenseite nicht nachträglich, weil sie beim damaligen Abruf nicht mitgeschrieben wurde. - Test in `tests/45_BuchungHerkunftTest.php`. ## Umbuchung wird auch von der Gegenkontoseite erkannt (27.09.2026) Eine Umbuchung gehört ihrem Ursprungskonto; der automatische Abgleich suchte eine passende Buchung bisher nur über deren `konto_id`. Kam der Kontoauszug der Empfängerseite mit ein paar Tagen Verzug herein, fand er die schon gebuchte Umbuchung nie und legte sie als eigenständige Buchung an – die Umbuchung stand danach doppelt im Kontoblatt und verfälschte den Saldo. - `Bankumsatz::passendeBuchung()` berücksichtigt jetzt auch die Gegenkonto-Seite einer Umbuchung, mit umgekehrtem Vorzeichen wie im Kontoblatt auch. - Test in `tests/16_BankingTest.php` (`umbuchungWirdAuchVonDerGegenkontoseiteErkannt`). ## Buchungen im Kontoblatt verknüpfen (27.09.2026) Liegen zwei Buchungen, die eigentlich derselbe Vorgang sind, zu weit auseinander (oder gehörte eine ursprünglich einem anderen Ursprungskonto), findet der automatische Abgleich sie nicht – die Umbuchung und ihre einzeln erfasste Dublette stehen dann dauerhaft nebeneinander im Kontoblatt. - Neu: `Buchung::verknuepfen()` führt eine Umbuchung (eigene Seite oder Gegenseite auf diesem Konto) und eine einzeln erfasste Buchung mit passendem Betrag zusammen – die Umbuchung bleibt, die Dublette fällt weg, ihr Bankumsatz hängt sich an die Umbuchung. - Im Kontoblatt lässt sich dafür auch die Gegenseite einer fremden Umbuchung ankreuzen; der Knopf „Verknüpfen“ neben „Auswahl löschen“ nutzt dieselbe Auswahl. - Test in `tests/44_BuchungenVerknuepfenTest.php`. ## Buchungsbericht: Splitbuchung nach Konto/Empfänger zusammengefasst (27.09.2026) Nach Kategorie oder Klasse gruppiert erscheint eine Splitbuchung weiterhin mit jeder ihrer Zeilen einzeln – nur so stimmen die Gruppensummen mit dem Summenbericht überein. Nach Konto oder Empfänger gruppiert landen aber ohnehin alle Zeilen einer Splitbuchung in derselben Gruppe; dort erschien bisher trotzdem jede Zeile für sich, mit ihrem Teilbetrag statt dem Gesamtbetrag – wer nur eine der beiden Zeilen sah, hielt das für den kompletten Betrag der Buchung. - `Auswertung::splitsZusammenfassen()` fasst bei Gruppierung nach Konto oder Empfänger alle Splitzeilen einer Buchung zu einer Zeile mit dem Gesamtbetrag zusammen, analog zum Kontoblatt. Die Zeile führt keine einzelne Kategorie, sondern das Badge „Splitbuchung“; die Aufschlüsselung bleibt dem Kontoblatt vorbehalten. - Bei Kategorie oder Klasse bleibt es beim bisherigen Verhalten. - Test in `tests/14_AuswertungTest.php` (`buchungsbericht`). ## Regeln lassen sich auf Buchungen ohne Kategorie anwenden (27.09.2026) Der Knopf unter Verwaltung → Regeln hieß „Gegen offene Umsätze prüfen“ und zählte nur, wie viele noch nicht abgeglichene Umsätze eine Regel träfe – das beantwortet weder, wo im Kontoblatt schon Lücken liegen, noch schließt es sie. - Neu: `Buchung::ohneKategorie()` liefert Buchungen ganz ohne Kategorie – weder direkt (`kategorie_id`) noch in einer Splitzeile –, ausgenommen Umbuchungen (die haben per Definition ein Gegenkonto statt einer Kategorie). - Neu: `Bankabgleich::regelnAufBuchungenOhneKategorieAnwenden()` trägt bei jeder Buchung ohne Kategorie die erste passende Regel nach (Kategorie, Klasse, Gegenkonto oder Split) – ohne Treffer bleibt die Buchung unverändert, ein abgeschlossener Monat oder sonstiger Fehler betrifft nur die eine Buchung und bricht den Lauf nicht ab. - Der Knopf „Regeln auf Buchungen ohne Kategorie anwenden“ (mit Rückfrage) ersetzt den reinen Testlauf; die Regel-Trefferzähler zählen mit. - Test in `tests/16_BankingTest.php` (`buchungOhneKategorieWirdGefunden`, `regelnKategorisierenBuchungenOhneKategorie`). ## `update.sh` zieht aus dem Fork (25.09.2026) - Das Update-Skript holt den Code ausdrücklich per `git pull --ff-only origin main`. Vorher brach es ohne Tracking-Branch mit „Es gibt keine Tracking-Informationen“ ab. - Quelle ist der Fork, weil es derzeit nur die Entwicklungsumgebung gibt; der Upstream ist Backup und Veröffentlichung. Für eine spätere Produktion: `UPDATE_REMOTE=upstream`. - Das Skript bricht bei unverändertem Code nicht mehr ab: Container starten und Migrationen laufen immer, damit ein von Hand gezogener Stand nicht ohne Migration bleibt. - Der Pull läuft mit HTTP/1.1 (Codeberg liefert bei HTTP/2 sonst 504). ## Echtzeitüberweisung im Zahlungsverkehr (25.09.2026) Der Überweisungsdialog bietet jetzt das Häkchen **Echtzeitüberweisung**: Das Geld ist binnen Sekunden beim Empfänger. SEPA behandelt die Echtzeitüberweisung als eigenes Verfahren (SCT Inst), der Finanzmanager deshalb auch. - Über FinTS geht der Auftrag als Geschäftsvorfall HKIPZ hinaus (`SendSEPARealtimeTransfer` aus php-fints) statt als HKCCS. In der pain.001-Nachricht steht die Zahlungsart `LclInstrm/Cd = INST`; das gibt es nur im ISO-Format `pain.001.001.03`. - Eine Echtzeitüberweisung hat keinen Ausführungstermin: Der Dialog sperrt das Feld, ein Termin in der Zukunft wird beim Speichern abgewiesen. - Bietet die Bank keine Echtzeitüberweisung an, scheitert der Auftrag mit einer klaren Meldung und bleibt stehen. Eine stille Umwandlung in eine gewöhnliche Überweisung durch die Bank ist abgeschaltet. - Die Übersicht kennzeichnet Echtzeitüberweisungen in der Spalte „Ausführung“, das Übermittlungsprotokoll nennt sie ausdrücklich. - Neue Spalte `zahlungsauftraege.echtzeit` (Migration `019`). - Test 43 (`tests/43_EchtzeitueberweisungTest.php`) und ein E2E-Test für das Sperren des Termins. ## Übermittlungsprotokoll für Überweisungen und Bankkommunikation (21.09.2026) Als Nachweis gegenüber der Bank, dass eine Überweisung fristgerecht und korrekt übermittelt wurde, speichert der Finanzmanager jetzt den gesamten Dialog – gesendete und empfangene Meldungen im Wortlaut, im Erfolgs- wie im Fehlerfall. PIN und Benutzerkennung sind maskiert (das erledigt die FinTS-Bibliothek, bevor die Zeilen ankommen). - **Zahlungsverkehr:** Ein Klick auf den Status einer Überweisung öffnet ihr Übermittlungsprotokoll in einem modalen Dialog. Eine Überweisung mit TAN-Freigabe hat mehrere Durchläufe; alle stehen darin, der älteste zuerst. Ohne JavaScript führt der Status auf dieselbe Ansicht als eigene Seite. - **Verwaltung › Online-Banking › Protokoll** (`/banking/protokoll`): das Gesamtprotokoll über jede Kommunikation mit den Banken – Überweisungen, Umsatzabrufe und die Abfrage der TAN-Verfahren. Filterbar nach Bank, Zeitraum und Freitext. - Neue Tabelle `bank_protokolle` (Migration `018`), Modell `Bankprotokoll`, Mitschnitt über `Services\BankProtokollLogger`. Das Protokoll bleibt stehen, auch wenn die Überweisung gelöscht wird; es wird nicht automatisch aufgeräumt. - Test 42 (`tests/42_UebermittlungsprotokollTest.php`). ## Letzter roter E2E-Test aus dem Typeahead-Umbau behoben (04.09.2026) Ein Test blieb nach der Anpassung an den Typeahead (siehe unten) noch rot: „Bargeld-Tool › Eine vorgemerkte Kategorie wird beim Zuordnen geprüft“ – und zwar zuverlässig, nicht nur gelegentlich. Ursache war kein Rest des Umbaus mehr, sondern ein zu knapper Timeout: Die Schnellzuordnung im Kontoblatt prüft dabei gegen eine absichtlich ungültige Adresse (`kasse.beispiel.invalid`), und bis der serverseitige Abruf daran scheitert, vergehen durch `BargeldTool::ZEITGRENZE` (8 s Verbindungsaufbau) plus die Namensauflösung der ungültigen Adresse über 15 Sekunden – mehr, als Playwrights Standard-Timeout von 5 s für `toContainText()` hergibt. Die betroffenen Erwartungen tragen jetzt einen eigenen Timeout von 25 s, der Test insgesamt 90 s statt 60 s. ## E2E-Datenbestand wird vor jedem Lauf aufgeräumt (04.09.2026) Die Browser-Tests legten ihre Konten, Kategorien und Buchungen bei jedem Lauf neu an, weggeräumt hat sie niemand: Zuletzt standen über dreitausend Zeilen Testdaten in der Datenbank – vierzig „E2E Bankprovision …“ hintereinander im Kategoriefilter des Kontoblatts, und kein Bildschirmfoto eines fehlgeschlagenen Tests war mehr zu gebrauchen. `scripts/e2e-aufraeumen.php` räumt den Bestand weg; `tests/e2e/run.sh` ruft es vor jedem Lauf auf. Vor dem Lauf und nicht danach, weil ein abgebrochener Lauf sonst einen Bestand hinterlässt, an den niemand mehr denkt – und weil die Daten des letzten Laufs so bis zum nächsten für die Fehlersuche stehen bleiben. `E2E_KEIN_AUFRAEUMEN=1` lässt sie liegen, `--probe` zählt nur, was wegfiele. Gelöscht wird, was am Testnutzer hängt – nicht, was nach Testdaten aussieht. Ein Namensmuster übersähe die Buchung „Supermarkt“ und träfe irgendwann etwas Echtes; die Fremdschlüssel dagegen sagen genau, was zu diesem Nutzer gehört, und nehmen über ihre Kaskaden auch die Zeilen ohne eigene `benutzer_id` mit (Splitzeilen, Budgetpositionen, Bankkonten, Kurse). Der Nutzer selbst bleibt stehen, sonst käme der nächste Lauf nicht durch die Anmeldung. Als Schranke gegen den Fehlgriff muss er den Anzeigenamen tragen, den `scripts/e2e-benutzer.php` setzt: Wer `E2E_USER` versehentlich auf seinen eigenen Zugang stellt, bekommt einen Abbruch statt eines leeren Datenbestands. Das Aufräumen deckte auf, dass sieben Tests auf den Resten früherer Läufe standen: Die Auswertungen prüfen, dass ein Bericht eine Tabelle statt eines Platzhalters zeigt, das Cockpit, dass „Übrige Konten“ eine Ebene tiefer führt – Daten dafür legte keiner von ihnen an. Auf einer frisch aufgeräumten Instanz fielen sie aus; vorher hing ihr Ergebnis davon ab, wer zuletzt was hatte laufen lassen. `scripts/e2e-grundbestand.php` stellt deshalb nach dem Aufräumen einen festgelegten Bestand her, dessen Zuschnitte an genau diesen Tests hängen: mehr Konten als `Vermoegensentwicklung::SERIEN`, Buchungen in jedem Monat des laufenden Jahres, ein Vorjahr für den Vergleichsbericht, ein Depot mit Wertpapiergeschäft. Die Suite prüft damit dasselbe wie vorher, aber auf einem Bestand, den sie kennt. ## E2E-Tests an den Typeahead im Kontoblatt angepasst (04.09.2026) Zwölf E2E-Tests standen seit dem Typeahead-Umbau (Fix #41) auf Rot – nicht, weil die Anwendung etwas falsch machte, sondern weil die Specs den alten Stand prüften. Vier Ursachen lagen darunter: - **Die Kombobox versteckt ihr `` in ein Eingabefeld mit gefilterter Liste um, ohne dass App- oder Formular-Code etwas davon merken – Wert und Ändern-Ereignis bleiben identisch. Geprüft wurde außerdem, dass „Empfänger ins Adressbuch übernehmen“ beim Erfassen einer Überweisung tatsächlich eine Adresse anlegt (`ZahlungsverkehrController::anlegen()` → `Adresse::anlegen()`). ## Zahlungsverkehr: automatische Bankauswahl (31.08.2026) Beim Erfassen einer Überweisung musste bisher neben dem Auftragskonto auch die Bankverbindung von Hand gewählt werden – obwohl sich diese meist schon aus dem Konto ergibt (Feature #50). Das Formular fragt jetzt nur noch das Auftragskonto ab; die passende Bank ermittelt `Bankzugang::zugangJeKonto()` anhand der Kontenzuordnung (`bank_konten.konto_id`) und trägt sie beim Speichern automatisch in `zahlungsauftraege.zugang_id` ein. Konten ohne konfigurierte Bankverbindung stehen als Auftragskonto gar nicht erst zur Auswahl – ohne Bank ließe sich ohnehin nichts senden. Ein bereits erfasster Auftrag behält sein Auftragskonto auch dann, wenn dessen Bank nachträglich entfernt wurde: Es bleibt in der Auswahl, statt beim nächsten Speichern unbemerkt zu wechseln. ## Einstellungen: Reiter "Profil" zum Ändern des eigenen Passworts (20.08.2026) Bislang ließ sich das eigene Passwort nur direkt über den Datenbestand ändern (Feature #46). Der Einstellungsdialog bekommt dafür einen neuen Reiter `Profil`, der den angemeldeten Benutzernamen anzeigt und ein eigenes Formular für die Passwortänderung enthält – getrennt vom generischen Speichern-Formular der übrigen Reiter, weil hier kein Einstellungswert, sondern der Anmeldedatensatz geschrieben wird. `EinstellungenController::passwortAendern()` (Route `POST /einstellungen/passwort`) verlangt das aktuelle Passwort (geprüft über `Benutzer::pruefeAnmeldung()`), ein neues mit mindestens 10 Zeichen – dieselbe Grenze wie bei der Ersteinrichtung – und dessen Wiederholung. Bei Erfolg schreibt `Benutzer::passwortSetzen()` den neuen Hash; ein Eintrag landet wie bei An- und Abmeldung im Debug-Log. ## CONTRIBUTE.md und Setup-Skript für Mitwirkende (19.08.2026) `CONTRIBUTE.md` beschreibt, wie Beiträge ankommen (Issue, PR, E-Mail), welche Unterstützung gesucht wird und die drei bindenden Leitlinien des Projekts: FOSS, Datensparsamkeit und Security mit direktem Kontakt an fds2610@sonnengetrock.net für Sicherheitslücken. Vorangestellt ist ein Disclaimer, dass VIBE Coding ausdrücklich erlaubt ist – das Projekt selbst ist mit intensivem LLM-Einsatz entstanden (Feature #43). Neu ist außerdem `dev-setup.sh` im Repo-Root: richtet Docker (optional), Checkout mit Fork- und Upstream-Remote, lokale Container und optional die E2E-Testinfrastruktur ein. Zielverzeichnis, E2E-Setup und Docker-Installation lassen sich über Flags oder Umgebungsvariablen abweichend vom Default setzen. ## Kontoblatt: Typeahead für die Kategorie (19.08.2026) Die Kategorie ließ sich im Kontoblatt nur über eine lange Auswahlliste finden – bei vielen Kategorien ein langes Blättern (Feature #41). Ein neues `kombobox.js` baut ein markiertes `` bleibt unsichtbar im Formular stehen, damit Wert, Name und das Ändern-Ereignis für vorhandenen Code unverändert bleiben. Betroffen sind der Kategorie-Filter oben im Kontoblatt und die Schnellzuordnung je Buchung (`kontoblatt.js` öffnet dort die Kombobox statt `showPicker()`, sobald ein `` – ihre Liste ist kurz. Ohne JavaScript bleibt es überall beim normalen `