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>
107 lines
3.3 KiB
PHP
107 lines
3.3 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace Finanzmanager\Models;
|
||
|
||
use Finanzmanager\Database;
|
||
use Finanzmanager\Mandant;
|
||
|
||
/**
|
||
* Klassen sind die zweite Auswertungsdimension neben der Kategorie – flach,
|
||
* ohne Hierarchie. Im QIF hängen sie mit "/" an der Kategorie
|
||
* ("400 - Leben:B-Essen alleine/Privat").
|
||
*/
|
||
class Klasse
|
||
{
|
||
/** @return list<array<string,mixed>> */
|
||
public static function alle(bool $nurAktive = false): array
|
||
{
|
||
$sql = 'SELECT * FROM klassen WHERE benutzer_id = ?';
|
||
if ($nurAktive) {
|
||
$sql .= ' AND aktiv = 1';
|
||
}
|
||
return Database::fetchAll($sql . ' ORDER BY name', [Mandant::id()]);
|
||
}
|
||
|
||
public static function find(int $id): ?array
|
||
{
|
||
$zeile = Database::fetchOne(
|
||
'SELECT * FROM klassen WHERE id = ? AND benutzer_id = ?',
|
||
[$id, Mandant::id()]
|
||
);
|
||
return $zeile === false ? null : $zeile;
|
||
}
|
||
|
||
public static function findeNachName(string $name): ?array
|
||
{
|
||
$zeile = Database::fetchOne(
|
||
'SELECT * FROM klassen WHERE name = ? AND benutzer_id = ?',
|
||
[trim($name), Mandant::id()]
|
||
);
|
||
return $zeile === false ? null : $zeile;
|
||
}
|
||
|
||
public static function anlegen(string $name, string $beschreibung = ''): int
|
||
{
|
||
$name = trim($name);
|
||
if ($name === '') {
|
||
throw new \InvalidArgumentException('Die Klasse braucht einen Namen.');
|
||
}
|
||
if (self::findeNachName($name) !== null) {
|
||
throw new \RuntimeException('Die Klasse „' . $name . '“ gibt es bereits.');
|
||
}
|
||
|
||
Database::execute(
|
||
'INSERT INTO klassen (benutzer_id, name, beschreibung) VALUES (?, ?, ?)',
|
||
[Mandant::id(), $name, $beschreibung]
|
||
);
|
||
return Database::lastInsertId();
|
||
}
|
||
|
||
/**
|
||
* Legt die Klasse an, falls es sie noch nicht gibt, und liefert in jedem
|
||
* Fall ihre Kennung. Der QIF-Import läuft ausschließlich hierüber, damit ein
|
||
* zweiter Importlauf keine Dubletten erzeugt.
|
||
*/
|
||
public static function findenOderAnlegen(string $name, string $beschreibung = ''): int
|
||
{
|
||
$vorhanden = self::findeNachName($name);
|
||
return $vorhanden !== null ? (int)$vorhanden['id'] : self::anlegen($name, $beschreibung);
|
||
}
|
||
|
||
public static function speichern(int $id, string $name, string $beschreibung, bool $aktiv): void
|
||
{
|
||
$name = trim($name);
|
||
if ($name === '') {
|
||
throw new \InvalidArgumentException('Die Klasse braucht einen Namen.');
|
||
}
|
||
|
||
$andere = self::findeNachName($name);
|
||
if ($andere !== null && (int)$andere['id'] !== $id) {
|
||
throw new \RuntimeException('Die Klasse „' . $name . '“ gibt es bereits.');
|
||
}
|
||
|
||
Database::execute(
|
||
'UPDATE klassen SET name = ?, beschreibung = ?, aktiv = ? WHERE id = ? AND benutzer_id = ?',
|
||
[$name, $beschreibung, $aktiv ? 1 : 0, $id, Mandant::id()]
|
||
);
|
||
}
|
||
|
||
public static function loeschen(int $id): void
|
||
{
|
||
Database::execute(
|
||
'DELETE FROM klassen WHERE id = ? AND benutzer_id = ?',
|
||
[$id, Mandant::id()]
|
||
);
|
||
}
|
||
|
||
public static function anzahl(): int
|
||
{
|
||
return (int)Database::fetchValue(
|
||
'SELECT COUNT(*) FROM klassen WHERE benutzer_id = ?',
|
||
[Mandant::id()],
|
||
0
|
||
);
|
||
}
|
||
}
|