'Januar', 2 => 'Februar', 3 => 'März', 4 => 'April', 5 => 'Mai', 6 => 'Juni', 7 => 'Juli', 8 => 'August', 9 => 'September', 10 => 'Oktober', 11 => 'November', 12 => 'Dezember', ]; public const MONATSKUERZEL = [ 1 => 'Jan', 2 => 'Feb', 3 => 'Mär', 4 => 'Apr', 5 => 'Mai', 6 => 'Jun', 7 => 'Jul', 8 => 'Aug', 9 => 'Sep', 10 => 'Okt', 11 => 'Nov', 12 => 'Dez', ]; /** * Ist-Werte je Kategorie und Monat aus den Buchungen eines Jahres. * * Gesplittete Buchungen zählen über ihre Splitzeilen, alle anderen über die * Buchung selbst – sonst würde eine Splitbuchung doppelt oder gar nicht * erfasst. Umbuchungen tragen keine Kategorie und bleiben damit außen vor, * wie im FinanzManager auch. * * @return array> [kategorie_id][monat] => Betrag */ public static function istWerte(int $jahr): array { $zeilen = Database::fetchAll( 'SELECT kategorie_id, monat, SUM(betrag) AS betrag FROM ( SELECT b.kategorie_id AS kategorie_id, MONTH(b.datum) AS monat, b.betrag AS betrag FROM buchungen b WHERE b.benutzer_id = ? AND YEAR(b.datum) = ? AND b.gesplittet = 0 AND b.kategorie_id IS NOT NULL UNION ALL SELECT s.kategorie_id AS kategorie_id, MONTH(b.datum) AS monat, s.betrag AS betrag FROM buchung_splits s JOIN buchungen b ON b.id = s.buchung_id WHERE b.benutzer_id = ? AND YEAR(b.datum) = ? AND b.gesplittet = 1 AND s.kategorie_id IS NOT NULL ) AS bewegungen GROUP BY kategorie_id, monat', [Mandant::id(), $jahr, Mandant::id(), $jahr] ); $werte = []; foreach ($zeilen as $zeile) { $werte[(int)$zeile['kategorie_id']][(int)$zeile['monat']] = (float)$zeile['betrag']; } return $werte; } /** * Die vollständige Tabelle: eine Zeile je Kategorie in Baumreihenfolge, * dazu die Gesamtzeile. * * @param array $budget * @return array{zeilen: list>, gesamt: array, jahr: int} */ public static function tabelle(array $budget): array { $jahr = (int)$budget['jahr']; $geplant = Budget::positionen((int)$budget['id']); $ist = self::istWerte($jahr); $kategorien = Kategorie::baumFlach(); // Erst die eigenen Werte je Kategorie, dann von unten nach oben die // Summen der Nachfahren aufaddieren. $zeilen = []; foreach ($kategorien as $kategorie) { $id = (int)$kategorie['id']; $zeilen[$id] = $kategorie + [ 'budget' => self::monatsreihe($geplant[$id] ?? []), 'ist' => self::monatsreihe($ist[$id] ?? []), // Ungefüllt statt mit Nullen aufgefüllt: Nur so bleibt „nicht // geplant“ von „mit 0,00 geplant“ unterscheidbar. Der Planer // zeigt in dem einen Fall ein leeres Feld, im anderen eine Null. 'eigenBudget' => $geplant[$id] ?? [], ]; } foreach (array_reverse(array_keys($zeilen)) as $id) { $eltern = $zeilen[$id]['eltern_id'] === null ? null : (int)$zeilen[$id]['eltern_id']; if ($eltern === null || !isset($zeilen[$eltern])) { continue; } for ($monat = 1; $monat <= 12; $monat++) { $zeilen[$eltern]['budget'][$monat] += $zeilen[$id]['budget'][$monat]; $zeilen[$eltern]['ist'][$monat] += $zeilen[$id]['ist'][$monat]; } } $gesamtBudget = self::monatsreihe([]); $gesamtIst = self::monatsreihe([]); foreach ($zeilen as &$zeile) { $zeile['saldo'] = self::differenz($zeile['ist'], $zeile['budget']); $zeile['jahrBudget'] = array_sum($zeile['budget']); $zeile['jahrIst'] = array_sum($zeile['ist']); $zeile['jahrSaldo'] = $zeile['jahrIst'] - $zeile['jahrBudget']; $zeile['geplant'] = $zeile['jahrBudget'] !== 0.0 || $zeile['jahrIst'] !== 0.0; // Nur Wurzeln summieren, sonst zählten Unterkategorien mehrfach. if ($zeile['eltern_id'] === null) { for ($monat = 1; $monat <= 12; $monat++) { $gesamtBudget[$monat] += $zeile['budget'][$monat]; $gesamtIst[$monat] += $zeile['ist'][$monat]; } } } unset($zeile); return [ 'jahr' => $jahr, 'zeilen' => array_values($zeilen), 'gesamt' => [ 'budget' => $gesamtBudget, 'ist' => $gesamtIst, 'saldo' => self::differenz($gesamtIst, $gesamtBudget), 'jahrBudget' => array_sum($gesamtBudget), 'jahrIst' => array_sum($gesamtIst), 'jahrSaldo' => array_sum($gesamtIst) - array_sum($gesamtBudget), ], ]; } /** Der Monat, der im laufenden Jahr gerade läuft – sonst 0. */ public static function laufenderMonat(int $jahr, ?string $heute = null): int { $heute = $heute ?? date('Y-m-d'); return $jahr === (int)substr($heute, 0, 4) ? (int)substr($heute, 5, 2) : 0; } /** * @param array $werte * @return array */ private static function monatsreihe(array $werte): array { $reihe = []; for ($monat = 1; $monat <= 12; $monat++) { $reihe[$monat] = (float)($werte[$monat] ?? 0.0); } return $reihe; } /** * @param array $ist * @param array $budget * @return array */ private static function differenz(array $ist, array $budget): array { $saldo = []; for ($monat = 1; $monat <= 12; $monat++) { $saldo[$monat] = $ist[$monat] - $budget[$monat]; } return $saldo; } }