Finanzmanager/tests/07_KontenTest.php
fds2610-bot 1e3227ce83 Phase 1: Stammdaten und QIF-Stammdatenimport
Konten, Kontengruppen, Kategorien und Klassen samt Übernahme aus dem
QIF-Export des Lexware FinanzManagers.

Kontenübersicht nach Kontengruppen gebündelt, mit Gruppensummen und
Gesamtsumme; die drei Saldospalten (Online-Saldo, aktueller und erwarteter
Saldo) sind angelegt und werden bis Phase 2 aus dem Anfangssaldo gespeist.

Kategorien sind hierarchisch mit ":" als Trenner. Der vollständige Pfad
steht redundant in der Tabelle, weil jede Anzeige und der gesamte Import
darüber laufen; Umbenennen und Verschieben schreiben den Teilbaum neu. Die
Art (Einnahme/Ausgabe) gilt für den ganzen Ast. Register wie im Original:
Alle, Einnahmen, Ausgaben, Umbuchungskonten.

QifParser liest zustandsbehaftet und sequenziell, wandelt ISO-8859-1 nach
UTF-8 und versteht US-Beträge sowie das Datumsformat aus der Kopfzeile.
QifStammdatenImport ist idempotent und bietet Trockenlauf und Protokoll -
im Dialog unter Verwaltung sowie über scripts/qif-import.php. Am echten
Export geprüft: 17 Klassen, 105 Kategorien, 37 Konten.

Enthält außerdem die Korrektur der englischen Monatsnamen im
Einstellungsdialog: date('F') ist sprachlich nicht umschaltbar, deshalb
liegen die Namen jetzt zentral als View::MONATE vor. Die Änderung ließ
sich nicht als eigener Commit trennen, weil sie dieselben Dateien berührt
wie Phase 1.

Tests: 227 PHP-Prüfungen und 14 Playwright-Tests, alle grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 21:33:40 +02:00

158 lines
6.6 KiB
PHP

