mandantAnlegen(); $this->stammdaten(); $this->einWartenderAuftragIstKeinEntwurf(); $this->derAbbruchStehtImProtokoll(); $this->einNeuerSendeversuchLoestDieFreigabeAbUndProtokolliert(); $this->ohneWartendeFreigabePassiertBeimAbbruchNichts(); $this->einAuftragMitProtokollWirdNichtGeloescht(); } private function stammdaten(): void { $this->giro = Konto::anlegen([ 'name' => 'Freigabe-Giro', 'kontoart' => 'bank', 'anfangssaldo' => 0, 'aktiv' => 1, ]); $this->zugang = Bankzugang::anlegen([ 'name' => 'Freigabe-Bank', 'bankname' => 'Freigabe-Bank AG', 'blz' => '12030000', 'fints_url' => 'https://banking.example.org/fints', 'benutzerkennung' => 'testkunde', 'pin' => 'geheim1234', 'abrufzeitraum' => 'letzter_abruf', 'aktiv' => 1, ]); // Auf dem eigenen Rechner lauscht niemand – ein echter Versuch scheitert sofort. Database::execute( 'UPDATE bank_zugaenge SET fints_url = ? WHERE id = ?', ['http://127.0.0.1:9/fints', $this->zugang] ); } private function auftrag(): int { return Zahlungsauftrag::anlegen([ 'konto_id' => $this->giro, 'zugang_id' => $this->zugang, 'empfaenger' => 'Finanzamt Musterstadt', 'iban' => 'DE02120300000000202051', 'betrag' => 4715.67, ]); } /** Legt einen Dialog in die Sitzung, der auf die TAN zum Auftrag wartet. */ private function tanAngefordert(int $auftragId): void { Bankprotokoll::speichern($this->zugang, 'ueberweisung', 'offen', 'Die Bank verlangt eine Freigabe.', '> HKTAN', $auftragId); Zahlungsauftrag::alsWartendVermerken($auftragId, 'Die Bank verlangt eine Freigabe.'); Auth::startSession(); $_SESSION[FinTsUeberweisung::SITZUNG] = [ 'schritt' => 'tan', 'zugang_id' => $this->zugang, 'auftrag_id' => $auftragId, 'tan' => ['bezeichnung' => 'Überweisung'], ]; } private function einWartenderAuftragIstKeinEntwurf(): void { $auftragId = $this->auftrag(); $this->tanAngefordert($auftragId); $this->assertEquals('freigabe', Zahlungsauftrag::find($auftragId)['status'], 'Nach der TAN-Anforderung wartet der Auftrag auf Freigabe'); $this->assertEquals($auftragId, FinTsUeberweisung::wartenderAuftrag(), 'Die Sitzung kennt den wartenden Auftrag'); $this->assertEquals(1, count(Zahlungsauftrag::alle('freigabe')), 'Der Reiter „wartet auf Freigabe“ zeigt ihn'); $this->assertGreaterThan(0, Zahlungsauftrag::anzahlOffen(), 'Er zählt zu den offenen Aufträgen'); FinTsUeberweisung::abbrechen(); } private function derAbbruchStehtImProtokoll(): void { $auftragId = $this->auftrag(); $this->tanAngefordert($auftragId); FinTsUeberweisung::abbrechen(); $this->assertNull(FinTsUeberweisung::wartenderAuftrag(), 'Nach dem Abbruch wartet nichts mehr'); $auftrag = Zahlungsauftrag::find($auftragId); $this->assertEquals('fehler', $auftrag['status'], 'Der abgebrochene Auftrag gilt als nicht ausgeführt'); $this->assert(str_contains((string)$auftrag['meldung'], 'keine TAN eingereicht'), 'Die Meldung nennt die fehlende TAN'); $eintraege = Bankprotokoll::fuerAuftrag($auftragId); $this->assertEquals(2, count($eintraege), 'TAN-Anforderung und Abbruch stehen im Protokoll'); $this->assertEquals('abgebrochen', $eintraege[1]['status'], 'Der Abbruch trägt den Status „abgebrochen“'); $this->assert(str_contains((string)$eintraege[1]['protokoll'], 'TAN eingeben'), 'Das Protokoll nennt den offenen Schritt'); $this->assert(str_contains(Bankprotokoll::nachweis($auftrag), 'Freigabe abgebrochen'), 'Der Nachweis zur Überweisung zeigt den Abbruch'); } private function einNeuerSendeversuchLoestDieFreigabeAbUndProtokolliert(): void { $alt = $this->auftrag(); $this->tanAngefordert($alt); $neu = $this->auftrag(); $ergebnis = FinTsUeberweisung::senden($neu); $this->assertEquals('fehler', $ergebnis['status'], 'Ohne erreichbare Bank scheitert der neue Versuch'); $eintraege = Bankprotokoll::fuerAuftrag($alt); $this->assertEquals('abgebrochen', end($eintraege)['status'], 'Die verdrängte Freigabe steht als abgebrochen im Protokoll'); $this->assert( str_contains((string)end($eintraege)['meldung'], 'neuen Sendeversuch'), 'Das Protokoll nennt den Grund' ); $this->assertEquals('fehler', Zahlungsauftrag::find($alt)['status'], 'Der verdrängte Auftrag wartet nicht mehr'); } private function ohneWartendeFreigabePassiertBeimAbbruchNichts(): void { $vorher = (int)Database::fetchValue('SELECT COUNT(*) FROM bank_protokolle', [], 0); FinTsUeberweisung::abbrechen(); $nachher = (int)Database::fetchValue('SELECT COUNT(*) FROM bank_protokolle', [], 0); $this->assertEquals($vorher, $nachher, 'Ohne wartende Freigabe entsteht kein Protokolleintrag'); } private function einAuftragMitProtokollWirdNichtGeloescht(): void { $mitProtokoll = $this->auftrag(); $this->tanAngefordert($mitProtokoll); FinTsUeberweisung::abbrechen(); $abgelehnt = false; try { Zahlungsauftrag::loeschen($mitProtokoll); } catch (\RuntimeException) { $abgelehnt = true; } $this->assert($abgelehnt, 'Ein Auftrag mit Übermittlungsprotokoll lässt sich nicht löschen'); $this->assert(Zahlungsauftrag::find($mitProtokoll) !== null, 'Er bleibt als Beleg stehen'); Zahlungsauftrag::stornieren($mitProtokoll); $this->assertEquals('storniert', Zahlungsauftrag::find($mitProtokoll)['status'], 'Stornieren geht weiterhin'); $ohneProtokoll = $this->auftrag(); Zahlungsauftrag::loeschen($ohneProtokoll); $this->assertNull(Zahlungsauftrag::find($ohneProtokoll), 'Ein nie gesendeter Entwurf lässt sich weiterhin löschen'); } }