Fünf Berichtstypen wie im FinanzManager – Summen-, Buchungs-, Vergleichs-, Kontostands- und Budgetbericht – mit Filterleiste, gespeicherten Berichten in Berichtsgruppen, Drilldown in die Buchungen und CSV-Export. Ein Bericht speichert nur seine Frage; die Zahlen entstehen bei jedem Aufruf neu aus den Buchungen. Gezählt werden Bewegungen (gesplittete Buchungen über ihre Splitzeilen), Umbuchungen bleiben außen vor, Unkategorisiertes bekommt eine eigene Zeile. Im Ausgabenabschnitt steht der Betrag ohne Minus wie im Original, die Schlusszeile rechnet mit den echten Vorzeichen. Einzelheiten in docs/auswertungen.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
343 lines
20 KiB
Markdown
343 lines
20 KiB
Markdown
# Änderungen
|
||
|
||
## Phase 4 – Auswertungen mit Berichtsmodul (11.08.2026)
|
||
|
||
**Datenmodell**
|
||
- Migration `005_berichte.sql`: `berichte`, `berichtsgruppen`
|
||
- Ein Bericht ist Name, Typ und Filter – die Zahlen entstehen bei jedem Aufruf neu aus den
|
||
Buchungen. Gespeichert wird ausschließlich die Frage.
|
||
- Der Filter liegt als JSON in einer Spalte und läuft beim Lesen immer durch
|
||
`Auswertung::normalisiere()`: Unbekannte Felder fallen weg, unbekannte Werte auf die
|
||
Vorgabe zurück. Keine eigene Spalte je Filterfeld – das sind Anzeigeparameter ohne
|
||
Fremdschlüssel, und jede neue Filtermöglichkeit bräuchte sonst eine Migration.
|
||
- Keine neuen Einstellungen: Zeitraum, Zeilen-Summe und Einrückung gehören zum einzelnen
|
||
Bericht, nicht in den Einstellungsdialog.
|
||
|
||
**Fünf Berichtstypen** (`src/Services/Auswertung.php`)
|
||
- **Summenbericht**: Summen je Zeilen-Summe und Spalte, getrennt nach Einnahmen und
|
||
Ausgaben, mit Schlusszeile *Gesamt Einnahmen − Ausgaben*
|
||
- **Buchungsbericht**: die Einzelbuchungen, gruppiert mit Zwischensummen
|
||
- **Vergleichsbericht**: Zeitraum gegen denselben Zeitraum des Vorjahres, mit Differenz
|
||
und Veränderung in Prozent
|
||
- **Kontostandsbericht**: Salden je Konto zu den Enden der Spaltenzeiträume
|
||
- **Budgetbericht**: Budget gegen Ist je Kategorie, mit Saldo
|
||
|
||
**Was gezählt wird**
|
||
- Bewegungen statt Buchungen: nicht gesplittete Buchungen mit ihrem Betrag, gesplittete
|
||
über ihre Splitzeilen – sonst zählte eine Splitbuchung doppelt oder gar nicht.
|
||
- **Umbuchungen bleiben außen vor** (weder Einnahme noch Ausgabe, wie im Budgetplaner).
|
||
Nur der Buchungsbericht führt sie auf Wunsch mit; sie erscheinen dort mit dem Gegenkonto
|
||
in eckigen Klammern.
|
||
- Unkategorisierte Buchungen stehen als eigene Zeile *Nicht kategorisiert* am Ende ihres
|
||
Abschnitts, statt lautlos aus der Summe zu fallen.
|
||
- Einnahme oder Ausgabe entscheidet die **Art der Kategorie**, nicht das Vorzeichen: Eine
|
||
Erstattung ist eine negative Ausgabe und gehört unter die Ausgaben.
|
||
- Aggregiert wird in PHP. Die Datenbank liefert die Bewegungen, das Einsortieren in Zeilen
|
||
und Spalten passiert im Service – Baumsummen, Vorzeichenregeln und Spaltenraster wären
|
||
in SQL erheblich schwerer nachzulesen.
|
||
|
||
**Vorzeichen**
|
||
- Intern durchgehend Buchhaltung (Ausgaben negativ), in der Anzeige dreht der
|
||
Ausgabenabschnitt sein Vorzeichen – wie im FinanzManager steht dort 636,39 statt
|
||
−636,39. Die Schlusszeile rechnet mit den echten Werten.
|
||
- Buchungs- und Budgetbericht sowie der Drilldown behalten das Vorzeichen: Sie haben keine
|
||
Abschnitte, die es erklären.
|
||
|
||
**Zeitraum und Spalten** (`src/Services/Zeitraum.php`, reine Kalenderrechnung ohne Datenbank)
|
||
- Neun Vorgaben von *Aktueller Monat* bis *Gesamter Zeitraum*; sie rechnen sich bei jedem
|
||
Aufruf neu aus dem Tagesdatum aus. Ein Bericht mit *Letzter Monat* zeigt im August den
|
||
Juli, nicht für immer den Monat seiner Entstehung.
|
||
- Spalten-Aufteilung nach Monat, Quartal oder Jahr; die Randspalten werden auf den
|
||
Zeitraum gestutzt.
|
||
- *Gesamter Zeitraum* nimmt die Datumsgrenzen der Buchungen (`Buchung::datumsgrenzen()`)
|
||
und endet spätestens heute – ein Ende in der Zukunft brächte nur leere Spalten.
|
||
- Beim Vorjahresvergleich bleibt ein Monatsende ein Monatsende, sonst träfe der 28.02. auf
|
||
einen 29.02.
|
||
|
||
**Summenbericht als Baum**
|
||
- Jede Kategorie zeigt ihre eigenen Werte, Kategorien mit Nachfahren bekommen dahinter
|
||
*Gesamt <Name>* mit der Astsumme. Eine Elternkategorie ohne eigene Buchungen steht als
|
||
bloße Kopfzeile da – genau wie im Original.
|
||
- *ohne Einrückung* schaltet auf eine flache Liste mit vollem Pfad und ohne Astsummen.
|
||
- Ein Kategoriefilter schließt die Unterkategorien ein: Wer „400 - Leben“ wählt, meint den
|
||
ganzen Ast.
|
||
|
||
**Oberfläche**
|
||
- Übersicht mit Kachel *Standardvorlagen* und je einer Kachel pro Berichtsgruppe,
|
||
Menü *Letzte Berichte*, Verwaltung der Gruppen unter `/auswertungen/gruppen`.
|
||
- Filterleiste sendet per GET: Jeder Zwischenstand ist eine Adresse, die man sich merken,
|
||
weitergeben und mit *Speichern unter* festhalten kann.
|
||
- *Speichern unter* kann die Berichtsgruppe gleich mit anlegen – wer sie erst anderswo
|
||
erzeugen müsste, verlöre den eingestellten Filter.
|
||
- Eine gelöschte Gruppe nimmt ihre Berichte nicht mit: Sie rücken unter *Nicht gruppiert*.
|
||
- Die Aufklappmenüs sind `<details>`-Elemente, kein JavaScript.
|
||
- Der Platzhalter für `/auswertungen` entfällt aus dem `PlatzhalterController`.
|
||
|
||
**Drilldown**
|
||
- Zeilenname und jede Zahl führen in die Buchungen dahinter, die Zahl zusätzlich
|
||
eingeschränkt auf ihre Spalte.
|
||
- Gefiltert wird durch Nachsieben derselben Bewegungen, aus denen die Zelle entstand –
|
||
nicht durch Übersetzen der Zeile in einen neuen Filter. Nur so trifft der Drilldown auch
|
||
*Nicht kategorisiert* oder eine Astsumme über mehrere Unterkategorien.
|
||
|
||
**CSV-Export** (`src/Services/BerichtExport.php`)
|
||
- Dieselben Zeilen und Vorzeichen wie am Bildschirm, je Berichtstyp eigene Spalten.
|
||
- Semikolon, Dezimalkomma, kein Tausenderpunkt, CRLF und BOM – sonst zeigt die
|
||
Tabellenkalkulation „Übrige“ statt „Übrige“ und liest Beträge als Text.
|
||
|
||
**Tests**
|
||
- `tests/14_AuswertungTest.php` (142 Prüfungen) und
|
||
`tests/e2e/specs/auswertungen.spec.js` (7 Abläufe)
|
||
- Einzelheiten in `docs/auswertungen.md`
|
||
|
||
## Cockpit: Vermögensentwicklung als gestapeltes Balkendiagramm (11.08.2026)
|
||
|
||
- Die Kachel *Vermögensentwicklung* war ein Platzhalter und zeigt jetzt die Kontostände
|
||
der letzten zwölf Monate je Monatsende – ein Balken pro Monat, ein Farbton pro Konto
|
||
(`Vermoegensentwicklung::reihe()`, `src/Views/cockpit/vermoegensdiagramm.php`).
|
||
- Negative Stände stapeln unter der Nulllinie.
|
||
- Schalter *nur aktive* oben rechts an der Kachel, im Standard eingeschaltet: Er blendet
|
||
stillgelegte Konten samt Legendeneintrag aus und merkt sich seinen Zustand über die
|
||
Einstellungen (`cockpit_vermoegen_nur_aktive`) – auch über das Abmelden hinaus. Er ist
|
||
das einzige Kriterium; das Σ-Kennzeichen filtert hier **nicht** mit, weil stillgelegte
|
||
Konten es meist nicht tragen und beim Ausschalten sonst genau das fehlte, was man sehen
|
||
will.
|
||
- `Buchung::bewegungenJeKontoUndMonat()` liefert die Monatsbewegungen in einer Abfrage je
|
||
Quelle – zwölf Stichtagsabfragen wären es sonst.
|
||
- Mehr Konten als Farbtöne: Die größten behalten ihre Farbe, der Rest wird zu *Übrige
|
||
Konten (n)* zusammengefasst. Zwei Konten in derselben Farbe wären in der Legende nicht
|
||
auseinanderzuhalten.
|
||
- Eigenes SVG, keine Diagrammbibliothek. Die Farben stehen als Klasse am Element und
|
||
folgen damit dem hellen wie dem dunklen Thema; jedes Balkenstück trägt ein `<title>`
|
||
als Tooltip, ohne eine Zeile JavaScript.
|
||
- **Übrige Konten (n)** ist ein Link: Er führt eine Ebene tiefer (`/?ebene=N`) und zeigt
|
||
die nächstkleineren Konten einzeln – beliebig tief, bis der Rest in die Palette passt.
|
||
Unter der Legende steht der Weg zurück. Jedes Konto erscheint auf genau einer Ebene, und
|
||
eine zu hohe Ebene fällt auf die letzte zurück (`Vermoegensentwicklung::ebenen()`).
|
||
- Die Balken nutzen die Kachelbreite aus, indem sie **auseinanderrücken**, nicht indem sie
|
||
breiter werden: Die Zeichenfläche rechnet waagerecht in Prozent, die Balkenbreite bleibt
|
||
bei 16 Punkten. Achsenbeschriftung und Zeichenfläche sind dafür zwei SVGs nebeneinander –
|
||
ein einzelnes könnte von seinen Prozentangaben keine feste Achsenbreite abziehen.
|
||
- Die Kachel ist so groß wie ihre Nachbarn: Der Ausgriff über zwei Rasterspalten
|
||
(`.kachel.breit`) und das engere Cockpit-Raster (`.kachelraster.dicht`) entfallen wieder.
|
||
- Die Legende steht unter der Zeichnung statt daneben und in kleinerer Schrift (.74rem);
|
||
sie füllt so viele Spalten, wie die Kachel hergibt. Der Schalter teilt sich die erste
|
||
Zeile mit der Überschrift, damit er in der schmaleren Kachel oben rechts bleibt.
|
||
|
||
## 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
|