Damit ist der geplante Funktionsumfang beisammen. Phase 4 abgeschlossen - Konfigurierbare Cockpit-Kacheln; abgewählte werden nicht berechnet - Einnahmen/Ausgaben je Monat als Balkenpaare mit Saldolinie, aufgesetzt auf den Summenbericht statt auf eine eigene Abfrage - Meine Ausgaben als Ring auf der obersten Kategorieebene - Offene Vorgänge aus allen Modulen Phase 5 – Online-Banking - nemiah/php-fints als Fork auf Codeberg, psr/log direkt eingebaut: die Anwendung hängt an keinem fremden Update-Zyklus - PIN AES-256-GCM-verschlüsselt; Schlüssel aus APP_KEY oder aus data/schluessel/app_key, den der Entrypoint einmalig erzeugt - TAN-Dialog über mehrere Requests (FinTs::persist plus serialisierte Aktion in der Sitzung) – ein wartender Prozess wäre mit php-fpm der falsche Weg - Abgleich statt Direktbuchung: verbinden, übernehmen oder verwerfen - Regeln mit vier Vergleichsarten, Betragsgrenzen und Split-Schema Phase 6 – Wertpapiere - Depotbuchung und Geldbuchung als zwei Seiten eines Geschäfts; betrag ist der Bruttowert, Nebenkosten stehen daneben - Einstand nach gleitendem Durchschnitt, realisierte Gewinne, Erträge - Kursabruf über Leeway oder FMP, täglicher Lauf per Cron - QIF-Import für !Type:Invst Dabei behoben: Der Import buchte den Geldfluss jedes Wertpapiergeschäfts als Abgang – auch bei Verkäufen und Dividenden; und er erkannte das Verrechnungskonto nicht, wenn dem Konto im L-Feld eine Kategorie vorangeht (bei Verkäufen also immer). Phase 7 – Nebenmodule und Zahlungsverkehr - Buchungsvorlagen inklusive QIF-Import, Splits werden mitskaliert - Regelmäßige Buchungen mit Fälligkeitslauf, Finanzkalender, Erinnerungen - Adressen mit IBAN-Prüfung, Verträge mit Kündigungsfrist, Sparziele - Kredite mit gerechnetem Tilgungsplan - Überweisungen über FinTS; die Buchung entsteht erst nach Annahme durch die Bank. Die pain.001-Nachricht erzeugt SepaXml selbst Nebenbei: .dockerignore ergänzt (ohne sie brach der Build an data/mariadb ab), PlatzhalterController entfernt, zwei Inline-Handler beseitigt, die die Content-Security-Policy ohnehin blockierte. 888 PHP-Prüfungen und 45 E2E-Tests grün. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
57 lines
1.8 KiB
PHP
57 lines
1.8 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace Finanzmanager;
|
||
|
||
class Config
|
||
{
|
||
private static array $data = [];
|
||
|
||
public static function load(): void
|
||
{
|
||
$wurzel = dirname(__DIR__);
|
||
|
||
self::$data = [
|
||
'db_host' => self::env('DB_HOST', 'db'),
|
||
'db_name' => self::env('DB_NAME', 'finanzmanager'),
|
||
'db_user' => self::env('DB_USER', 'finanzmanager'),
|
||
'db_pass' => self::env('DB_PASS', 'finanzmanager'),
|
||
// Ohne APP_KEY in der Umgebung greift der beim ersten Containerstart
|
||
// erzeugte Schlüssel unter data/. Beides leer heißt: keine
|
||
// verschlüsselten Felder – Bankzugänge lassen sich dann nicht anlegen.
|
||
'app_key' => self::env('APP_KEY', '') ?: self::schluesseldatei($wurzel . '/data/schluessel/app_key'),
|
||
'debug_log' => self::env('DEBUG_LOG', '0') === '1',
|
||
'log_dir' => $wurzel . '/data/logs',
|
||
'import_dir' => $wurzel . '/data/import',
|
||
'base_url' => '',
|
||
];
|
||
}
|
||
|
||
public static function get(string $key, mixed $default = null): mixed
|
||
{
|
||
return self::$data[$key] ?? $default;
|
||
}
|
||
|
||
/** Nur für Tests: erlaubt das gezielte Überschreiben einzelner Werte. */
|
||
public static function set(string $key, mixed $value): void
|
||
{
|
||
self::$data[$key] = $value;
|
||
}
|
||
|
||
private static function env(string $key, string $default): string
|
||
{
|
||
$wert = $_ENV[$key] ?? getenv($key);
|
||
return ($wert === false || $wert === '') ? $default : (string)$wert;
|
||
}
|
||
|
||
/** Der vom Entrypoint erzeugte Schlüssel; fehlt die Datei, bleibt es leer. */
|
||
private static function schluesseldatei(string $pfad): string
|
||
{
|
||
if (!is_readable($pfad)) {
|
||
return '';
|
||
}
|
||
|
||
return trim((string)@file_get_contents($pfad));
|
||
}
|
||
}
|