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>
100 lines
5.1 KiB
SQL
100 lines
5.1 KiB
SQL
-- Phase 6: Wertpapiere, Depotbuchungen und Kurse.
|
||
--
|
||
-- Ein Depot ist ein Konto der Kontoart „depot“. Was darin liegt, steht nicht in
|
||
-- `buchungen`, sondern in `depot_buchungen`: Dort gehören Stückzahl und Kurs
|
||
-- dazu, und der Saldo eines Depots ist keine Summe von Beträgen, sondern der
|
||
-- bewertete Bestand.
|
||
--
|
||
-- Die Geldseite läuft weiter über gewöhnliche Buchungen auf dem
|
||
-- Verrechnungskonto – ein Kauf nimmt dort Geld weg, eine Dividende bringt
|
||
-- welches. Beide Seiten sind über `buchung_id` verbunden.
|
||
|
||
CREATE TABLE IF NOT EXISTS wertpapiere (
|
||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
benutzer_id INT UNSIGNED NOT NULL,
|
||
name VARCHAR(255) NOT NULL,
|
||
wkn VARCHAR(16) NOT NULL DEFAULT '',
|
||
isin VARCHAR(16) NOT NULL DEFAULT '',
|
||
-- Wertpapierart aus dem QIF-Feld „&“: 1 Aktie, 2 Fonds, 3 Anleihe …
|
||
art VARCHAR(32) NOT NULL DEFAULT 'aktie',
|
||
waehrung CHAR(3) NOT NULL DEFAULT 'EUR',
|
||
-- Kürzel für den Kursabruf, falls der Anbieter die ISIN nicht kennt.
|
||
symbol VARCHAR(32) NOT NULL DEFAULT '',
|
||
aktiv TINYINT(1) NOT NULL DEFAULT 1,
|
||
notiz VARCHAR(255) NOT NULL DEFAULT '',
|
||
erstellt_am DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (id),
|
||
-- Die ISIN ist der verlässliche Schlüssel; Papiere ohne ISIN werden über
|
||
-- den Namen wiedergefunden, deshalb ist auch der eindeutig.
|
||
UNIQUE KEY uq_wertpapier_isin (benutzer_id, isin, name),
|
||
KEY idx_wertpapier_wkn (benutzer_id, wkn),
|
||
CONSTRAINT fk_wertpapier_benutzer FOREIGN KEY (benutzer_id)
|
||
REFERENCES benutzer (id) ON DELETE CASCADE
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
|
||
CREATE TABLE IF NOT EXISTS depot_buchungen (
|
||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
benutzer_id INT UNSIGNED NOT NULL,
|
||
konto_id INT UNSIGNED NOT NULL,
|
||
wertpapier_id INT UNSIGNED NOT NULL,
|
||
datum DATE NOT NULL,
|
||
valuta DATE NULL,
|
||
-- kauf, verkauf, dividende, einlage, entnahme, split
|
||
vorgang VARCHAR(24) NOT NULL DEFAULT 'kauf',
|
||
-- Der Vorgang, wie ihn der FinanzManager nennt (DivIn/DivAus/WP-Einlage …).
|
||
-- Er bleibt erhalten, weil die Zuordnung auf die eigenen Vorgänge nicht in
|
||
-- jedem Fall umkehrbar ist.
|
||
vorgang_qif VARCHAR(24) NOT NULL DEFAULT '',
|
||
stueck DECIMAL(18,6) NOT NULL DEFAULT 0.000000,
|
||
kurs DECIMAL(18,6) NULL,
|
||
-- Der Betrag der Wertpapierseite (QIF-Feld U).
|
||
betrag DECIMAL(15,2) NOT NULL DEFAULT 0.00,
|
||
gebuehren DECIMAL(15,2) NOT NULL DEFAULT 0.00,
|
||
steuern DECIMAL(15,2) NOT NULL DEFAULT 0.00,
|
||
waehrung CHAR(3) NOT NULL DEFAULT 'EUR',
|
||
-- Das Konto, über das die Geldseite läuft, und die dort erzeugte Buchung.
|
||
verrechnungskonto_id INT UNSIGNED NULL,
|
||
buchung_id INT UNSIGNED NULL,
|
||
notiz TEXT NULL,
|
||
quelle VARCHAR(16) NOT NULL DEFAULT 'manuell',
|
||
import_hash CHAR(40) NULL,
|
||
erstellt_am DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (id),
|
||
KEY idx_depot_konto (konto_id, datum),
|
||
KEY idx_depot_wertpapier (wertpapier_id, datum),
|
||
UNIQUE KEY uq_depot_import (benutzer_id, import_hash),
|
||
CONSTRAINT fk_depot_benutzer FOREIGN KEY (benutzer_id)
|
||
REFERENCES benutzer (id) ON DELETE CASCADE,
|
||
CONSTRAINT fk_depot_konto FOREIGN KEY (konto_id)
|
||
REFERENCES konten (id) ON DELETE CASCADE,
|
||
CONSTRAINT fk_depot_wertpapier FOREIGN KEY (wertpapier_id)
|
||
REFERENCES wertpapiere (id) ON DELETE CASCADE,
|
||
CONSTRAINT fk_depot_verrechnung FOREIGN KEY (verrechnungskonto_id)
|
||
REFERENCES konten (id) ON DELETE SET NULL,
|
||
-- Wird die Geldbuchung gelöscht, bleibt die Depotbuchung bestehen; sie ist
|
||
-- das eigentliche Geschäft.
|
||
CONSTRAINT fk_depot_buchung FOREIGN KEY (buchung_id)
|
||
REFERENCES buchungen (id) ON DELETE SET NULL
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
|
||
-- Ein Kurs je Wertpapier und Tag. Mehr Auflösung braucht eine Vermögensübersicht
|
||
-- nicht, und der Kursdienst liefert im Freikontingent ohnehin nur Tagesschlüsse.
|
||
CREATE TABLE IF NOT EXISTS kurse (
|
||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
wertpapier_id INT UNSIGNED NOT NULL,
|
||
datum DATE NOT NULL,
|
||
kurs DECIMAL(18,6) NOT NULL,
|
||
waehrung CHAR(3) NOT NULL DEFAULT 'EUR',
|
||
quelle VARCHAR(32) NOT NULL DEFAULT 'manuell',
|
||
erstellt_am DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY uq_kurs (wertpapier_id, datum),
|
||
CONSTRAINT fk_kurs_wertpapier FOREIGN KEY (wertpapier_id)
|
||
REFERENCES wertpapiere (id) ON DELETE CASCADE
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||
|
||
INSERT INTO einstellungen (schluessel, wert, bereich) VALUES
|
||
('wertpapiere_kursdienst', 'leeway', 'wertpapiere'),
|
||
('wertpapiere_api_key', '', 'wertpapiere'),
|
||
('wertpapiere_kursabruf_aktiv', '0', 'wertpapiere')
|
||
ON DUPLICATE KEY UPDATE schluessel = schluessel;
|