Die Umsatzlisten haben jetzt dieselbe Mehrfachauswahl wie ein Kontoblatt: ein Ankreuzfeld vor jedem Umsatz, eines in der Titelleiste für alle angezeigten und darüber der Knopf „Auswahl löschen“. Er steht in jedem Reiter – offen, übernommen und verworfen –, nennt die Anzahl der Auswahl und bleibt ohne Auswahl gesperrt. Bisher ließ sich ein abgerufener Umsatz nur übernehmen oder verwerfen; wegräumen ging gar nicht. Gelöscht wird allein die abgerufene Zeile. Eine daraus entstandene Buchung gehört ins Kontoblatt und bleibt dort stehen – wer sie loswerden will, löscht sie dort, und dann kehrt ihr Umsatz ohnehin in die Vorschlagsliste zurück. Mit dem Umsatz geht aber sein Fingerabdruck: Was im nächsten Abrufzeitraum liegt, kommt danach wieder herein, bei einem verworfenen Umsatz also erneut als offen. - Neue Route POST /banking/umsaetze/loeschen auf Bankumsatz::mehrereLoeschen(); der Status hängt am Formularziel, sodass der Weg in denselben Reiter zurückführt. Ohne JavaScript weist der Controller eine leere Auswahl ab. - Die Liste ist keine Tabelle, sondern eine Folge von Karten. Die Kopfzeile, die eine Tabelle von selbst hätte, gibt es deshalb neu als .listenkopf; die Mechanik dahinter (.alle-waehlen mit data-waehlt) ist unverändert die des Kontoblatts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
344 lines
19 KiB
Markdown
344 lines
19 KiB
Markdown
# Online-Banking (FinTS)
|
||
|
||
Umsätze kommen per Knopfdruck von der Bank, werden gegen die vorhandenen Buchungen
|
||
abgeglichen und über Regeln weitgehend selbst kategorisiert.
|
||
|
||
## Die Bibliothek: ein eigener Fork
|
||
|
||
Verwendet wird `nemiah/php-fints`, aber nicht von Packagist, sondern als **Fork auf
|
||
Codeberg** (`fds2610-bot/phpFinTS`, eingebunden über ein VCS-Repository in der
|
||
`composer.json`, Version fest auf `4.1.1` gepinnt).
|
||
|
||
Im Fork ist `psr/log` **direkt eingebaut**: Die acht Interface-Dateien liegen unter
|
||
`psr-log/Psr/Log/` und werden über einen eigenen Autoload-Eintrag geladen, der
|
||
`require`-Eintrag ist entfallen. Damit hängt der Finanzmanager an keinem fremden
|
||
Update-Zyklus – die Anwendung hat genau eine Abhängigkeit, und die steht unter eigener
|
||
Kontrolle.
|
||
|
||
Beim Nachziehen eines neuen Upstream-Standes muss dieser Eingriff wiederholt werden; wie,
|
||
steht in `psr-log/README.md` des Forks.
|
||
|
||
## Der Schlüssel für die Zugangsdaten
|
||
|
||
PIN und TAN-Medium stehen AES-256-GCM-verschlüsselt in der Datenbank (`Services\Crypto`,
|
||
übernommen aus `Steuerkram`). Der Schlüssel ist eine 64-stellige Hex-Zeichenkette:
|
||
|
||
* aus der Umgebung (`APP_KEY` in der `.env`), oder
|
||
* aus `data/schluessel/app_key` – die Datei erzeugt der Container-Entrypoint beim ersten
|
||
Start, wenn `APP_KEY` leer ist.
|
||
|
||
Das Verzeichnis ist ein eigenes Bind-Mount, damit der Schlüssel einen Neubau des Images
|
||
überlebt. **Er gehört ins Backup**: Ohne ihn sind gespeicherte Zugangsdaten nicht mehr
|
||
lesbar (neu eingeben hilft, verloren ist sonst nichts).
|
||
|
||
## Ablauf eines Abrufs
|
||
|
||
Der Abruf ist eine Zustandsmaschine über mehrere Requests:
|
||
|
||
```
|
||
anmelden → konten → umsaetze (je Konto) → fertig
|
||
```
|
||
|
||
Jeder Schritt kann eine TAN verlangen – bei den meisten Banken schon der Login, auch für
|
||
reines Lesen. Dann sichert `Services\FinTsAbruf` den Dialog (`FinTs::persist()`) und die
|
||
serialisierte Aktion in der **Sitzung**, zeigt die TAN-Maske und macht nach der Eingabe an
|
||
derselben Stelle weiter.
|
||
|
||
Ein wartender Prozess wäre der naheliegende, aber falsche Weg: php-fpm hat dafür keinen
|
||
Platz, und ein Request, der fünf Minuten auf eine TAN wartet, blockiert einen Worker.
|
||
|
||
Was in der Sitzung liegt, enthält keine PIN – die Bibliothek schließt sie beim Persistieren
|
||
ausdrücklich aus. Sie wird bei jedem Request neu aus der Datenbank geholt und entschlüsselt.
|
||
|
||
### Wie weit zurück: Zeitraum und Stichtag
|
||
|
||
Ab wann abgerufen wird, bestimmt `Bankzugang::abrufBeginn()`. Zwei Angaben spielen hinein:
|
||
|
||
* Der **Abrufzeitraum** der Bankverbindung (`bank_zugaenge.abrufzeitraum`): seit dem letzten
|
||
Abruf, letzte 7/30/365 Tage – oder *Ab Stichtag* mit dem Datum aus
|
||
`bank_zugaenge.abruf_stichtag`.
|
||
* Ein **Stichtag für diesen einen Abruf**, angegeben im Datumsfeld neben *Umsätze abrufen*.
|
||
Er geht der Vorgabe vor und wird **nirgends gespeichert**.
|
||
|
||
Beides nebeneinander, weil beides gebraucht wird: Die Vorgabe beantwortet den Alltag („immer
|
||
seit dem letzten Abruf“), der einmalige Stichtag den Sonderfall – einen Bestand ab einem
|
||
festen Datum aufbauen, eine Lücke nachholen. Ohne ihn müsste man die Einstellung des Zugangs
|
||
ändern und hinterher zurückstellen, mit dem üblichen Ausgang, dass man das Zurückstellen
|
||
vergisst.
|
||
|
||
Der einmalige Stichtag liegt im **Abrufzustand**, nicht in der Datenbank: Nach einer
|
||
TAN-Runde läuft der Abruf im nächsten Request weiter und muss dort denselben Zeitraum holen
|
||
wie beim Start.
|
||
|
||
„Seit dem letzten Abruf“ nimmt den **Tag** des letzten Laufs, nicht die Uhrzeit: Banken
|
||
buchen nachträglich, und ein Umsatz, der schon da war, fällt ohnehin über seinen
|
||
Fingerabdruck heraus. Beim allerersten Abruf gilt ein Jahr zurück – sonst käme entweder
|
||
nichts oder alles.
|
||
|
||
Ein Stichtag **in der Zukunft** wird auf heute gesetzt statt abgewiesen: Aus der Zukunft
|
||
liefert keine Bank etwas, und ein verrutschtes Datum soll den Abruf nicht ergebnislos machen
|
||
– das Protokoll vermerkt die Korrektur. *Ab Stichtag* **ohne** Datum lässt sich dagegen gar
|
||
nicht erst speichern; eine Vorgabe, die nichts vorgibt, führte beim Abruf nur zu einem
|
||
anderen Zeitraum als gedacht.
|
||
|
||
Wie weit die Bank tatsächlich zurückreicht, entscheidet sie selbst – 90 Tage sind üblich,
|
||
mehr liefert nicht jede. Ein früherer Stichtag holt deshalb nicht zwingend mehr; die Lücke
|
||
davor schließt der Anfangssaldo des Kontos oder der QIF-Import.
|
||
|
||
### TAN-Verfahren
|
||
|
||
* **Klassisch** (chipTAN, photoTAN): Die Maske zeigt den Hinweistext der Bank und, wenn
|
||
vorhanden, die photoTAN-Grafik als Datenadresse. Der Nutzer tippt die TAN ein.
|
||
* **Entkoppelt** (pushTAN-App): Die Freigabe passiert im Gerät des Nutzers. Statt zu warten,
|
||
fragt die Anwendung auf Knopfdruck nach (`checkDecoupledSubmission`) – steht die Freigabe
|
||
noch aus, bleibt die Maske stehen.
|
||
|
||
### Einrichtung in drei Schritten
|
||
|
||
Fast jede Bank bietet mehrere Verfahren an, und welche das sind, weiß nur sie. Fragen lässt
|
||
sie sich aber erst, wenn Bankleitzahl, FinTS-Adresse, Benutzerkennung und PIN vorliegen –
|
||
deshalb zerfällt das Anlegen einer Bankverbindung in drei Schritte:
|
||
|
||
1. **Zugangsdaten** (`/banking/neu`) – *Weiter* speichert sie.
|
||
2. **TAN-Verfahren** (`/banking/{id}/verfahren`) – die Seite fragt die Bank beim Aufruf
|
||
selbst und zeigt die Liste zur Wahl.
|
||
3. **Konten zuordnen** (`/banking/{id}/konten`) – welches Bankkonto gehört zu welchem
|
||
eigenen Konto?
|
||
|
||
Der zweite Schritt holt die Liste **ohne Knopfdruck**: Wer hier landet, will genau das, und
|
||
ein Knopf, dessen Ergebnis feststeht, wäre ein Schritt zu viel. Ein erneuter Anlauf steht
|
||
trotzdem bereit, wenn sich etwas geändert hat.
|
||
|
||
Am Ende des dritten Schritts – und ebenso beim vorzeitigen Verlassen über *Später
|
||
einstellen* oder *Später zuordnen* – geht es zurück in die **Übersicht der
|
||
Bankverbindungen** (`BankingController::zielNachZuordnung()`), nicht in die Zugangsmaske:
|
||
Dort steht die neue Verbindung fertig zwischen den anderen, während die Zugangsmaske nur
|
||
noch anböte, was gerade durchlaufen wurde. Einzeln aufgerufen bleibt die Zuordnung dagegen
|
||
auf ihrer Seite stehen.
|
||
|
||
Später erreicht man dieselbe Seite über *TAN-Verfahren einstellen* am Zugang.
|
||
|
||
**Abgefragt, nicht eingetippt.** Die Kennungen sind bankspezifisch – 913 heißt nicht
|
||
überall chipTAN QR –, und niemand soll sie in einer PDF der Bank suchen müssen.
|
||
`getTanModes()` kommt ohne Anmeldung aus (die Bibliothek braucht dafür nur die
|
||
Bankparameterdaten) und verlangt deshalb selbst keine TAN.
|
||
|
||
`getTanMedia()` wird nur für die Verfahren aufgerufen, die ein Medium brauchen: Jeder
|
||
Aufruf ist ein eigener Dialog mit der Bank.
|
||
|
||
Die Zeitüberschreitungen sind kürzer gesetzt als in der Bibliothek (8 statt 15 Sekunden zum
|
||
Verbinden, 20 statt 30 zur Antwort): Die Abfrage hängt an einem Seitenaufruf.
|
||
|
||
Das Ergebnis liegt in der Sitzung, **auch ein gescheitertes** – sonst liefe jeder
|
||
Seitenaufruf erneut in dieselbe Zeitüberschreitung. Scheitert die Abfrage, erklärt die
|
||
Seite, wo der Fehler meist steckt, und der Weg zurück zu den Zugangsdaten steht daneben.
|
||
Die Wahl lässt sich überspringen; die Verbindung ist auch ohne sie benutzbar.
|
||
|
||
Beim Übernehmen wird gegen die abgefragte Liste geprüft: Eine Kennung, die die Bank nicht
|
||
angeboten hat, wäre beim nächsten Abruf wirkungslos.
|
||
|
||
**Ohne Wahl** nimmt der erste Abruf das erste Verfahren, das die Bank meldet, und hält es
|
||
fest; sonst stünde im Formular nichts. Eine **ausdrückliche** Wahl wird dagegen nie
|
||
überschrieben: Bietet die Bank sie einmal nicht an, sagt das Protokoll das und die
|
||
Einstellung bleibt stehen.
|
||
|
||
Das Zugangsformular fasst Verfahren und Medium nicht an: Ohne diesen Vorbehalt setzte jedes
|
||
Speichern die Wahl zurück.
|
||
|
||
Abruf und Überweisung benutzen dieselbe Wahl (`Services\FinTsVerfahren::anwenden()`) – eine
|
||
Überweisung soll nicht plötzlich ein anderes Verfahren verlangen als der Abruf.
|
||
|
||
### Konten zuordnen
|
||
|
||
Für jedes Konto, das die Bank meldet, legt die Anwendung eine Zeile in `bank_konten` an.
|
||
Trägt ein eigenes Konto dieselbe IBAN, wird es gleich zugeordnet; sonst geschieht das auf
|
||
der Zuordnungsseite. **Ohne Zuordnung lassen sich die Umsätze nicht buchen** – das Konto
|
||
fehlt dann schlicht.
|
||
|
||
Die Liste kommt frisch von der Bank. Anders als die Verfahrensabfrage braucht das eine
|
||
**Anmeldung**, und die verlangt bei den meisten Banken eine TAN – deshalb steht dafür ein
|
||
Knopf und keine Automatik beim Seitenaufruf. Im Einrichtungsablauf startet das Übernehmen
|
||
des TAN-Verfahrens die Abfrage gleich mit; sie benutzt dieselbe Zustandsmaschine wie der
|
||
Umsatzabruf, hält aber nach der Kontenabfrage an (`FinTsAbruf::starten($id, 'konten')`).
|
||
|
||
Fehlt ein passendes eigenes Konto, legt *neues Konto anlegen* es an. IBAN, Kontonummer und
|
||
Bankname kommen aus dem, was die Bank gemeldet hat; einzutragen bleiben Name, Kontoart und
|
||
Anfangssaldo – letzterer deckt die Zeit ab, die der Abruf nicht mehr hergibt.
|
||
|
||
Wird die Zuordnung **nach** einem Abruf getroffen, ziehen die schon abgerufenen **offenen**
|
||
Umsätze mit: `bank_umsaetze.bank_konto_id` merkt sich, von welchem Bankkonto ein Umsatz
|
||
stammt. Übernommene bleiben, wo sie sind – ihre Buchung steht bereits in einem Kontoblatt,
|
||
und ein Kontowechsel würde sie dort wegziehen.
|
||
|
||
Was nicht abgerufen werden soll, wird dort abgewählt; das ist der Kontenfilter.
|
||
|
||
Der Saldo wird mitgeholt und landet in der Spalte *Online-Saldo* der Kontenübersicht.
|
||
Verlangt die Bank dafür eine eigene TAN, entfällt er – eine zweite TAN-Runde nur für eine
|
||
Zahl wäre niemandem zu erklären.
|
||
|
||
## Vom Umsatz zur Buchung
|
||
|
||
Abgerufene Umsätze landen zuerst in `bank_umsaetze`. Ob sie von dort selbst weiterwandern,
|
||
entscheidet *Abgerufene Umsätze selbst buchen* (`banking_automatisch_buchen`, Vorgabe **an**)
|
||
in den Einstellungen.
|
||
|
||
### Automatisch buchen
|
||
|
||
Eingeschaltet läuft nach jedem Abruf – und nach dem Speichern der Kontenzuordnung –
|
||
`Services\Bankabgleich` über alle offenen Umsätze:
|
||
|
||
* Passt eine schon erfasste Buchung, wird **verbunden** statt neu gebucht. Sonst stünde jede
|
||
selbst erfasste Zahlung doppelt im Kontoblatt.
|
||
* Sonst entsteht eine **neue Buchung** – mit der Kategorie aus der Regel, und wenn keine
|
||
greift, ohne. Genau dafür ist der Schalter da: Der Umsatz soll im Kontoblatt stehen, die
|
||
Kategorie wird dort nachgetragen.
|
||
* Umsätze **ohne zugeordnetes Konto** bleiben offen; ohne Konto gibt es kein Kontoblatt, in
|
||
das sie gehörten.
|
||
|
||
Der Lauf läuft **einmal am Ende**, nicht je Konto: So sieht er alle Umsätze auf einmal und
|
||
kann jeden mit der Buchung verbinden, die zu ihm passt. Ein einzelner Umsatz bricht ihn
|
||
nicht ab – ein abgeschlossener Monat oder ein doppelter Fingerabdruck betrifft nur ihn, er
|
||
bleibt offen, und die Meldung nennt die ersten Fälle.
|
||
|
||
Wo dabei noch eine Kategorie fehlt, zeigt die **Kontenübersicht**: Konten mit
|
||
unkategorisierten Buchungen aus dem laufenden Jahr stehen dort fett.
|
||
|
||
### Der Abgleichdialog
|
||
|
||
Abgeschaltet – oder für alles, was liegen geblieben ist – bleibt es beim Abgleich von Hand.
|
||
Die Vorschlagsliste (*Umsätze abgleichen*) kennt drei Wege:
|
||
|
||
| Vorschlag | Wann | Wirkung |
|
||
|---|---|---|
|
||
| **Verbinden** | Es gibt eine erfasste Buchung mit gleichem Konto, gleichem Betrag und nahem Datum | Der Umsatz wird mit ihr verknüpft, die Buchung gilt als abgeglichen |
|
||
| **Als neue Buchung** | Keine passende Buchung vorhanden | Neue Buchung mit Quelle *fints*, Kennzeichen abgeglichen |
|
||
| **Verwerfen** | Der Umsatz soll nicht ins Kontoblatt | Bleibt als „verworfen“ liegen und kommt nicht wieder |
|
||
|
||
Wie weit Buchungs- und Banktag auseinanderliegen dürfen, steht als *Toleranz beim Abgleich*
|
||
in den Einstellungen (Vorgabe fünf Tage). Eine Buchung, die schon mit einem anderen Umsatz
|
||
verbunden ist, wird nie ein zweites Mal vorgeschlagen.
|
||
|
||
Dass derselbe Umsatz bei überlappenden Abrufzeiträumen nur einmal hereinkommt, sichert
|
||
`extern_id` – ein Fingerabdruck aus Bankkonto, Datum, Betrag, Verwendungszweck und
|
||
Empfänger. Das Bankkonto steckt als `bank_konto_id` darin, nicht als IBAN: Welche Kennung
|
||
eine Bank liefert, wechselt von Dialog zu Dialog – die Comdirect etwa nennt die IBAN beim
|
||
Kontenabruf, beim Umsatzabruf aber nicht. Hinge der Fingerabdruck daran, käme derselbe
|
||
Umsatz ein zweites Mal herein. Aus demselben Grund findet ein Bankkonto sich über seine
|
||
**Kontonummer** wieder; eine später nachgelieferte IBAN wird am vorhandenen Eintrag
|
||
nachgetragen, statt einen zweiten anzulegen.
|
||
|
||
Verliert ein Umsatz seine Buchung – jemand löscht sie im Kontoblatt –, kehrt er in die
|
||
Vorschlagsliste zurück. Bliebe er auf *übernommen* stehen, wäre er nirgends mehr zu sehen:
|
||
nicht im Kontoblatt, nicht in der Liste, und beim nächsten Abruf als schon vorhanden
|
||
übersprungen. Verwaiste Umsätze aus gelöschten Konten fängt derselbe Mechanismus vor jedem
|
||
Abruf und vor jedem Öffnen der Liste ab.
|
||
|
||
**Alle eindeutigen übernehmen** erledigt in einem Zug, was ohne Rückfrage geht: verbinden,
|
||
wo eine Buchung passt, und übernehmen, wo eine Regel greift. Umsätze ohne Regel bleiben hier
|
||
offen – anders als beim automatischen Lauf. Wer den Dialog öffnet, will die Zuordnung selbst
|
||
treffen; eine Buchung ohne Kategorie wäre ihm nur ungefragt aufgehalst.
|
||
|
||
Nichts verschwindet von allein: Ein Umsatz, der weder übernommen noch verworfen wird, bleibt
|
||
offen und steht beim nächsten Öffnen wieder da. Das Cockpit zählt die offenen unter *zum
|
||
Abgleich*.
|
||
|
||
### Umsätze löschen
|
||
|
||
Jeder Reiter der Liste – *offen*, *übernommen*, *verworfen* – hat dieselbe Mehrfachauswahl
|
||
wie ein Kontoblatt: ein Ankreuzfeld vor jedem Umsatz, eines in der Titelleiste für alle
|
||
angezeigten und darüber der Knopf **Auswahl löschen** (`POST /banking/umsaetze/loeschen`,
|
||
`Bankumsatz::mehrereLoeschen()`). Der Knopf nennt die Anzahl und bleibt ohne Auswahl
|
||
gesperrt; ohne JavaScript weist der Controller eine leere Auswahl ab.
|
||
|
||
Gelöscht wird allein die abgerufene Zeile. Eine daraus entstandene Buchung gehört ins
|
||
Kontoblatt und bleibt dort stehen – wer sie loswerden will, löscht sie dort, und dann kehrt
|
||
ihr Umsatz ohnehin in die Vorschlagsliste zurück. Mit dem Umsatz geht aber sein
|
||
Fingerabdruck: Was innerhalb des nächsten Abrufzeitraums liegt, kommt danach wieder herein –
|
||
bei einem verworfenen Umsatz also erneut als offen.
|
||
|
||
## Regeln
|
||
|
||
Eine Regel prüft ein Muster gegen Empfänger und/oder Verwendungszweck:
|
||
|
||
* **Vergleich**: enthält, beginnt mit, endet auf, regulärer Ausdruck. Groß- und
|
||
Kleinschreibung spielt nie eine Rolle – Banken schreiben denselben Empfänger mal so, mal
|
||
so.
|
||
* **Betragsgrenzen** rechnen mit Vorzeichen: „von −60 bis −40“ trifft Lastschriften
|
||
zwischen 40 und 60 Euro.
|
||
* **Wirkung**: Kategorie, Klasse, Gegenkonto (macht daraus eine Umbuchung) oder eine
|
||
**Splitbuchung**.
|
||
|
||
### Splitbuchung in einer Regel
|
||
|
||
Unter jeder Regel klappt *Splitbuchung* die Zeilen auf: je Zeile eine Kategorie, wahlweise
|
||
Klasse und Notiz, dazu ein Anteil in Prozent. Greift die Regel, entsteht keine Buchung mit
|
||
einer Kategorie, sondern eine Splitbuchung mit diesen Zeilen.
|
||
|
||
Die Anteile gelten **zueinander**, nicht als feste Beträge – darin liegt der ganze Zweck:
|
||
Dieselbe Regel trifft bei jeder Rechnungshöhe dasselbe Verhältnis. Eine Nebenkostenabrechnung
|
||
über 300 € wird bei 60 zu 30 in 200 € und 100 € geteilt, eine über 450 € in 300 € und 150 €.
|
||
Ob dort 60 und 30 steht oder 2 und 1, ändert nichts.
|
||
|
||
Dass die Anteile aufeinander bezogen werden und nicht absolut gelten, ist Absicht: Sonst
|
||
bekäme die letzte Zeile als Rest plötzlich 40 statt der eingetragenen 30 Prozent, und der
|
||
Fehler wäre in der fertigen Buchung nicht mehr zu sehen. Die letzte Zeile bekommt weiterhin
|
||
den Rundungsrest, damit die Summe auf den Cent zum Buchungsbetrag passt – bei einem Drittel
|
||
zu zwei Dritteln ginge sonst ein Cent verloren, und die Buchung ließe sich nicht speichern.
|
||
|
||
Eine Zeile ohne Kategorie oder ohne Anteil fällt weg; ohne jede Splitzeile bucht die Regel
|
||
wie zuvor auf ihre Kategorie. Im Abgleichdialog zeigt der Vorschlag die errechneten Zeilen
|
||
mit ihren Beträgen an und übernimmt sie unverändert – geändert wird eine Aufteilung im
|
||
Kontoblatt, wo die ganze Buchung samt Summenprüfung vorliegt.
|
||
|
||
Die **erste passende Regel gewinnt**, sortiert nach Priorität (kleinere Zahl zuerst). Kein
|
||
Sammeln über mehrere Regeln: Wenn zwei verschiedene Kategorien setzen wollten, wäre am Ende
|
||
nicht mehr nachvollziehbar, warum die eine dasteht.
|
||
|
||
Reguläre Ausdrücke werden beim Speichern geprüft – ein kaputtes Muster brächte sonst jeden
|
||
Abruf zum Stehen. Eingegeben wird ohne Trennzeichen: `Miete|Nebenkosten`, nicht `/Miete/`.
|
||
|
||
Der Zähler *Treffer* zeigt, welche Regeln im Alltag überhaupt greifen.
|
||
|
||
## Protokoll und Diagnose
|
||
|
||
Jeder Abruf schreibt eine Zeile nach `bank_abrufe` mit Status, Zahlen und Verlauf – ohne das
|
||
ist ein fehlgeschlagener Abruf nicht mehr zu rekonstruieren, denn FinTS-Meldungen der Banken
|
||
sind selten selbsterklärend. Läufe, die länger als zwei Stunden auf eine TAN warten, gelten
|
||
als abgebrochen.
|
||
|
||
Bei eingeschaltetem Debug-Log (Einstellungen → Diagnose) landen zusätzlich die
|
||
FinTS-Dialogschritte in `data/logs/` – über `Services\DebugLogAdapter`, der den PSR-3-Logger
|
||
der Bibliothek an das eigene Log hängt. PIN und TAN maskieren beide Seiten.
|
||
|
||
## Tests
|
||
|
||
`tests/16_BankingTest.php` prüft alles um den Dialog herum: Verschlüsselung (Kennung, kein
|
||
Klartext, wechselnder Geheimtext, kein Verschachteln), PIN bleibt beim Speichern erhalten,
|
||
Abrufzeiträume samt Stichtag (gespeicherte Vorgabe, einmalige Angabe geht ihr vor und lässt
|
||
die Vorgabe stehen, Zukunft wird auf heute gesetzt, unlesbare Datumsangaben werden
|
||
abgewiesen), Dublettenerkennung, Regeln nach Priorität und Feld, Splitschema mit
|
||
Restausgleich, Abweisung ungültiger Ausdrücke, Abgleich gegen vorhandene Buchungen samt
|
||
Toleranz sowie Übernehmen, Verwerfen und Zurücksetzen.
|
||
|
||
Zur Verfahrenswahl geprüft: Kennung, Name und verschlüsseltes Medium werden gespeichert,
|
||
unbekannte Kennungen und Medien abgewiesen, ein Verfahren ohne Medienzwang bekommt keines
|
||
angehängt, und das Speichern des Zugangsformulars lässt die Wahl stehen. Die Abfrage bei der
|
||
Bank steckt hinter derselben Grenze wie der Dialog – geprüft wird, was mit ihrem Ergebnis
|
||
geschieht.
|
||
|
||
`tests/28_UmsaetzeLoeschenTest.php` prüft das Sammellöschen: Es trifft genau die
|
||
ausgewählten Zeilen, eine schon erzeugte Buchung überlebt es, jeder Status lässt sich
|
||
löschen, leere und unbekannte Auswahl bleiben folgenlos, eine doppelt gesendete Nummer zählt
|
||
einmal – und die gerenderte Ansicht trägt in jedem Reiter Ankreuzfelder, Titelleiste und
|
||
Knopf, während die leere Liste keinen Knopf zeigt.
|
||
|
||
`tests/19_KontenzuordnungTest.php` nimmt sich den Weg vor, den ein Umsatz nimmt, wenn die
|
||
Zuordnung erst nach dem Abruf getroffen wird: Offene Umsätze ziehen mit, übernommene nicht,
|
||
und nur das Bankkonto, um das es geht. Dazu das automatische Buchen – mit Regel, ohne Regel,
|
||
verbinden statt doppeln, Umsätze ohne Konto bleiben liegen, abgeschaltet passiert nichts –
|
||
sowie die Zählung der unkategorisierten Buchungen je Konto (Jahresgrenze, Umbuchung und
|
||
Splitbuchung zählen nicht mit) und ihr Weg in die Kontenübersicht.
|
||
|
||
Der FinTS-Dialog selbst braucht eine echte Bankverbindung und lässt sich nicht nachstellen.
|
||
Geprüft wurde er gegen den Fehlerpfad: Eine unerreichbare Adresse bricht sauber ab,
|
||
protokolliert und meldet verständlich.
|