-- Phase 3: Budgetplaner und Monatsabschluss. -- -- Ein Budget gilt für genau ein Jahr; mehrere Budgets pro Jahr sind erlaubt -- („2027-sparsam“ neben „2027-normal“), eines davon ist das aktive. Die Werte -- stehen monatsweise je Kategorie – das entspricht dem Budgetplaner des -- FinanzManagers, in dem jede Kategoriezeile zwölf Monatsspalten hat. CREATE TABLE IF NOT EXISTS budgets ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, benutzer_id INT UNSIGNED NOT NULL, name VARCHAR(120) NOT NULL, jahr SMALLINT UNSIGNED NOT NULL, beschreibung VARCHAR(255) NOT NULL DEFAULT '', -- Genau ein Budget je Benutzer ist aktiv; es öffnet sich beim Aufruf des -- Budgetplaners und liefert die Werte für Auswertungen (Phase 4). aktiv TINYINT(1) NOT NULL DEFAULT 0, erstellt_am DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, geaendert_am DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uq_budget_name (benutzer_id, name), KEY idx_budget_jahr (benutzer_id, jahr), CONSTRAINT fk_budget_benutzer FOREIGN KEY (benutzer_id) REFERENCES benutzer (id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Eine Zeile je Kategorie und Monat. Nicht geplante Monate haben keine Zeile – -- das hält die Tabelle klein und macht „kein Budget“ von „Budget 0,00“ -- unterscheidbar. Das Vorzeichen folgt der Buchhaltung: Ausgaben negativ, -- Einnahmen positiv (Budget::positionSetzen dreht die Eingabe entsprechend). CREATE TABLE IF NOT EXISTS budget_positionen ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, budget_id INT UNSIGNED NOT NULL, kategorie_id INT UNSIGNED NOT NULL, monat TINYINT UNSIGNED NOT NULL, betrag DECIMAL(15,2) NOT NULL DEFAULT 0.00, PRIMARY KEY (id), UNIQUE KEY uq_budget_position (budget_id, kategorie_id, monat), KEY idx_position_kategorie (kategorie_id), CONSTRAINT fk_position_budget FOREIGN KEY (budget_id) REFERENCES budgets (id) ON DELETE CASCADE, CONSTRAINT fk_position_kategorie FOREIGN KEY (kategorie_id) REFERENCES kategorien (id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Ein abgeschlossener Monat sperrt Buchungen in seinem Zeitraum. Der Snapshot -- hält Kontosalden und Kategoriesummen als JSON fest, damit später -- nachvollziehbar bleibt, mit welchen Zahlen abgeschlossen wurde – auch wenn -- der Monat wiedereröffnet und nachgebucht wird. CREATE TABLE IF NOT EXISTS monatsabschluesse ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, benutzer_id INT UNSIGNED NOT NULL, jahr SMALLINT UNSIGNED NOT NULL, monat TINYINT UNSIGNED NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'abgeschlossen', notiz VARCHAR(255) NOT NULL DEFAULT '', snapshot LONGTEXT NULL, abgeschlossen_am DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, abgeschlossen_von VARCHAR(120) NOT NULL DEFAULT '', wiedereroeffnet_am DATETIME NULL, PRIMARY KEY (id), UNIQUE KEY uq_monatsabschluss (benutzer_id, jahr, monat), CONSTRAINT fk_abschluss_benutzer FOREIGN KEY (benutzer_id) REFERENCES benutzer (id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; INSERT INTO einstellungen (schluessel, wert, bereich) VALUES ('budget_alarm_ab_prozent', '100', 'buchhaltung'), ('monatsabschluss_sperrt_buchungen', '1', 'buchhaltung') ON DUPLICATE KEY UPDATE schluessel = schluessel;