Finanzmanager/Changes.md
fds2610-bot 20b2406219 Budgetplaner: drei Ansichten und Verteilen über das Zahnrad
Abgelaufene Monate werden nicht mehr zusammengefasst – alle zwölf Monate
stehen einzeln da und bleiben beplanbar. Statt Verlauf und alle Monate
gibt es Alles, Nur Budget und Nur IST; die Budgetspalte ist überall dort
editierbar, wo sie steht.

Das Zahnrad vor jedem Feld verteilt dessen Wert auf das ganze oder das
restliche Jahr. Es arbeitet auf den Eingabefeldern statt auf der
Datenbank: Das Ergebnis ist vor dem Speichern zu sehen und geht mit
demselben Knopf weg wie jede andere Änderung.

Nebenbei: app.js war doppelt eingebunden, jeder Handler lief zweimal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 08:22:35 +02:00

12 KiB
Raw Blame History

Änderungen

Budgetplaner: drei Ansichten und Verteilen über das Zahnrad (11.08.2026)

  • Abgelaufene Monate werden nicht mehr zusammengefasst. Alle zwölf Monate stehen einzeln da; die Sammelspalte „Januar–Vormonat“ entfällt samt Budgetplaner::abgelaufenBis() und Budgetplaner::summe().
  • Statt Verlauf und alle Monate gibt es drei Ansichten: Alles (Budget, Ist und Saldo je Monat), Nur Budget und Nur IST mit je einer Spalte. Alte URLs mit ansicht=verlauf|alle fallen auf Alles zurück.
  • Die Budgetspalte ist überall dort, wo sie steht, editierbar – auch in vergangenen Monaten.
  • Vor jedem Eingabefeld sitzt ein ⚙: Ganzes Jahr oder Restliches Jahr schreibt den Wert des Feldes in die übrigen Monate der Zeile und überschreibt, was dort stand. Gearbeitet wird auf den Eingabefeldern, sichtbar vor dem Speichern; der Knopf ↦ und Budget::aufFolgemonateUebertragen() entfallen dafür.
  • app.js war doppelt eingebunden (Kopf und Fuß): Jeder Handler lief zweimal, jede Rückfrage kam zweimal. Jetzt nur noch im Kopf.

Phase 3 – Budgetplaner und Monatsabschluss (11.08.2026)

Datenmodell

  • Migration 004_budget_monatsabschluss.sql: budgets, budget_positionen, monatsabschluesse
  • Ein Budget gilt für ein Jahr, mehrere je Jahr sind möglich, genau eines ist aktiv
  • Ein Monat ohne Zeile in budget_positionen ist nicht geplant – etwas anderes als mit 0,00 geplant

Budgetplaner

  • Zeilen sind der Kategoriebaum (auf- und zuklappbar), Spalten die Monate; die ganze Tabelle wird in einem Formular gespeichert, damit mehrere Änderungen zusammen ankommen
  • Zwei Ansichten: Verlauf (Jahressumme, abgelaufene Monate zusammengefasst, laufender Monat mit Budget/Ist/Saldo, danach Folgemonate) und alle Monate zum Planen
  • Ausgaben werden ohne Vorzeichen eingetragen; das Vorzeichen kommt aus der Kategorieart. Saldo ist Ist minus Budget – positiv heißt besser als geplant, negativ ist die Überschreitung und wird rot ausgewiesen
  • Übergeordnete Kategorien summieren ihre Nachfahren und sind selbst nicht beplanbar
  • Wert auf alle Folgemonate übertragen, Budget als Kopie ins neue Jahr

Monatsabschluss

  • Prüflauf über nicht kategorisierte Buchungen, nicht abgeglichene Umsätze und Splitsummen-Differenzen; er hält niemanden auf, sondern zeigt den Stand
  • Snapshot der Kontosalden und Kategoriesummen als JSON, auch nach Wiedereröffnung
  • Abgeschlossene Monate sperren Buchungen (Buchung::anlegen/speichern/loeschen), in beide Richtungen: weder hinein- noch herausschieben. Abschaltbar über die Einstellung monatsabschluss_sperrt_buchungen
  • Wiedereröffnen bleibt am Datensatz vermerkt

