Der gesamte FinTS-Dialog (gesendete und empfangene Meldungen) wird je Durchlauf in bank_protokolle gespeichert, im Erfolgs- wie im Fehlerfall. Im Zahlungsverkehr öffnet der Status das Protokoll der Überweisung als modalen Dialog; unter Online-Banking › Protokoll gibt es das Gesamtprotokoll mit Filter nach Bank, Datum und Freitext. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
148 lines
6.2 KiB
PHP
148 lines
6.2 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
use Finanzmanager\Database;
|
||
use Finanzmanager\Models\Bankprotokoll;
|
||
use Finanzmanager\Models\Bankzugang;
|
||
use Finanzmanager\Models\Konto;
|
||
use Finanzmanager\Models\Zahlungsauftrag;
|
||
use Finanzmanager\Services\BankProtokollLogger;
|
||
use Finanzmanager\Services\FinTsUeberweisung;
|
||
|
||
/**
|
||
* Übermittlungsprotokoll: Nachweis gegenüber der Bank.
|
||
*
|
||
* Geprüft wird, dass der Dialog mit der Bank gespeichert wird – im Erfolgs- wie
|
||
* im Fehlerfall –, dass er zur Überweisung wiederzufinden ist und dass das
|
||
* Gesamtprotokoll nach Bank, Datum und Freitext filtert. Für den Fehlerfall geht
|
||
* die Überweisung an eine Adresse auf dem eigenen Rechner, auf der niemand
|
||
* lauscht: Es verlässt nichts den Container.
|
||
*/
|
||
class UebermittlungsprotokollTest extends TestCase
|
||
{
|
||
protected string $testName = 'Test 42: Übermittlungsprotokoll';
|
||
|
||
private int $giro = 0;
|
||
private int $zugang = 0;
|
||
private int $zweiterZugang = 0;
|
||
|
||
public function run(): void
|
||
{
|
||
$this->mandantAnlegen();
|
||
$this->stammdaten();
|
||
|
||
$this->derMitschnittSammeltGesendetUndEmpfangen();
|
||
$this->einProtokollHaengtAmAuftrag();
|
||
$this->einFehlgeschlagenerVersuchHinterlaesstEinProtokoll();
|
||
$this->dasGesamtprotokollFiltert();
|
||
}
|
||
|
||
private function stammdaten(): void
|
||
{
|
||
$this->giro = Konto::anlegen([
|
||
'name' => 'Protokoll-Giro', 'kontoart' => 'bank', 'anfangssaldo' => 0, 'aktiv' => 1,
|
||
]);
|
||
|
||
$zugang = static fn(string $name): array => [
|
||
'name' => $name,
|
||
'bankname' => $name . ' AG',
|
||
'blz' => '12030000',
|
||
'fints_url' => 'https://banking.example.org/fints',
|
||
'benutzerkennung' => 'testkunde',
|
||
'pin' => 'geheim1234',
|
||
'abrufzeitraum' => 'letzter_abruf',
|
||
'aktiv' => 1,
|
||
];
|
||
|
||
$this->zugang = Bankzugang::anlegen($zugang('Protokoll-Bank'));
|
||
$this->zweiterZugang = Bankzugang::anlegen($zugang('Andere-Bank'));
|
||
}
|
||
|
||
private function auftrag(): int
|
||
{
|
||
return Zahlungsauftrag::anlegen([
|
||
'konto_id' => $this->giro,
|
||
'zugang_id' => $this->zugang,
|
||
'empfaenger' => 'Finanzamt Musterstadt',
|
||
'iban' => 'DE02120300000000202051',
|
||
'betrag' => 42.5,
|
||
]);
|
||
}
|
||
|
||
private function derMitschnittSammeltGesendetUndEmpfangen(): void
|
||
{
|
||
$logger = new BankProtokollLogger();
|
||
$logger->debug('> HNHBK:1:3+000000000123');
|
||
$logger->debug("< HNHBK:1:3+000000000456 Ümlaut\xE4");
|
||
$logger->vermerke('Ergebnis: angenommen');
|
||
|
||
$text = $logger->text();
|
||
$this->assert(str_contains($text, '> HNHBK:1:3+000000000123'), 'Die gesendete Meldung steht im Mitschnitt');
|
||
$this->assert(str_contains($text, '< HNHBK:1:3+000000000456'), 'Die empfangene Meldung steht im Mitschnitt');
|
||
$this->assert(str_contains($text, 'Ergebnis: angenommen'), 'Auch die eigenen Vermerke stehen darin');
|
||
$this->assert(mb_check_encoding($text, 'UTF-8'), 'Der Mitschnitt ist gültiges UTF-8, auch nach Latin-1');
|
||
}
|
||
|
||
private function einProtokollHaengtAmAuftrag(): void
|
||
{
|
||
$auftragId = $this->auftrag();
|
||
|
||
Bankprotokoll::speichern($this->zugang, 'ueberweisung', 'offen', 'TAN nötig', '> Anfrage', $auftragId);
|
||
Bankprotokoll::speichern($this->zugang, 'ueberweisung', 'erfolg', 'angenommen', '< Antwort', $auftragId);
|
||
|
||
$eintraege = Bankprotokoll::fuerAuftrag($auftragId);
|
||
$this->assertEquals(2, count($eintraege), 'Beide Durchläufe gehören zur Überweisung');
|
||
$this->assertEquals('offen', $eintraege[0]['status'], 'Der älteste Durchlauf steht zuerst');
|
||
$this->assertEquals('< Antwort', $eintraege[1]['protokoll'], 'Das Protokoll bleibt vollständig erhalten');
|
||
|
||
$liste = Zahlungsauftrag::alle();
|
||
$this->assertEquals(2, (int)$liste[0]['protokolle'], 'Die Liste zählt die Protokolle je Überweisung');
|
||
}
|
||
|
||
private function einFehlgeschlagenerVersuchHinterlaesstEinProtokoll(): void
|
||
{
|
||
// Auf dem eigenen Rechner lauscht niemand – der Versuch scheitert sofort.
|
||
Database::execute(
|
||
'UPDATE bank_zugaenge SET fints_url = ? WHERE id = ?',
|
||
['http://127.0.0.1:9/fints', $this->zugang]
|
||
);
|
||
|
||
$auftragId = $this->auftrag();
|
||
$ergebnis = FinTsUeberweisung::senden($auftragId);
|
||
FinTsUeberweisung::abbrechen();
|
||
|
||
$this->assertEquals('fehler', $ergebnis['status'], 'Ohne erreichbare Bank scheitert das Senden');
|
||
|
||
$eintraege = Bankprotokoll::fuerAuftrag($auftragId);
|
||
$this->assertEquals(1, count($eintraege), 'Auch der Fehlerfall wird protokolliert');
|
||
$this->assertEquals('fehler', $eintraege[0]['status'], 'Der Eintrag trägt den Status „fehler“');
|
||
$this->assert(
|
||
str_contains((string)$eintraege[0]['protokoll'], 'Finanzamt Musterstadt'),
|
||
'Das Protokoll nennt die Überweisung'
|
||
);
|
||
$this->assert(
|
||
!str_contains((string)$eintraege[0]['protokoll'], 'geheim1234'),
|
||
'Die PIN steht nicht im Protokoll'
|
||
);
|
||
}
|
||
|
||
private function dasGesamtprotokollFiltert(): void
|
||
{
|
||
Bankprotokoll::speichern($this->zweiterZugang, 'abruf', 'erfolg', 'Umsätze geholt', 'Kontoauszug Nummer 77');
|
||
|
||
$alle = Bankprotokoll::suchen();
|
||
$this->assertGreaterThan(3, count($alle), 'Das Gesamtprotokoll führt alle Durchläufe');
|
||
|
||
$andere = Bankprotokoll::suchen(['zugang_id' => $this->zweiterZugang]);
|
||
$this->assertEquals(1, count($andere), 'Der Bankfilter zeigt nur diese Bank');
|
||
|
||
$treffer = Bankprotokoll::suchen(['text' => 'Nummer 77']);
|
||
$this->assertEquals(1, count($treffer), 'Die Freitextsuche findet den Wortlaut der Meldungen');
|
||
|
||
$heute = date('Y-m-d');
|
||
$this->assertGreaterThan(0, count(Bankprotokoll::suchen(['von' => $heute, 'bis' => $heute])), 'Heute ist im Zeitraum');
|
||
$this->assertEquals(0, count(Bankprotokoll::suchen(['bis' => '2000-01-01'])), 'Ein Zeitraum in der Vergangenheit ist leer');
|
||
$this->assertEquals(0, count(Bankprotokoll::suchen(['text' => '100%'])), 'Sonderzeichen der Suche sind wörtlich gemeint');
|
||
}
|
||
}
|