Finanzmanager/src/Models/Berichtsgruppe.php
fds2610-bot 90bba06585 Phase 4: Auswertungen mit Berichtsmodul
Fünf Berichtstypen wie im FinanzManager – Summen-, Buchungs-, Vergleichs-,
Kontostands- und Budgetbericht – mit Filterleiste, gespeicherten Berichten in
Berichtsgruppen, Drilldown in die Buchungen und CSV-Export.

Ein Bericht speichert nur seine Frage; die Zahlen entstehen bei jedem Aufruf neu
aus den Buchungen. Gezählt werden Bewegungen (gesplittete Buchungen über ihre
Splitzeilen), Umbuchungen bleiben außen vor, Unkategorisiertes bekommt eine
eigene Zeile. Im Ausgabenabschnitt steht der Betrag ohne Minus wie im Original,
die Schlusszeile rechnet mit den echten Vorzeichen.

Einzelheiten in docs/auswertungen.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 17:38:30 +02:00

110 lines
3.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace Finanzmanager\Models;
use Finanzmanager\Database;
use Finanzmanager\Mandant;
/**
* Berichtsgruppen bündeln gespeicherte Berichte zu Kacheln der Auswertungs-
* übersicht – im FinanzManager heißen sie „Bankgeschäfte“, „Planung“, „Steuern“.
* Reine Ordnung, keine Fachlogik: Ein Bericht funktioniert auch ohne Gruppe und
* steht dann unter „Nicht gruppiert“.
*/
class Berichtsgruppe
{
/** @return list<array<string,mixed>> */
public static function alle(): array
{
return Database::fetchAll(
'SELECT * FROM berichtsgruppen WHERE benutzer_id = ? ORDER BY sortierung, name',
[Mandant::id()]
);
}
public static function find(int $id): ?array
{
$zeile = Database::fetchOne(
'SELECT * FROM berichtsgruppen 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 berichtsgruppen WHERE name = ? AND benutzer_id = ?',
[trim($name), Mandant::id()]
);
return $zeile === false ? null : $zeile;
}
public static function anlegen(string $name, int $sortierung = 0): int
{
$name = self::pruefeName($name);
if (self::findeNachName($name) !== null) {
throw new \RuntimeException('Die Berichtsgruppe „' . $name . '“ gibt es bereits.');
}
Database::execute(
'INSERT INTO berichtsgruppen (benutzer_id, name, sortierung) VALUES (?, ?, ?)',
[Mandant::id(), $name, $sortierung]
);
return Database::lastInsertId();
}
public static function findenOderAnlegen(string $name): int
{
$vorhanden = self::findeNachName($name);
return $vorhanden === null ? self::anlegen($name) : (int)$vorhanden['id'];
}
public static function speichern(int $id, string $name, int $sortierung): void
{
$name = self::pruefeName($name);
$andere = self::findeNachName($name);
if ($andere !== null && (int)$andere['id'] !== $id) {
throw new \RuntimeException('Die Berichtsgruppe „' . $name . '“ gibt es bereits.');
}
Database::execute(
'UPDATE berichtsgruppen SET name = ?, sortierung = ? WHERE id = ? AND benutzer_id = ?',
[$name, $sortierung, $id, Mandant::id()]
);
}
/**
* Die Gruppe verschwindet, ihre Berichte bleiben: Der Fremdschlüssel setzt
* gruppe_id auf NULL, sie rücken damit unter „Nicht gruppiert“.
*/
public static function loeschen(int $id): void
{
Database::execute(
'DELETE FROM berichtsgruppen WHERE id = ? AND benutzer_id = ?',
[$id, Mandant::id()]
);
}
public static function anzahl(): int
{
return (int)Database::fetchValue(
'SELECT COUNT(*) FROM berichtsgruppen WHERE benutzer_id = ?',
[Mandant::id()],
0
);
}
private static function pruefeName(string $name): string
{
$name = trim($name);
if ($name === '') {
throw new \InvalidArgumentException('Die Berichtsgruppe braucht einen Namen.');
}
return mb_substr($name, 0, 120);
}
}