Merge pull request 'Fix n.n.: Buchungsbericht zeigt Splitbuchung nach Konto/Empfänger zusammengefasst' (#80) from fds2610-bot/Finanzmanager:main into main
Reviewed-on: https://codeberg.org/fds2610/Finanzmanager/pulls/80
This commit is contained in:
commit
234bd2e8af
5 changed files with 119 additions and 13 deletions
16
Changes.md
16
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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
'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']] ?? ''),
|
||||
: ($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<array<string,mixed>> $bewegungen
|
||||
* @return list<array<string,mixed>>
|
||||
*/
|
||||
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 ────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -58,7 +58,9 @@ include __DIR__ . '/../layout/kopf.php';
|
|||
<td><?= View::h($buchung['text']) ?></td>
|
||||
<td>
|
||||
<?= View::h($buchung['kategorie']) ?>
|
||||
<?php if ($buchung['gesplittet']): ?>
|
||||
<?php if ($buchung['split_gesamt']): ?>
|
||||
<span class="marke" title="Gesamtbetrag einer Splitbuchung – Aufschlüsselung im Kontoblatt">Splitbuchung</span>
|
||||
<?php elseif ($buchung['gesplittet']): ?>
|
||||
<span class="marke" title="Zeile einer Splitbuchung">Split</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@ use Finanzmanager\View;
|
|||
<td><?= View::h($buchung['text']) ?></td>
|
||||
<td>
|
||||
<?= View::h($buchung['kategorie']) ?>
|
||||
<?php if ($buchung['gesplittet']): ?>
|
||||
<?php if ($buchung['split_gesamt']): ?>
|
||||
<span class="marke" title="Gesamtbetrag einer Splitbuchung – Aufschlüsselung im Kontoblatt">Splitbuchung</span>
|
||||
<?php elseif ($buchung['gesplittet']): ?>
|
||||
<span class="marke" title="Zeile einer Splitbuchung">Split</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
|
@ -73,6 +75,12 @@ use Finanzmanager\View;
|
|||
</table>
|
||||
|
||||
<p class="hinweis klein">
|
||||
<?php if (in_array($bericht['filter']['zeilensumme'], ['konto', 'empfaenger'], true)): ?>
|
||||
Eine Splitbuchung erscheint mit ihrem Gesamtbetrag – die Aufschlüsselung nach
|
||||
Kategorie steht im Kontoblatt.
|
||||
<?php else: ?>
|
||||
Eine Splitbuchung erscheint mit jeder ihrer Zeilen einzeln – so stimmt die Summe mit
|
||||
dem Summenbericht überein. Der Empfänger führt in die Buchungsmaske.
|
||||
dem Summenbericht überein.
|
||||
<?php endif; ?>
|
||||
Der Empfänger führt in die Buchungsmaske.
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue