From fef2e672483cb62732d16254a9e32224ada60b89 Mon Sep 17 00:00:00 2001 From: fds2610-bot Date: Mon, 12 Oct 2026 22:03:54 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20#137:=20Spalte=20und=20Filter=20=E2=80=9E?= =?UTF-8?q?abgeglichen=E2=80=9C=20im=20Buchungsbericht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Buchungsbericht zeigt vor der Spalte „Bar“ des Bargeld-Tools die Spalte „✓“ (abgeglichen) und lässt sich nach abgeglichenen bzw. nicht abgeglichenen Buchungen filtern. „Buchungen nicht abgeglichen“ unter den offenen Vorgängen im Cockpit öffnet jetzt diesen Bericht mit dem Filter „nicht abgeglichen“ statt der Kontenübersicht. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 4 ++ Changes.md | 8 ++++ docs/auswertungen.md | 2 +- docs/cockpit.md | 3 ++ public/static/app.css | 12 ++++-- src/Services/Auswertung.php | 17 +++++++- src/Services/Cockpit.php | 49 ++++++++++++++++++---- src/Views/auswertungen/bericht.php | 12 ++++++ src/Views/auswertungen/teil-buchungen.php | 7 +++- tests/14_AuswertungTest.php | 37 ++++++++++++++++ tests/15_CockpitTest.php | 51 +++++++++++++++++++++++ tests/e2e/specs/auswertungen.spec.js | 21 ++++++++++ 12 files changed, 208 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea0bc4e..5c5de3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/). ### Added +- Buchungsbericht: Spalte „✓“ (abgeglichen) vor der Spalte „Bar“ des Bargeld-Tools und + Filter „Abgeglichen“ (alle / abgeglichen / nicht abgeglichen); „Buchungen nicht + abgeglichen“ unter den offenen Vorgängen im Cockpit öffnet den Buchungsbericht mit + diesem Filter statt der Kontenübersicht (#137) - Buchungsmaske: Schalter „Ohne Bargeld-Abgleich“ für Sammelbuchungen und Differenzausgleiche; solche Buchungen werden nicht mit dem Bargeld-Tool abgeglichen und erscheinen nicht mehr unter den offenen Vorgängen im Cockpit (#136) diff --git a/Changes.md b/Changes.md index 0e9c1e6..b0b0477 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,13 @@ # Änderungen +## Buchungsbericht: Spalte und Filter „abgeglichen“ (12.10.2026) + +- Anlass (#137): Der Buchungsbericht zeigte nicht, ob eine Buchung abgeglichen ist, und ließ sich nicht danach filtern. +- Neue Spalte „✓“ (Tooltip „abgeglichen“) vor der Spalte „Bar“ des Bargeld-Tools – wie im Kontoblatt. Splitzeilen erben den Haken ihrer Buchung. +- Neuer Filter „Abgeglichen“ (alle / abgeglichen / nicht abgeglichen), Filterfeld `abgeglichen` mit den Werten `ja`/`nein`; wie der Bargeld-Filter wirkt er nur im Buchungsbericht und wird mit gespeicherten Berichten gesichert. +- Cockpit, *Offene Vorgänge*: „Buchungen nicht abgeglichen“ führt jetzt in den Buchungsbericht mit dem Filter „nicht abgeglichen“ statt in die Kontenübersicht (`Cockpit::nichtAbgeglichenBericht()`). Zeitraum von der ersten betroffenen Buchung bis heute, Umbuchungen eingeschlossen; liegen nicht abgeglichene Buchungen auf aufgelösten Konten, beschränkt sich der Bericht auf die aktiven Konten – so zeigt er genau die gezählten Buchungen. +- Tests: `tests/14_AuswertungTest.php`, `tests/15_CockpitTest.php`, `tests/e2e/specs/auswertungen.spec.js`. + ## Buchen ohne Bargeld-Abgleich (12.10.2026) - Anlass (#136): Sammelbuchungen und Differenzausgleiche tragen eine Kategorie, die gegen das Bargeld-Tool geprüft wird, stehen dort aber absichtlich nicht – und wurden deshalb immer wieder als offener Vorgang angemahnt. diff --git a/docs/auswertungen.md b/docs/auswertungen.md index 25c11af..f5b5d60 100644 --- a/docs/auswertungen.md +++ b/docs/auswertungen.md @@ -48,7 +48,7 @@ Zwei Ausnahmen führen das Vorzeichen mit, weil sie keine Abschnitte haben: der | Typ | Zeilen | Spalten | |---|---|---| | **Summenbericht** | Zeilen-Summe, nach Einnahmen und Ausgaben getrennt | Zeitraum, je nach Spalten-Aufteilung | -| **Buchungsbericht** | Einzelbuchungen, gruppiert nach Zeilen-Summe; bei eingerichtetem Bargeld-Tool mit Spalte „Bar“ und Filter „Bargeld-Tool“ | feste Buchungsspalten | +| **Buchungsbericht** | Einzelbuchungen, gruppiert nach Zeilen-Summe; Spalte „✓“ (abgeglichen) mit Filter „Abgeglichen“ (#137); bei eingerichtetem Bargeld-Tool mit Spalte „Bar“ und Filter „Bargeld-Tool“ | feste Buchungsspalten | | **Vergleichsbericht** | wie Summenbericht | Zeitraum, Vorjahreszeitraum, Differenz, Veränderung | | **Kontostandsbericht** | Konten | Salden zu den Enden der Spaltenzeiträume | | **Budgetbericht** | Kategoriebaum | Budget, Ist, Saldo | diff --git a/docs/cockpit.md b/docs/cockpit.md index 8a88cc2..2acabea 100644 --- a/docs/cockpit.md +++ b/docs/cockpit.md @@ -179,6 +179,9 @@ Prüfungen, die gerade nichts melden und daher nicht in der Liste stehen: gesplittet sind. Umbuchungen brauchen keine Kategorie und zählen deshalb nicht mit, ebenso wenig Buchungen, die bewusst ohne Kategorie gebucht sind (siehe `docs/buchungen.md`). * **nicht abgeglichen** – Buchungen aktiver Konten bis heute ohne Abgleich-Kennzeichen. + Führt in den Buchungsbericht mit dem Filter „Abgeglichen: nicht abgeglichen“, von der + ersten betroffenen Buchung bis heute und mit Umbuchungen; liegen auch auf aufgelösten + Konten solche Buchungen, ist der Bericht auf die aktiven Konten eingeschränkt (#137). * **Bargeld-Tool-Prüfung fehlgeschlagen** – Buchungen mit `bargeld_status = 'fehler'`, also einem gescheiterten Abruf beim Bargeld-Tool (siehe `docs/buchungen.md`), nicht zu verwechseln mit „im Bargeld-Tool nicht gefunden“. Führt in den Buchungsbericht über den diff --git a/public/static/app.css b/public/static/app.css index e0ce994..76b9fe9 100644 --- a/public/static/app.css +++ b/public/static/app.css @@ -1412,17 +1412,21 @@ table.kostentabelle select { max-width: 16rem; width: 100%; } table.kostentabelle th[scope="row"] { font-weight: 400; text-align: left; } table.kostentabelle .gruppenzeile th { padding-top: .7rem; font-weight: 600; } -/* Die Abgleichspalte zwischen Klasse und Betrag: so schmal wie ihr Zeichen. - Sie erscheint nur, wenn ein Bargeld-Tool eingerichtet ist. */ +/* Die Abgleichspalten zwischen Klasse und Betrag: so schmal wie ihr Zeichen. + „✓“ (abgeglichen) steht immer davor, „Bar“ erscheint nur, wenn ein + Bargeld-Tool eingerichtet ist. */ table.liste th.bargeldspalte, -table.liste td.bargeldspalte { +table.liste td.bargeldspalte, +table.liste th.abgleichspalte, +table.liste td.abgleichspalte { width: 1.8rem; padding-left: .2rem; padding-right: .2rem; text-align: center; } -table.liste th.bargeldspalte { +table.liste th.bargeldspalte, +table.liste th.abgleichspalte { font-size: .78rem; color: var(--text-gedaempft); } diff --git a/src/Services/Auswertung.php b/src/Services/Auswertung.php index ba72ace..9ce3c0a 100644 --- a/src/Services/Auswertung.php +++ b/src/Services/Auswertung.php @@ -56,6 +56,9 @@ class Auswertung /** Zulässige Werte des Bargeld-Filters; 'keiner' = nicht abgeglichen. */ public const BARGELD_FILTER = ['gefunden', 'fehlt', 'fehler', 'keiner']; + /** Zulässige Werte des Abgleich-Filters im Buchungsbericht (#137). */ + public const ABGEGLICHEN_FILTER = ['ja', 'nein']; + /** Wonach die Zeilen zusammengefasst werden. */ public const ZEILENSUMMEN = [ 'kategorie' => 'Kategorie', @@ -127,6 +130,10 @@ class Auswertung 'bargeld' => $typ === 'buchungsbericht' && in_array($roh['bargeld'] ?? '', self::BARGELD_FILTER, true) ? (string)$roh['bargeld'] : '', + // Ebenso nur dort: der Haken „abgeglichen“ der Buchung (#137). + 'abgeglichen' => $typ === 'buchungsbericht' && in_array($roh['abgeglichen'] ?? '', self::ABGEGLICHEN_FILTER, true) + ? (string)$roh['abgeglichen'] + : '', ]; } @@ -842,6 +849,7 @@ class Auswertung 'bargeld_status' => $zeile['bargeld_status'] ?? null, 'bargeld_hinweis' => $zeile['bargeld_hinweis'] ?? null, 'ohne_bargeld_abgleich' => !empty($zeile['ohne_bargeld_abgleich']), + 'abgeglichen' => !empty($zeile['abgeglichen']), ]; } @@ -1195,14 +1203,14 @@ class Auswertung SELECT b.id AS buchung_id, NULL AS split_id, b.datum, b.betrag, b.konto_id, b.kategorie_id, b.klasse_id, b.gegenkonto_id, b.empfaenger, b.buchungstext AS text, b.bargeld_status, b.bargeld_hinweis, - b.ohne_bargeld_abgleich + b.ohne_bargeld_abgleich, b.abgeglichen FROM buchungen b WHERE b.benutzer_id = ? AND b.gesplittet = 0 AND b.datum BETWEEN ? AND ? UNION ALL SELECT b.id AS buchung_id, s.id AS split_id, b.datum, s.betrag, b.konto_id, s.kategorie_id, s.klasse_id, s.gegenkonto_id, b.empfaenger, s.notiz AS text, b.bargeld_status, b.bargeld_hinweis, - b.ohne_bargeld_abgleich + b.ohne_bargeld_abgleich, b.abgeglichen FROM buchung_splits s JOIN buchungen b ON b.id = s.buchung_id WHERE b.benutzer_id = ? AND b.gesplittet = 1 AND b.datum BETWEEN ? AND ? @@ -1258,6 +1266,11 @@ class Auswertung $params[] = $bargeld; } + $abgeglichen = (string)($filter['abgeglichen'] ?? ''); + if ($abgeglichen !== '') { + $wo[] = $abgeglichen === 'ja' ? 'abgeglichen = 1' : 'abgeglichen = 0'; + } + if (($filter['suche'] ?? '') !== '') { $wo[] = '(empfaenger LIKE ? OR text LIKE ?)'; $muster = '%' . $filter['suche'] . '%'; diff --git a/src/Services/Cockpit.php b/src/Services/Cockpit.php index 62c3984..c36c3bb 100644 --- a/src/Services/Cockpit.php +++ b/src/Services/Cockpit.php @@ -261,19 +261,19 @@ class Cockpit ]; } - $nichtAbgeglichen = (int)Database::fetchValue( - 'SELECT COUNT(*) FROM buchungen b + $stichtag = $heute ?? date('Y-m-d'); + $nichtAbgeglichen = Database::fetchOne( + 'SELECT COUNT(*) AS anzahl, MIN(b.datum) AS erste FROM buchungen b JOIN konten k ON k.id = b.konto_id WHERE b.benutzer_id = ? AND b.abgeglichen = 0 AND k.aktiv = 1 AND b.datum <= ?', - [$benutzer, $heute ?? date('Y-m-d')], - 0 + [$benutzer, $stichtag] ); - if ($nichtAbgeglichen > 0) { + if ((int)($nichtAbgeglichen['anzahl'] ?? 0) > 0) { $vorgaenge[] = [ 'schluessel' => 'nicht_abgeglichen', 'text' => 'nicht abgeglichen', - 'anzahl' => $nichtAbgeglichen, - 'pfad' => '/konten', + 'anzahl' => (int)$nichtAbgeglichen['anzahl'], + 'pfad' => self::nichtAbgeglichenBericht((string)$nichtAbgeglichen['erste'], $stichtag), ]; } @@ -387,4 +387,39 @@ class Cockpit 'angepasst' => '1', ]); } + + /** + * Der Buchungsbericht, gefiltert auf nicht abgeglichene Buchungen (#137) – + * mit denselben Grenzen wie der offene Vorgang: von der ersten betroffenen + * Buchung bis heute, Umbuchungen eingeschlossen. Liegen auch auf + * aufgelösten Konten nicht abgeglichene Buchungen, beschränkt sich der + * Bericht auf die aktiven Konten, denn nur die zählt der Vorgang. + */ + private static function nichtAbgeglichenBericht(string $erste, string $stichtag): string + { + $parameter = [ + 'typ' => 'buchungsbericht', + 'zeitraum' => 'benutzerdefiniert', + 'von' => $erste, + 'bis' => $stichtag, + 'umbuchungen' => '1', + 'abgeglichen' => 'nein', + ]; + + $inaktiv = (int)Database::fetchValue( + 'SELECT COUNT(*) FROM buchungen b + JOIN konten k ON k.id = b.konto_id + WHERE b.benutzer_id = ? AND b.abgeglichen = 0 AND k.aktiv = 0 AND b.datum BETWEEN ? AND ?', + [Mandant::id(), $erste, $stichtag], + 0 + ); + if ($inaktiv > 0) { + $parameter['konten'] = array_map('intval', array_column(Database::fetchAll( + 'SELECT id FROM konten WHERE benutzer_id = ? AND aktiv = 1 ORDER BY id', + [Mandant::id()] + ), 'id')); + } + + return '/auswertungen/bericht?' . http_build_query($parameter + ['angepasst' => '1']); + } } diff --git a/src/Views/auswertungen/bericht.php b/src/Views/auswertungen/bericht.php index 82e0fd0..5ef8170 100644 --- a/src/Views/auswertungen/bericht.php +++ b/src/Views/auswertungen/bericht.php @@ -175,6 +175,18 @@ include __DIR__ . '/../layout/kopf.php'; + + + +