Budgetplaner mit Monatswerten je Kategorie, Monatsabschluss mit Prüflauf, Snapshot und Buchungssperre. Dazu drei Vorarbeiten in der Oberfläche. Budgetplaner - Migration 004: budgets, budget_positionen, monatsabschluesse - Zeilen sind der Kategoriebaum, Spalten die Monate; die ganze Tabelle wird in einem Formular gespeichert, damit mehrere Änderungen zusammen ankommen - Zwei Ansichten: Verlauf (Jahressumme, abgelaufene Monate zusammengefasst, laufender Monat mit Budget/Ist/Saldo, danach Folgemonate) und alle Monate - Ausgaben ohne Vorzeichen eingeben, es kommt aus der Kategorieart. Saldo ist Ist minus Budget: positiv heißt besser als geplant, negativ ist die Überschreitung und wird rot ausgewiesen - Übergeordnete Kategorien summieren ihre Nachfahren und sind nicht beplanbar - Ein Monat ohne Position ist "nicht geplant" und damit etwas anderes als "mit 0,00 geplant" - Wert auf Folgemonate übertragen, Budget als Kopie ins neue Jahr Monatsabschluss - Prüflauf über nicht kategorisierte Buchungen, nicht abgeglichene Umsätze und Splitsummen-Differenzen; er hält niemanden auf, sondern zeigt den Stand - Snapshot der Kontosalden und Kategoriesummen als JSON, bleibt auch nach einer Wiedereröffnung stehen - Abgeschlossene Monate sperren Buchungen in beide Richtungen: weder hinein noch heraus verschieben. Abschaltbar über monatsabschluss_sperrt_buchungen Vorarbeiten - .knopf.primaer trug einen eigenen margin-top und stand dadurch in jeder Knopfleiste tiefer als seine Nachbarn - sichtbar an "Abbrechen" neben "Konto anlegen". Der Abstand liegt jetzt allein bei der Knopfleiste - Kontengruppen werden wie Konten und Kategorien auf einer eigenen Formularseite gepflegt statt inline; dort hängt auch die Zuordnung der Konten (ankreuzen statt jedes Konto einzeln zu öffnen) - Der QIF-Export enthält keine Kontengruppen: kein Feld im !Account-Eintrag, nichts in der QSD-Datei. Import-Oberfläche und Doku sagen das jetzt - Die Baum-Einrückung lief über ein style-Attribut und wurde von der Content-Security-Policy verworfen; sie steht jetzt als data-ebene im Markup. Aus demselben Grund blendete app.js Erfolgsmeldungen nie aus Tests: 11_BudgetTest, 12_MonatsabschlussTest (76 Prüfungen), E2E planung.spec.js. Einzelheiten in docs/planung.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
58 lines
1.4 KiB
PHP
58 lines
1.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Finanzmanager\Controllers;
|
|
|
|
use Finanzmanager\Auth;
|
|
use Finanzmanager\View;
|
|
|
|
abstract class BaseController
|
|
{
|
|
protected View $view;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->view = new View();
|
|
}
|
|
|
|
/** Seiten hinter der Anmeldung rufen das zuerst auf. */
|
|
protected function verlangeAnmeldung(): void
|
|
{
|
|
Auth::verlange();
|
|
}
|
|
|
|
protected function render(string $template, array $daten = []): void
|
|
{
|
|
$daten['csrfToken'] = Auth::csrfToken();
|
|
$daten['benutzer'] = Auth::benutzer();
|
|
$this->view->render($template, $daten);
|
|
}
|
|
|
|
protected function post(string $feld, string $standard = ''): string
|
|
{
|
|
$wert = $_POST[$feld] ?? $standard;
|
|
return is_string($wert) ? trim($wert) : $standard;
|
|
}
|
|
|
|
/**
|
|
* Mehrfachauswahl aus dem Formular (Ankreuzfelder mit Namen wie „konten[]“).
|
|
*
|
|
* @return list<int>
|
|
*/
|
|
protected function postIds(string $feld): array
|
|
{
|
|
$werte = $_POST[$feld] ?? [];
|
|
if (!is_array($werte)) {
|
|
return [];
|
|
}
|
|
|
|
return array_values(array_map('intval', array_filter($werte, 'is_scalar')));
|
|
}
|
|
|
|
protected function query(string $feld, string $standard = ''): string
|
|
{
|
|
$wert = $_GET[$feld] ?? $standard;
|
|
return is_string($wert) ? trim($wert) : $standard;
|
|
}
|
|
}
|