# Änderungen ## 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 `