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'; + + + +