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, 'Splitbuchung'), '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 = '#\s*#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(); } }