<?php
declare(strict_types=1);
use Finanzmanager\Controllers\KontenController;
use Finanzmanager\Models\Konto;
use Finanzmanager\Models\Kontengruppe;
class KontenTest extends TestCase
{
protected string $testName = 'Test 07: Konten und Kontengruppen';
public function run(): void
{
$this->mandantAnlegen();
$this->anlegenUndFinden();
$this->kontoarten();
$this->uebersichtGruppiertUndSummiert();
$this->betragsEingabe();
}
private function anlegenUndFinden(): void
{
$id = Konto::anlegen([
'name' => 'DKB Girokonto',
'kontoart' => 'bank',
'kontonummer' => '1010903027',
'anfangssaldo' => 1234.56,
'aktiv' => 1,
]);
$konto = Konto::find($id);
$this->assertEquals('DKB Girokonto', $konto['name'], 'Konto wird angelegt');
$this->assertEquals('1234.56', $konto['anfangssaldo'], 'Anfangssaldo wird übernommen');
$doppelt = false;
try {
Konto::anlegen(['name' => 'DKB Girokonto']);
} catch (\Throwable) {
$doppelt = true;
}
$this->assert($doppelt, 'Kontonamen sind eindeutig');
$ohneName = false;
try {
Konto::anlegen(['name' => ' ']);
} catch (\Throwable) {
$ohneName = true;
}
$this->assert($ohneName, 'Ein Konto ohne Namen wird abgewiesen');
// Leerzeichen in der IBAN sind beim Abtippen normal und müssen weg.
Konto::speichern($id, [
'name' => 'DKB Girokonto',
'kontoart' => 'bank',
'iban' => 'de89 3704 0044 0532 0130 00',
'aktiv' => 1,
]);
$this->assertEquals(
'DE89370400440532013000',
Konto::find($id)['iban'],
'IBAN wird ohne Leerzeichen und in Großbuchstaben abgelegt'
);
Konto::loeschen($id);
$this->assertNull(Konto::find($id), 'Konto lässt sich löschen');
}
private function kontoarten(): void
{
$this->assertEquals('bank', Konto::artAusQif('Bank'), 'QIF-Typ Bank');
$this->assertEquals('kreditkarte', Konto::artAusQif('CCard'), 'QIF-Typ CCard');
$this->assertEquals('sparkonto', Konto::artAusQif('Oth T'), 'QIF-Typ Oth T');
$this->assertEquals('depot', Konto::artAusQif('Port'), 'QIF-Typ Port');
$this->assertEquals('depot', Konto::artAusQif('Invst'), 'QIF-Typ Invst');
$this->assertEquals('sonstiges', Konto::artAusQif('Unbekannt'), 'Unbekannter Typ fällt auf Sonstiges');
$this->assertEquals('Kreditkarte', Konto::bezeichnungDerArt('kreditkarte'), 'Bezeichnung auf Deutsch');
$unbekannt = false;
try {
Konto::anlegen(['name' => 'Testkonto', 'kontoart' => 'gibtesnicht']);
} catch (\Throwable) {
$unbekannt = true;
}
$this->assert($unbekannt, 'Unbekannte Kontoart wird abgewiesen');
}
private function uebersichtGruppiertUndSummiert(): void
{
$anlage = Kontengruppe::anlegen('Anlage', '', 1);
Konto::anlegen(['name' => 'Tagesgeld', 'kontoart' => 'sparkonto',
'kontengruppe_id' => $anlage, 'anfangssaldo' => 10000, 'aktiv' => 1]);
Konto::anlegen(['name' => 'Depot', 'kontoart' => 'depot',
'kontengruppe_id' => $anlage, 'anfangssaldo' => 5000, 'aktiv' => 1]);
Konto::anlegen(['name' => 'Girokonto', 'kontoart' => 'bank',
'anfangssaldo' => 1500.50, 'aktiv' => 1]);
Konto::anlegen(['name' => 'Kreditkarte', 'kontoart' => 'kreditkarte',
'anfangssaldo' => -320.25, 'aktiv' => 1]);
Konto::anlegen(['name' => 'Altes Konto', 'kontoart' => 'bank',
'anfangssaldo' => 999, 'aktiv' => 0]);
$uebersicht = Konto::uebersicht();
$summeErwartet = 10000 + 5000 + 1500.50 - 320.25;
$this->assertEquals($summeErwartet, $uebersicht['summe'], 'Gesamtsumme ohne inaktive Konten');
$this->assertEquals(2, count($uebersicht['gruppen']), 'Zwei Gruppen: Anlage und Nicht gruppiert');
$nachName = [];
foreach ($uebersicht['gruppen'] as $gruppe) {
$nachName[$gruppe['name']] = $gruppe;
}
$this->assertEquals(15000.0, $nachName['Anlage']['summe'] ?? 0.0, 'Summe der Gruppe Anlage');
$this->assertEquals(
2,
count($nachName[Kontengruppe::OHNE_GRUPPE]['konten'] ?? []),
'Konten ohne Gruppe stehen unter „Nicht gruppiert“'
);
$mitInaktiven = Konto::uebersicht(false);
$this->assertEquals(
$summeErwartet + 999,
$mitInaktiven['summe'],
'Inaktive Konten zählen mit, wenn sie eingeblendet werden'
);
// Die Saldospalten existieren schon, gespeist aus dem Anfangssaldo.
$giro = null;
foreach ($nachName[Kontengruppe::OHNE_GRUPPE]['konten'] as $konto) {
if ($konto['name'] === 'Girokonto') {
$giro = $konto;
}
}
$this->assertEquals(1500.50, $giro['saldo_aktuell'], 'Aktueller Saldo entspricht dem Anfangssaldo');
$this->assertEquals(1500.50, $giro['saldo_erwartet'], 'Erwarteter Saldo entspricht dem Anfangssaldo');
$this->assertNull($giro['saldo_online'], 'Online-Saldo bleibt leer bis zum ersten Bankabruf');
// Wird die Gruppe gelöscht, bleiben die Konten erhalten.
Kontengruppe::loeschen($anlage);
$danach = Konto::uebersicht();
$this->assertEquals(1, count($danach['gruppen']), 'Nach dem Löschen bleibt nur „Nicht gruppiert“');
$this->assertEquals($summeErwartet, $danach['summe'], 'Die Summe ändert sich dadurch nicht');
}
private function betragsEingabe(): void
{
$this->assertEquals(1234.56, KontenController::zahl('1.234,56'), 'Deutsches Format mit Tausenderpunkt');
$this->assertEquals(-320.25, KontenController::zahl('-320,25'), 'Negativer Betrag');
$this->assertEquals(1234.56, KontenController::zahl('1234.56'), 'Punkt vor zwei Ziffern ist Dezimaltrenner');
$this->assertEquals(1500.0, KontenController::zahl('1.500'), 'Punkt vor drei Ziffern ist Tausendertrenner');
$this->assertEquals(1234567.0, KontenController::zahl('1.234.567'), 'Mehrere Punkte sind Tausendertrenner');
$this->assertEquals(0.5, KontenController::zahl('0.5'), 'Einzelne Nachkommastelle');
$this->assertEquals(0.0, KontenController::zahl(''), 'Leere Eingabe ergibt null');
$this->assertEquals(99.9, KontenController::zahl(' 99,90 € '), 'Währungszeichen und Leerzeichen stören nicht');
}
}