Eine falsche Splitsumme zeigte zwar die Fehlermeldung, leitete aber auf eine
frische Buchungsmaske weiter und riss dabei alle gerade erfassten Splitzeilen
(und übrigen Feldwerte) heraus, obwohl in der Datenbank nichts geändert wurde.
anlegen()/speichern() zeichnen die Maske bei einem Validierungsfehler jetzt
direkt mit den zuletzt abgeschickten Werten neu, statt wegzuleiten.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Die Summenzeile der Splitbuchungen (Buchungsmaske und Splitdialog im
Kontoblatt) zeigt jetzt den noch offenen Restbetrag zum Buchungswert statt
der Summe der Zeilen. „Speichern" bleibt gesperrt, solange Splitzeilen
befüllt sind und der Restbetrag nicht Null ist; ohne Splitzeilen (auch mit
direkter Kategorie/Gegenkonto) bleibt der Knopf wie bisher an.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
In der Buchungsmaske sperrt eine Eingabe in den Splitzeilen sofort
Kategorie / Gegenkonto und Klasse, und umgekehrt sperrt eine Kategorie
oder Klasse alle Splitzeilen. Der neue Knopf „Splitbuchungen löschen“
rechts über der Splittabelle leert alle Zeilen und gibt die direkten
Felder wieder frei.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bei einer Splitbuchung zeigt die Klassenspalte jetzt die gemeinsame Klasse
der Splitzeilen. Unterscheiden sie sich, steht dort die Marke „Splitbuchung“,
der Tooltip nennt je Splitzeile Kategorie und Klasse. Ein Klick auf die
Klassenzelle einer Splitbuchung öffnet den Splitdialog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zwölf Tests standen auf Rot, ohne dass die Anwendung etwas falsch machte – die
Specs prüften den Stand vor dem Umbau. Vier Ursachen lagen darunter:
Die Kombobox lässt ihr <select> zwar im Formular stehen, setzt es aber auf
hidden; selectOption() wartet darauf vergeblich. Der neue Helfer
tests/e2e/specs/hilfen.js bedient das Eingabefeld, tippt die Beschriftung ein
und klickt den genau passenden Treffer an – exakt, weil [Depot] sonst auch das
[Depot] … des Wertpapiergeschäfts träfe.
Seit dem Schnellspeichern ohne Neuladen stehen zwei Erfolgsmeldungen
übereinander; gemeint ist die neue, also .last().
Die Adressliste ist seit der Adressverwaltung eine Bearbeitungstabelle: Die
Namen stehen in Eingabefeldern, wo toContainText nicht hinsieht. Geprüft wird
jetzt der Feldwert – gespeichert wurde die Adresse die ganze Zeit korrekt.
Die beiden übrigen Fehlschläge waren Folgefehler: Playwright startet nach jedem
Fehlschlag einen frischen Worker, das Spec-Modul wird neu geladen, seine Marke
neu gezogen und beforeAll legt leere Konten an. Sie brauchten keine Änderung.
87 Tests grün, und ohne die Timeouts in 2,7 statt 7,1 Minuten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AQ57wQWZ4xKHcKmp26bzgr
Das Ändern von Kategorie oder Klasse im Kontoblatt lud bisher die ganze
Seite neu, um die Zeile zu aktualisieren – dabei sprang der Bildschirm
nach ganz oben. kontoblatt.js schickt die Auswahl jetzt per fetch im
Hintergrund; Kategorie, Klasse und das Symbol der Bargeld-Abgleichspalte
aktualisieren sich an Ort und Stelle, die Bildlaufposition bleibt stehen.
Ohne JavaScript bleibt es bei der bisherigen Weiterleitung.
Neue Buchung und Buchung bearbeiten zeigen oben das Konto, in dem die
Buchung liegt. Die Maske steht zweispaltig, die Beschriftungen links
neben ihren Eingaben und die Erklärungen in deren Tooltip – damit endet
sie samt Splitzeilen und Knopfleiste oberhalb der Fensterhöhe.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Klassenspalte ist anklickbar wie die Zuordnung daneben: Ein Klick
öffnet die Klassenliste in der Zelle, die Auswahl wird sofort über
POST /buchungen/{id}/klasse gespeichert. Buchung::klasseSetzen() lässt
Kategorie, Gegenkonto und Splitzeilen unberührt.
Die Buchungszeilen sind jetzt alle zwei Textzeilen hoch, ihr Inhalt steht
senkrecht in der Mitte. Was nicht auf zwei Zeilen passt, wird
abgeschnitten; den vollständigen Text zeigt der Tooltip – bei Empfänger
und Buchungstext nur dort, wo wirklich etwas fehlt, über dem
Kategoriefeld einer Splitbuchung deren Zeilen mit Kategorie und Betrag.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eine Umbuchung war auf dem Gegenkonto nicht mehr zuzuordnen: Die Zeile trug die
Marke „Umbuchung“, die Kategoriespalte daneben blieb leer. Die Daten waren
richtig, die Anzeige verschwieg sie. In der Kategoriespalte steht jetzt auch in
der Gegensicht das Konto in eckigen Klammern, von dem die Umbuchung kommt – die
Abfrage lieferte es längst mit.
Kategorie und Gegenkonto stehen in einem Feld (`Views/buchungen/zuordnungsliste.php`):
Unter den Kategorien stehen die eigenen Konten in eckigen Klammern, wie im
FinanzManager, dessen Kategoriefeld `[Konto]` annimmt. Eines davon macht die
Buchung zur Umbuchung; die Auswahl „Ausgabe / Einnahme“ bestimmt seither nur
noch die Richtung. Dieselbe Liste steht in den Splitzeilen – eine Splitzeile mit
Gegenkonto ließ sich bisher nur im Import anlegen und ging beim Bearbeiten der
Buchung stillschweigend verloren.
Schnellzuordnung: Ein Klick auf die Kategoriespalte öffnet die Liste in der
Zelle, die Auswahl speichert sofort (POST /buchungen/{id}/zuordnung) und kommt
in dieselbe Ansicht zurück. Die Liste gibt es einmal und wandert in die
angeklickte Zelle – eine Liste je Zeile wären bei hundert Zeilen ebenso viele
vollständige Kategoriebäume im HTML. Feld und Liste sind gleich breit und gleich
hoch, sonst rückte beim Anklicken die halbe Zeile. Ohne JavaScript führt
derselbe Klick in die Buchungsmaske: Die Zelle ist ein Link. Die Gegenseite
einer fremden Umbuchung führt zu ihrer Buchung auf dem anderen Konto.
Der Eintrag „Splitbuchung“ öffnet ein modales Fenster mit Zeilen für Kategorie
oder Gegenkonto, Klasse, Notiz und Betrag; der Restbetrag wird mitgerechnet. Ein
Klick auf eine bereits gesplittete Buchung öffnet direkt den Dialog. „Splitbuchung
löschen“ verwirft alle Zeilen samt ihren Zuordnungen – auch die Gegenseite einer
Splitzeile mit Gegenkonto –, die Buchung steht danach unzugeordnet im Kontoblatt.
Beide Wege gehen über `Buchung::zuordnen()` und `Buchung::splitsErsetzen()`,
achten den Monatsabschluss und lassen den Rest der Buchung unberührt.
`public/static/betrag.js` bündelt das Lesen deutscher Beträge und die
Restbetragsrechnung für Maske und Kontoblatt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Kontenzuordnung als dritter Einrichtungsschritt: Welches Bankkonto gehört zu
welchem eigenen Konto? Umsätze merken sich über `bank_konto_id`, woher sie
stammen, und ziehen mit, wenn die Zuordnung erst nach einem Abruf fällt.
Abgerufene Umsätze buchen sich auf Wunsch selbst (`Services\Bankabgleich`).
Im Kontoblatt lassen sich Buchungen sammelweise löschen; Statusmeldungen liegen
jetzt über der Ansicht, statt den Inhalt springen zu lassen.
Dazu drei Fehler, die zusammen dafür sorgten, dass abgerufene Umsätze spurlos
verschwanden – der Abruf meldete sie als vorhanden, das Kontoblatt zeigte
nichts, und in der Vorschlagsliste standen sie ebenfalls nicht:
- Eine gelöschte Buchung ließ ihren Umsatz auf „uebernommen“ stehen. Der
Fremdschlüssel setzte nur `buchung_id` auf NULL. `Buchung::loeschen()` gibt
den Umsatz jetzt vorher frei, `Bankumsatz::verwaisteOeffnen()` fängt die Wege
ab, die daran vorbeiführen – Kaskaden lösen in MariaDB keine Trigger aus.
- Ein Bankkonto entstand doppelt, wenn die Bank die IBAN nicht bei jedem Dialog
mitliefert. Gesucht wird jetzt über die Kontonummer; eine nachgelieferte IBAN
wird am vorhandenen Eintrag nachgetragen.
- Der Fingerabdruck hing an der IBAN und kippte mit ihr. Er nimmt jetzt die
`bank_konto_id`, die fest steht, solange es den Bankzugang gibt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Plan ging davon aus, Umbuchungen stünden zweimal im Export und müssten
zusammengeführt werden. Der Vollexport zeigt das Gegenteil: Eine Umbuchung
steht einmal, in dem Konto, in dem sie erfasst wurde; die Gegenseite ist nur
durch L[Zielkonto] benannt und muss beim Import erzeugt werden. Von 965
Umbuchungen hat keine ein zweites Vorkommen. Mit erzeugter Gegenseite stimmen
acht gegen den FinanzManager geprüfte Kontosalden auf den Cent.
Datenmodell: Migration 003 mit buchungen, buchung_splits und buchungsadressen.
Eine Umbuchung ist ein Datensatz mit gegenkonto_id, kein gespiegeltes Paar -
zwei Zeilen laufen früher oder später auseinander. Das Kontoblatt des
Gegenkontos zeigt sie mit umgekehrtem Vorzeichen. Auch einzelne Splitzeilen
dürfen ein Gegenkonto tragen. Konten führen zusätzlich in_summe, die
Sigma-Spalte der Kontenübersicht, und saldo_online für Phase 5.
Oberfläche: Kontoblatt mit laufendem Saldo, Filter auf Zeitraum, Kategorie,
Klasse und Abgleich, Volltextsuche und Seitenblättern. Buchungsmaske für
Einnahme, Ausgabe und Umbuchung in beide Richtungen, mit Splitzeilen und
Restbetragsanzeige.
Import: QifBuchungsImport arbeitet mit Stichtag. Alles davor wird je Konto zum
Anfangssaldo aufsummiert, Gegenbuchungen eingerechnet; alles danach bis zum
Importtag kommt einzeln in die Datenbank. Spätere Buchungen sind
Vorausberechnungen des FinanzManagers und bleiben draußen. Wertpapiergeschäfte
schlagen vorerst nur mit ihrem Geldfluss auf dem Verrechnungskonto zu Buche.
Am Vollexport geprüft: 833 Buchungen ab 01.01.2026, 7946 in die Anfangssalden
von 35 Konten verrechnet.
Behoben: Die confirm()-Rückfragen vor dem Löschen waren wirkungslos.
public/static/app.js brachte den Mechanismus mit, war aber nie im Layout
eingebunden; die ersatzweise gesetzten onsubmit-Attribute verstoßen gegen die
eigene Content-Security-Policy und wurden vom Browser verworfen - gelöscht
wurde ohne Rückfrage. Ein E2E-Test besteht jetzt auf dem Dialog.
Tests: 307 PHP-Prüfungen und 21 Playwright-Tests, alle grün.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>