Vorarbeiten in der Oberfläche

  • .knopf.primaer trug einen eigenen margin-top und stand dadurch in jeder Knopfleiste tiefer als seine Nachbarn – sichtbar etwa an „Abbrechen“ neben „Konto anlegen“. Der Abstand liegt jetzt allein bei der Knopfleiste.
  • Kontengruppen werden wie Konten und Kategorien auf einer eigenen Formularseite gepflegt statt inline in der Tabelle. Dort hängt auch die Zuordnung der Konten: ankreuzen statt jedes Konto einzeln zu öffnen (Kontengruppe::kontenZuordnen()).
  • Der QIF-Export enthält keine Kontengruppen – kein Feld im !Account-Eintrag, nichts in der QSD-Datei. Import-Oberfläche und Dokumentation sagen das jetzt und verweisen auf die Zuordnung.
  • Die Einrückung des Kategoriebaums lief über ein style-Attribut und wurde von der Content-Security-Policy (style-src 'self') stillschweigend verworfen; sie steht jetzt als data-ebene im Markup. Aus demselben Grund blendete app.js Erfolgsmeldungen nie aus – das läuft jetzt über eine Klasse statt über element.style.

Tests

  • 11_BudgetTest, 12_MonatsabschlussTest (76 Prüfungen), E2E planung.spec.js
  • Einzelheiten in docs/planung.md

Korrektur – Σ-Kennzeichen ließ sich nicht abschalten (10.08.2026)

Das Kennzeichen Zählt zur Summe der Kontostände blieb beim Speichern eines Kontos unverändert stehen, obwohl die Anwendung „Konto gespeichert“ meldete.

  • Ursache: KontenController::formulardaten() reichte in_summe gar nicht durch. Ein nicht angehaktes Ankreuzfeld steht nicht im POST, also griff im Model der Standardwert „zählt mit“ – gespeichert wurde ein Ja, das niemand angegeben hatte.
  • formulardaten() nimmt das POST-Array jetzt als Parameter und ist damit ohne HTTP prüfbar; die Ankreuzfelder werden ausdrücklich auf true/false abgebildet.
  • Der Standard „zählt zur Summe“ bleibt für Aufrufe ohne den Schlüssel bestehen – der QIF-Import gibt das Feld nicht an und soll es auch nicht.
  • 07_KontenTest prüft die Formularabbildung und den Weg durch das Model; E2E stammdaten.spec.js hakt das Kennzeichen in der Oberfläche ab, setzt es wieder und sieht in beiden Fällen nach, statt der Erfolgsmeldung zu glauben.

Phase 2 – Buchungen, Kontoblatt und Buchungsimport (10.08.2026)

Erkenntnis aus dem Vollexport

Der Plan ging davon aus, Umbuchungen stünden zweimal in der Datei und müssten zusammengeführt werden. Das Gegenteil ist der Fall: 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 im Export hat keine ein zweites Vorkommen. Mit erzeugter Gegenseite stimmen acht gegen den FinanzManager geprüfte Kontosalden auf den Cent.

Datenmodell

  • Migration 003_buchungen.sql: buchungen, buchung_splits, buchungsadressen
  • Eine Umbuchung ist ein Datensatz mit gegenkonto_id, kein gespiegeltes Paar – das Kontoblatt des Gegenkontos zeigt sie mit umgekehrtem Vorzeichen
  • Auch einzelne Splitzeilen dürfen ein Gegenkonto tragen
  • Konten führen in_summe (die Σ-Spalte des FinanzManagers) sowie saldo_online

Kontoblatt und Buchungsmaske

  • 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; Splitzeilen mit Restbetragsanzeige, geprüft auch serverseitig
  • Aktueller und erwarteter Saldo; in die Summen zählen nur Konten mit Σ-Kennzeichen

