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>
158 lines
6.6 KiB
PHP
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');
|
|
}
|
|
}
|