From c2a6a9b899763535364cef03ae09cf57b66d5d1c Mon Sep 17 00:00:00 2001 From: fds2610-bot Date: Sun, 27 Sep 2026 14:16:41 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20n.n.:=20Buchungsbericht=20zeigt=20Splitbu?= =?UTF-8?q?chung=20nach=20Konto/Empf=C3=A4nger=20zusammengefasst?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bei Gruppierung nach Konto oder Empfänger landen alle Zeilen einer Splitbuchung ohnehin in derselben Gruppe, erschienen dort aber trotzdem einzeln mit ihrem Teilbetrag statt dem Gesamtbetrag der Buchung – wer nur eine Zeile sah, hielt sie für den vollständigen Betrag. Bei Kategorie oder Klasse bleibt es beim bisherigen Verhalten, weil eine Splitbuchung dort auf mehrere Gruppen verteilt ist und die Gruppensummen sonst nicht mehr zum Summenbericht passten. Co-Authored-By: Claude Sonnet 5 --- Changes.md | 16 ++++++ src/Services/Auswertung.php | 66 ++++++++++++++++++++--- src/Views/auswertungen/drilldown.php | 4 +- src/Views/auswertungen/teil-buchungen.php | 14 +++-- tests/14_AuswertungTest.php | 32 +++++++++-- 5 files changed, 119 insertions(+), 13 deletions(-) diff --git a/Changes.md b/Changes.md index 9783cff..86ae944 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,21 @@ # Änderungen +## Buchungsbericht: Splitbuchung nach Konto/Empfänger zusammengefasst (27.09.2026) + +Nach Kategorie oder Klasse gruppiert erscheint eine Splitbuchung weiterhin mit jeder +ihrer Zeilen einzeln – nur so stimmen die Gruppensummen mit dem Summenbericht überein. +Nach Konto oder Empfänger gruppiert landen aber ohnehin alle Zeilen einer Splitbuchung +in derselben Gruppe; dort erschien bisher trotzdem jede Zeile für sich, mit ihrem +Teilbetrag statt dem Gesamtbetrag – wer nur eine der beiden Zeilen sah, hielt das für +den kompletten Betrag der Buchung. + +- `Auswertung::splitsZusammenfassen()` fasst bei Gruppierung nach Konto oder Empfänger + alle Splitzeilen einer Buchung zu einer Zeile mit dem Gesamtbetrag zusammen, analog + zum Kontoblatt. Die Zeile führt keine einzelne Kategorie, sondern das Badge + „Splitbuchung“; die Aufschlüsselung bleibt dem Kontoblatt vorbehalten. +- Bei Kategorie oder Klasse bleibt es beim bisherigen Verhalten. +- Test in `tests/14_AuswertungTest.php` (`buchungsbericht`). + ## Regeln lassen sich auf Buchungen ohne Kategorie anwenden (27.09.2026) Der Knopf unter Verwaltung → Regeln hieß „Gegen offene Umsätze prüfen“ und zählte nur, diff --git a/src/Services/Auswertung.php b/src/Services/Auswertung.php index 1d0005e..02ab7c5 100644 --- a/src/Services/Auswertung.php +++ b/src/Services/Auswertung.php @@ -595,8 +595,13 @@ class Auswertung { $namen = self::namen((string)$filter['zeilensumme']); + $bewegungen = self::bewegungen($filter); + if (in_array($filter['zeilensumme'], ['konto', 'empfaenger'], true)) { + $bewegungen = self::splitsZusammenfassen($bewegungen); + } + $gruppen = []; - foreach (self::bewegungen($filter) as $zeile) { + foreach ($bewegungen as $zeile) { $schluessel = self::zeilenschluessel($zeile, (string)$filter['zeilensumme']); $gruppen[$schluessel] ??= [ @@ -700,6 +705,10 @@ class Auswertung } )); + if (in_array($dimension, ['konto', 'empfaenger'], true)) { + $bewegungen = self::splitsZusammenfassen($bewegungen); + } + $liste = self::alsBuchungsliste($bewegungen); return [ @@ -749,13 +758,16 @@ class Auswertung 'konto_id' => (int)$zeile['konto_id'], 'empfaenger' => (string)$zeile['empfaenger'], 'text' => (string)$zeile['text'], - 'kategorie' => $zeile['kategorie_id'] === null - ? ($zeile['gegenkonto_id'] === null - ? self::OHNE_KATEGORIE - : '[' . ($kontonamen[(int)$zeile['gegenkonto_id']] ?? '?') . ']') - : ($kategorien[(int)$zeile['kategorie_id']] ?? ''), + 'kategorie' => !empty($zeile['split_zusammengefasst']) + ? '' + : ($zeile['kategorie_id'] === null + ? ($zeile['gegenkonto_id'] === null + ? self::OHNE_KATEGORIE + : '[' . ($kontonamen[(int)$zeile['gegenkonto_id']] ?? '?') . ']') + : ($kategorien[(int)$zeile['kategorie_id']] ?? '')), 'klasse' => $zeile['klasse_id'] === null ? '' : ($klassen[(int)$zeile['klasse_id']] ?? ''), 'gesplittet' => $zeile['split_id'] !== null, + 'split_gesamt' => !empty($zeile['split_zusammengefasst']), 'betrag' => round((float)$zeile['betrag'], 2), ]; } @@ -768,6 +780,48 @@ class Auswertung return $liste; } + /** + * Fasst die Splitzeilen einer Buchung zu einer Zeile mit dem Gesamtbetrag + * zusammen – wie im Kontoblatt. Das ist nur bei Konto oder Empfänger + * unbedenklich: Dort landen alle Zeilen einer Splitbuchung ohnehin in + * derselben Gruppe. Bei Kategorie oder Klasse gehören sie in + * unterschiedliche Gruppen; dort blieben sie einzeln, sonst stimmten die + * Gruppensummen nicht mehr mit dem Summenbericht überein. + * + * @param list> $bewegungen + * @return list> + */ + private static function splitsZusammenfassen(array $bewegungen): array + { + $ergebnis = []; + $index = []; + + foreach ($bewegungen as $zeile) { + if ($zeile['split_id'] === null) { + $ergebnis[] = $zeile; + continue; + } + + $buchungId = (int)$zeile['buchung_id']; + if (!isset($index[$buchungId])) { + $index[$buchungId] = count($ergebnis); + $ergebnis[] = $zeile + [ + 'kategorie_id' => null, + 'klasse_id' => null, + 'gegenkonto_id' => null, + 'text' => '', + 'split_zusammengefasst' => true, + ]; + continue; + } + + $ergebnis[$index[$buchungId]]['betrag'] = + (float)$ergebnis[$index[$buchungId]]['betrag'] + (float)$zeile['betrag']; + } + + return $ergebnis; + } + // ── Vergleichsbericht ──────────────────────────────────────────────────── /** diff --git a/src/Views/auswertungen/drilldown.php b/src/Views/auswertungen/drilldown.php index 3d52e78..2212ae2 100644 --- a/src/Views/auswertungen/drilldown.php +++ b/src/Views/auswertungen/drilldown.php @@ -58,7 +58,9 @@ include __DIR__ . '/../layout/kopf.php'; - + + Splitbuchung + Split diff --git a/src/Views/auswertungen/teil-buchungen.php b/src/Views/auswertungen/teil-buchungen.php index 6cfa086..0d4b3ef 100644 --- a/src/Views/auswertungen/teil-buchungen.php +++ b/src/Views/auswertungen/teil-buchungen.php @@ -41,7 +41,9 @@ use Finanzmanager\View; - + + Splitbuchung + Split @@ -73,6 +75,12 @@ use Finanzmanager\View;