QIF-Buchungsimport

  • QifBuchungsImport mit Stichtag: alles davor wird je Konto zum Anfangssaldo aufsummiert – einschließlich der erzeugten Gegenbuchungen –, alles danach importiert
  • Buchungen mit Datum nach dem Importtag gelten als Vorausberechnung und bleiben draußen
  • Eröffnungsbuchungen (L[eigenes Konto]) werden nicht zu Umbuchungen
  • Wertpapiergeschäfte schlagen vorerst nur mit ihrem Geldfluss auf dem Verrechnungskonto zu Buche; Bestände folgen in Phase 6
  • Idempotent über einen Fingerabdruck je Buchung, der auch echte Doppelbuchungen am selben Tag auseinanderhält
  • Am Vollexport geprüft: 833 Buchungen ab 01.01.2026, 7946 in die Anfangssalden von 35 Konten verrechnet, eine vorausberechnete verworfen

Behoben

  • Die confirm()-Rückfragen vor dem Löschen waren wirkungslos. public/static/app.js brachte den Mechanismus (data-bestaetigen) von Anfang an mit, war aber nie im Layout eingebunden; ersatzweise standen Inline-Handler (onsubmit) im HTML, und die verstoßen gegen die eigene Content-Security-Policy (script-src 'self') – der Browser verwarf sie stillschweigend, gelöscht wurde ohne Rückfrage. Jetzt ist das Skript eingebunden, die Inline-Handler sind raus, und ein E2E-Test besteht auf dem Dialog.

Tests

  • 09_BuchungenTest – Umbuchungen mit zwei Sichten, Splitzeilen samt Summenprüfung, Splitzeile mit Gegenkonto, aktueller und erwarteter Saldo, Σ-Kennzeichen, Filter
  • 10_QifBuchungsImportTest – Stichtag, Anfangssalden aus der Vorgeschichte, erzeugte Gegenbuchung, Eröffnungsbuchung, Zukunftsfilter, zweiter Lauf ohne Wirkung
  • E2E buchungen.spec.js – Ausgabe, Umbuchung auf beiden Kontoblättern, Splitbuchung, abgewiesene Splitsumme, Suche, Löschbestätigung

Phase 1 – Stammdaten und QIF-Stammdatenimport (09.08.2026)

Datenmodell

  • Migration 002_stammdaten.sql: kontengruppen, konten, kategorien, klassen
  • Alle Tabellen führen benutzer_id; Mandant::id() liefert die Kennung aus der Session oder – im CLI – vom ersten aktiven Benutzer

Konten

  • Kontenübersicht nach Kontengruppen gebündelt, mit Gruppensummen und Gesamtsumme
  • Die drei Saldospalten des FinanzManagers (Online-Saldo, aktueller und erwarteter Saldo) sind angelegt und werden bis Phase 2 aus dem Anfangssaldo gespeist
  • Kontoarten des FinanzManagers samt Zuordnung zu den QIF-Typen
  • Kontenpflege mit Anfangssaldo, Kreditlimit, IBAN/BIC, Kontengruppe, aktiv/inaktiv
  • Kontengruppen anlegen, umbenennen, sortieren, löschen – die Konten bleiben erhalten

Kategorien und Klassen

  • Hierarchische Kategorien mit : als Trenner; der vollständige Pfad wird mitgeführt und beim Umbenennen oder Verschieben für den ganzen Teilbaum neu geschrieben
  • Art (Einnahme/Ausgabe) gilt für den ganzen Ast und vererbt sich an Unterkategorien
  • Verschieben, Zusammenführen und Löschen; Verschieben in den eigenen Teilbaum wird abgewiesen
  • Register Alle / Einnahmen / Ausgaben / Umbuchungskonten wie im Original
  • Klassen als flache zweite Auswertungsdimension, in der Liste direkt bearbeitbar

