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