mandantAnlegen(); $this->stammdaten(); $this->uebernimmtEmpfaengerKategorieUndKlasse(); $this->weichtOhneEmpfaengerAufDenBuchungstextAus(); $this->ohneEmpfaengerUndBuchungstextEntstehtKeineRegel(); $this->splitbuchungBekommtVerhaeltnisgleicheAnteile(); $this->unbekannteBuchungWirdAbgewiesen(); } private function stammdaten(): void { $this->giro = Konto::anlegen([ 'name' => 'Regeltest Giro', 'kontoart' => 'bank', 'anfangssaldo' => 1000, 'aktiv' => 1, ]); $this->strom = Kategorie::anlegen(['name' => 'Regeltest Strom', 'art' => 'ausgabe']); $this->versicherung = Kategorie::anlegen(['name' => 'Regeltest Versicherung', 'art' => 'ausgabe']); $this->klasse = Klasse::anlegen('Regeltest Privat'); } private function uebernimmtEmpfaengerKategorieUndKlasse(): void { $buchungId = Buchung::anlegen([ 'konto_id' => $this->giro, 'datum' => '2026-05-01', 'betrag' => -42.50, 'empfaenger' => 'Stadtwerke Strom', 'buchungstext' => 'Abschlag Mai', 'kategorie_id' => $this->strom, 'klasse_id' => $this->klasse, ]); $regelId = AbgleichRegel::ausBuchung($buchungId); $this->assertNotEmpty($regelId, 'Die Regel wird angelegt'); $regel = AbgleichRegel::find((int)$regelId); $this->assertNotEmpty($regel, 'Sie lässt sich wiederfinden'); $this->assertEquals('empfaenger', $regel['feld'], 'Geprüft wird der Empfänger'); $this->assertEquals('Stadtwerke Strom', $regel['muster'], 'Das Muster ist der Empfänger'); $this->assertEquals('enthaelt', $regel['vergleich'], 'Der Vergleich ist „enthält“'); $this->assertEquals($this->strom, (int)$regel['kategorie_id'], 'Die Kategorie wird übernommen'); $this->assertEquals($this->klasse, (int)$regel['klasse_id'], 'Die Klasse wird übernommen'); $this->assertEquals(1, (int)$regel['aktiv'], 'Die Regel ist gleich aktiv'); // Und sie taucht in der Liste der bestehenden Regeln auf – ergänzt, nicht // ersetzt. $gefunden = false; foreach (AbgleichRegel::alle() as $eintrag) { $gefunden = $gefunden || (int)$eintrag['id'] === (int)$regelId; } $this->assert($gefunden, 'Die neue Regel steht in der Liste der bestehenden Regeln'); // Und sie trifft tatsächlich auf einen passenden Umsatz. $this->assert( AbgleichRegel::passt($regel, ['betrag' => -42.50, 'empfaenger' => 'Stadtwerke Strom AG']), 'Die Regel trifft einen ähnlichen Umsatz' ); } private function weichtOhneEmpfaengerAufDenBuchungstextAus(): void { $buchungId = Buchung::anlegen([ 'konto_id' => $this->giro, 'datum' => '2026-05-02', 'betrag' => -15.00, 'buchungstext' => 'Parkgebühr Innenstadt', ]); $regelId = AbgleichRegel::ausBuchung($buchungId); $regel = AbgleichRegel::find((int)$regelId); $this->assertEquals('verwendungszweck', $regel['feld'], 'Ohne Empfänger zählt der Buchungstext'); $this->assertEquals('Parkgebühr Innenstadt', $regel['muster'], 'Das Muster ist der Buchungstext'); } private function ohneEmpfaengerUndBuchungstextEntstehtKeineRegel(): void { $buchungId = Buchung::anlegen([ 'konto_id' => $this->giro, 'datum' => '2026-05-03', 'betrag' => -5.00, ]); $this->assertNull(AbgleichRegel::ausBuchung($buchungId), 'Ohne Muster entsteht keine Regel'); } private function splitbuchungBekommtVerhaeltnisgleicheAnteile(): void { $buchungId = Buchung::anlegen( [ 'konto_id' => $this->giro, 'datum' => '2026-05-04', 'betrag' => -80.00, 'empfaenger' => 'Sammelrechnung Versicherung', ], [ ['kategorie_id' => $this->versicherung, 'betrag' => -50.00, 'notiz' => 'Kfz'], ['kategorie_id' => $this->versicherung, 'betrag' => -30.00, 'notiz' => 'Haftpflicht'], ] ); $regelId = AbgleichRegel::ausBuchung($buchungId); $regel = AbgleichRegel::find((int)$regelId); $zeilen = AbgleichRegel::splitzeilen($regel); $this->assertEquals(2, count($zeilen), 'Beide Splitzeilen kommen in der Regel an'); $this->assertEquals(62.5, $zeilen[0]['prozent'], 'Anteil der ersten Zeile im selben Verhältnis wie die Buchung'); $this->assertEquals(37.5, $zeilen[1]['prozent'], 'Anteil der zweiten Zeile ebenso'); // anwenden() rechnet die Anteile auf jeden Buchungsbetrag um – hier zur // Probe auf einen doppelt so hohen Umsatz. $angewendet = AbgleichRegel::anwenden($regel, ['betrag' => -160.00]); $this->assertEquals(-100.0, $angewendet['splits'][0]['betrag'], 'Erste Zeile bleibt im Verhältnis 62,5 %'); $this->assertEquals(-60.0, $angewendet['splits'][1]['betrag'], 'Zweite Zeile bleibt im Verhältnis 37,5 %'); } private function unbekannteBuchungWirdAbgewiesen(): void { $this->assert( $this->wirftFehler(fn() => AbgleichRegel::ausBuchung(PHP_INT_MAX)), 'Eine nicht vorhandene Buchung wird abgewiesen' ); } /** Hilfsmittel: Wirft der Aufruf eine Ausnahme? */ private function wirftFehler(callable $aufruf): bool { try { $aufruf(); } catch (\Throwable) { return true; } return false; } }