QIF-Import

  • QifParser: zustandsbehaftetes, sequenzielles Lesen; ISO-8859-1 nach UTF-8, US-Beträge, Datumsformat aus der Kopfzeile, Mehrfachfelder für spätere Splitzeilen
  • QifStammdatenImport für Klassen, Kategorien und Konten – idempotent, mit Trockenlauf und Protokoll; fehlende Zwischenstufen im Kategoriepfad entstehen selbsttätig
  • Dialog unter Verwaltung → QIF-Import mit Upload, Trockenlauf und Übernahme
  • scripts/qif-import.php für dieselbe Aufgabe auf der Kommandozeile
  • Geprüft am echten Export: 17 Klassen, 105 Kategorien, 37 Konten

Tests

  • 06_KategorienTest – Hierarchie, Pfadpflege, Artvererbung, Verschieben, Zusammenführen
  • 07_KontenTest – Kontoarten, Gruppierung und Summen, Betragseingabe
  • 08_QifImportTest – Parser, Zeichensatz, Beträge und Datum, Import, Trockenlauf, Prüfung der Dateinamen
  • E2E stammdaten.spec.js – Konto, Kategorie mit Unterkategorie, Register, Klasse, QIF-Upload mit Trockenlauf

Korrektur – Monatsnamen auf Deutsch (09.08.2026)

  • Der Einstellungsdialog zeigte unter Buchhaltung englische Monatsnamen, weil date('F') sprachlich nicht umschaltbar ist.
  • Monatsnamen liegen jetzt zentral als View::MONATE und View::MONATE_KURZ vor, abrufbar über View::monat(). Budgetplaner und Auswertungen greifen später darauf zu.
  • 05_DarstellungTest prüft Monatsnamen, Betrags- und Datumsformat gegen eine feste Sollliste – der Vergleich gegen date('F') allein würde April, August, September und November nicht auffallen lassen.

Phase 0 – Projektgerüst (09.08.2026)

Grundgerüst der Anwendung. Fachliche Module folgen ab Phase 1.

Betrieb

  • Docker Compose mit drei Services: nginx (Port 8000), php (php-fpm 8.3), db (MariaDB 11)
  • docker-entrypoint.sh legt Datenverzeichnisse an und spielt Migrationen ein
  • scripts/migrate.php – nummerierte SQL-Migrationen, idempotent, mit Journal-Tabelle
  • scripts/update.sh – Update für den Produktionsserver: Backup, git pull, Image-Neubau bei Bedarf, Migrationen, Rechte, Tests
  • scripts/backup.sh – Datenbank- und Dateisicherung mit Rotation (7 Tage / 4 Wochen / 12 Monate), gegen Parallelläufe per flock abgesichert

Anwendung

  • Config, Database (PDO-Wrapper, verschachtelbare Transaktionen), Router (Platzhalter-Routen, CSRF-Prüfung für alle verändernden Requests), View, Auth
  • Anmeldung mit Session-Härtung, Ersteinrichtung des ersten Benutzers
  • Zentraler Einstellungsdialog mit Reitern (Allgemein, Darstellung, Buchhaltung, Online-Banking, Import/Export, Wertpapiere, Diagnose); Werte in Tabelle einstellungen
  • Debug-Log, ein- und ausschaltbar, mit Maskierung von PINs, Passwörtern, TANs und API-Keys; Ausnahmen werden immer protokolliert
  • Layout nach Vorbild des FinanzManagers: feste Navigation links, Werkzeugleiste oben, Tab-Leiste; Farbschema hell/dunkel/System

Tests

  • Test-Harness mit Transaktions-Rollback und Aufräumen temporärer Pfade
  • 01_AuthTest – Anmeldung, Passwort-Hashing, CSRF, Cookie-Attribute
  • 02_EinstellungenTest – Lesen/Schreiben, Cache-Invalidierung, Bereichsabdeckung
  • 03_RouterTest – Pfad-Muster, Platzhalter, keine Treffer über Pfadgrenzen
  • 04_DebugLogTest – Schalter, Maskierung, Fehlerprotokollierung