- Eine Splitbuchung erscheint mit jeder ihrer Zeilen einzeln – so stimmt die Summe mit - dem Summenbericht überein. Der Empfänger führt in die Buchungsmaske. + + Eine Splitbuchung erscheint mit ihrem Gesamtbetrag – die Aufschlüsselung nach + Kategorie steht im Kontoblatt. + + Eine Splitbuchung erscheint mit jeder ihrer Zeilen einzeln – so stimmt die Summe mit + dem Summenbericht überein. + + Der Empfänger führt in die Buchungsmaske.

diff --git a/tests/14_AuswertungTest.php b/tests/14_AuswertungTest.php index 0c790e2..d178775 100644 --- a/tests/14_AuswertungTest.php +++ b/tests/14_AuswertungTest.php @@ -411,8 +411,9 @@ class AuswertungTest extends TestCase { $bericht = $this->bericht(['typ' => 'buchungsbericht', 'zeilensumme' => 'konto']); - // Fünf einfache Buchungen minus Umbuchung, plus zwei Splitzeilen. - $this->assertEquals(6, $bericht['anzahl'], 'Splitzeilen zählen einzeln, die Umbuchung gar nicht'); + // Fünf einfache Buchungen minus Umbuchung macht vier, plus die + // Splitbuchung als eine zusammengefasste Zeile. + $this->assertEquals(5, $bericht['anzahl'], 'Bei Konto/Empfänger zählt eine Splitbuchung nur einmal'); $this->assertEquals(2762.5, $bericht['summe'], 'Die Summe entspricht dem Summenbericht'); $this->assertEquals(1, count($bericht['gruppen']), 'Alles auf einem Konto, also eine Gruppe'); @@ -420,9 +421,34 @@ class AuswertungTest extends TestCase $this->assertEquals('2026-07-05', $erste['datum'], 'Chronologisch sortiert'); $this->assertEquals('400 - Auswertungsleben:A-Einkaufen', $erste['kategorie'], 'Kategorie als voller Pfad'); + $split = null; + foreach ($bericht['gruppen'][0]['buchungen'] as $buchung) { + if ($buchung['empfaenger'] === 'Rewe') { + $split = $buchung; + } + } + $this->assertEquals(-80.0, $split['betrag'], + 'Die Splitbuchung zeigt ihren Gesamtbetrag, nicht nur die erste Splitzeile'); + $this->assertEquals('', $split['kategorie'], 'Keine einzelne Kategorie bei der zusammengefassten Zeile'); + $this->assert($split['split_gesamt'], 'Als zusammengefasste Splitbuchung markiert'); + + // Bei Kategorie/Klasse bleiben die Splitzeilen einzeln – sonst + // stimmten die Gruppensummen nicht mehr mit dem Summenbericht überein. + $nachKategorie = $this->bericht(['typ' => 'buchungsbericht', 'zeilensumme' => 'kategorie']); + $splitzeilen = 0; + foreach ($nachKategorie['gruppen'] as $gruppe) { + foreach ($gruppe['buchungen'] as $buchung) { + if ($buchung['empfaenger'] === 'Rewe') { + $splitzeilen++; + $this->assert(!$buchung['split_gesamt'], 'Nach Kategorie keine Zusammenfassung'); + } + } + } + $this->assertEquals(2, $splitzeilen, 'Nach Kategorie erscheint jede Splitzeile einzeln'); + // Mit Umbuchungen: eine Buchung und 200 € mehr. $mit = $this->bericht(['typ' => 'buchungsbericht', 'zeilensumme' => 'konto', 'umbuchungen' => true]); - $this->assertEquals(7, $mit['anzahl'], 'Mit Schalter kommt die Umbuchung dazu'); + $this->assertEquals(6, $mit['anzahl'], 'Mit Schalter kommt die Umbuchung dazu'); $this->assertEquals(2562.5, $mit['summe'], 'Die Umbuchung verschiebt die Summe'); $umbuchung = null;