Finanzmanager/tests/47_SplitKlasseTest.php
fds2610-bot c26c316798 Fix #76: Klassen der Splitzeilen in der Klassenspalte des Kontoblatts
Bei einer Splitbuchung zeigt die Klassenspalte jetzt die gemeinsame Klasse
der Splitzeilen. Unterscheiden sie sich, steht dort die Marke „Splitbuchung“,
der Tooltip nennt je Splitzeile Kategorie und Klasse. Ein Klick auf die
Klassenzelle einer Splitbuchung öffnet den Splitdialog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 12:57:13 +02:00

192 lines
7.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
use Finanzmanager\Models\Buchung;
use Finanzmanager\Models\Kategorie;
use Finanzmanager\Models\Klasse;
use Finanzmanager\Models\Konto;
/**
* Fix #76: Die Klassenspalte des Kontoblatts berücksichtigt bei einer
* Splitbuchung die Klassen ihrer Zeilen. Tragen alle Zeilen dieselbe Klasse,
* steht sie in der Spalte; unterscheiden sie sich, steht dort die Marke
* „Splitbuchung“ mit den Klassen je Zeile im Tooltip.
*/
class SplitKlasseTest extends TestCase
{
protected string $testName = 'Test 47: Klassen der Splitzeilen im Kontoblatt';
private int $konto = 0;
private int $lebensmittel = 0;
private int $drogerie = 0;
private int $privat = 0;
private int $beruf = 0;
private int $alt = 0;
public function run(): void
{
$this->mandantAnlegen();
$this->konto = Konto::anlegen([
'name' => 'Splitklasse Giro', 'kontoart' => 'bank', 'anfangssaldo' => 0, 'aktiv' => 1,
]);
$this->lebensmittel = Kategorie::anlegen(['name' => 'Splitklasse Lebensmittel', 'art' => 'ausgabe']);
$this->drogerie = Kategorie::anlegen(['name' => 'Splitklasse Drogerie', 'art' => 'ausgabe']);
$this->privat = Klasse::anlegen('Splitklasse Privat');
$this->beruf = Klasse::anlegen('Splitklasse Beruf');
$this->alt = Klasse::anlegen('Splitklasse Buchungsklasse');
$this->splitKlasseErmitteln();
$this->kontoblattZeigtGemeinsameKlasse();
$this->kontoblattZeigtMarkeBeiVerschiedenenKlassen();
$this->ohneSplitBleibtEsBeiDerBuchungsklasse();
}
private function splitKlasseErmitteln(): void
{
$gleich = [['klasse_id' => $this->privat], ['klasse_id' => (string)$this->privat]];
$this->assertEquals(
['gemischt' => false, 'klasse_id' => $this->privat],
Buchung::splitKlasse($gleich, $this->alt),
'Gleiche Klassen in allen Zeilen gelten für die ganze Buchung'
);
$verschieden = [['klasse_id' => $this->privat], ['klasse_id' => $this->beruf]];
$this->assertEquals(
['gemischt' => true, 'klasse_id' => null],
Buchung::splitKlasse($verschieden, $this->alt),
'Verschiedene Klassen machen die Buchung gemischt'
);
$teilweise = [['klasse_id' => $this->privat], ['klasse_id' => null]];
$this->assertEquals(
true,
Buchung::splitKlasse($teilweise, null)['gemischt'],
'Eine Zeile ohne Klasse unterscheidet sich von einer mit Klasse'
);
$ohne = [['klasse_id' => null], ['klasse_id' => null]];
$this->assertEquals(
['gemischt' => false, 'klasse_id' => $this->alt],
Buchung::splitKlasse($ohne, $this->alt),
'Ohne Klasse in den Zeilen bleibt die Klasse der Buchung'
);
$this->assertEquals(
['gemischt' => false, 'klasse_id' => null],
Buchung::splitKlasse($ohne, null),
'Ohne jede Klasse bleibt die Spalte leer'
);
}
private function kontoblattZeigtGemeinsameKlasse(): void
{
$id = $this->splitbuchung('Gleichmarkt', $this->privat, $this->privat);
$zelle = $this->klassenzelle($id);
$this->assert(str_contains($zelle, 'Splitklasse Privat'), 'Die gemeinsame Klasse steht in der Spalte');
$this->assert(!str_contains($zelle, 'class="marke"'), 'Ohne Marke „Splitbuchung“');
$this->assert(str_contains($zelle, 'data-split="1"'), 'Der Klick führt in den Splitdialog');
$this->assert(
str_contains($zelle, 'data-klasse="' . $this->privat . '"'),
'Die Zelle trägt die gemeinsame Klasse'
);
}
private function kontoblattZeigtMarkeBeiVerschiedenenKlassen(): void
{
$id = $this->splitbuchung('Mischmarkt', $this->privat, $this->beruf);
$zelle = $this->klassenzelle($id);
$this->assert(
str_contains($zelle, '<span class="marke">Splitbuchung</span>'),
'Verschiedene Klassen zeigen die Marke „Splitbuchung“'
);
$this->assert(
str_contains($zelle, 'title="Splitklasse Lebensmittel: Splitklasse Privat' . "\n"
. 'Splitklasse Drogerie: Splitklasse Beruf"'),
'Der Tooltip nennt je Splitzeile Kategorie und Klasse'
);
$this->assert(
!str_contains($zelle, 'Splitklasse Buchungsklasse'),
'Die Klasse der Buchung selbst spielt keine Rolle'
);
}
private function ohneSplitBleibtEsBeiDerBuchungsklasse(): void
{
$id = Buchung::anlegen([
'konto_id' => $this->konto, 'datum' => '2026-05-03', 'betrag' => -12.00,
'empfaenger' => 'Einzelmarkt', 'kategorie_id' => $this->lebensmittel, 'klasse_id' => $this->beruf,
]);
$zelle = $this->klassenzelle($id);
$this->assert(str_contains($zelle, 'Splitklasse Beruf'), 'Eine einfache Buchung zeigt ihre Klasse');
$this->assert(!str_contains($zelle, 'data-split'), 'Und öffnet weiter die Klassenliste');
}
private function splitbuchung(string $empfaenger, int $klasseA, int $klasseB): int
{
return Buchung::anlegen(
[
'konto_id' => $this->konto, 'datum' => '2026-05-02', 'betrag' => -30.00,
'empfaenger' => $empfaenger, 'klasse_id' => $this->alt,
],
[
['kategorie_id' => $this->lebensmittel, 'klasse_id' => $klasseA, 'betrag' => -20.00],
['kategorie_id' => $this->drogerie, 'klasse_id' => $klasseB, 'betrag' => -10.00],
]
);
}
/**
* Die Klassenzelle einer Buchung aus dem gerenderten Kontoblatt.
*/
private function klassenzelle(int $buchungId): string
{
$html = $this->kontoblattHtml();
$muster = '#<td class="klassenzelle nebentext">\s*<a href="/buchungen/' . $buchungId . '".*?</td>#s';
return preg_match($muster, $html, $treffer) === 1 ? $treffer[0] : '';
}
/**
* Das Kontoblatt rendern wie der Controller es täte – ohne Webserver.
*/
private function kontoblattHtml(): string
{
$zeilen = Buchung::kontoblatt($this->konto);
$gesplittete = [];
foreach ($zeilen as $zeile) {
if ((int)$zeile['gesplittet'] === 1 && !$zeile['gegensicht']) {
$gesplittete[] = (int)$zeile['id'];
}
}
$daten = [
'konto' => Konto::mitSalden(Konto::find($this->konto)),
'zeilen' => $zeilen,
'gesamt' => count($zeilen),
'seite' => 1,
'seiten' => 1,
'filter' => [],
'kategorien' => Kategorie::baumFlach(),
'klassen' => Klasse::alle(),
'konten' => Konto::alle(),
'splits' => Buchung::splitsJeBuchung($gesplittete),
'wertpapiere' => [],
'bargeld' => false,
'titel' => 'Test',
'flash' => [],
'benutzer' => null,
'csrfToken' => 'x',
'aktuellerPfad' => '/konten/' . $this->konto,
];
extract($daten, EXTR_SKIP);
ob_start();
include dirname(__DIR__) . '/src/Views/buchungen/kontoblatt.php';
return (string)ob_get_clean();
}
}