diff --git a/Changes.md b/Changes.md index 082cb8a..e8707ef 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,12 @@ # Änderungen +## Echtzeitüberweisung: offener Status ist kein Fehler (01.10.2026) + +- Quittiert die Bank die TAN mit 3045 („noch keine Rückmeldung zum Status“), meldete php-fints „Bank did not confirm SEPATransfer execution“, obwohl der Auftrag bei der Bank liegt. Jetzt gilt der Auftrag als gesendet (gebucht), die Meldung weist darauf hin, den Kontoumsatz zu prüfen und nicht erneut zu senden. +- `Services/FinTsEchtzeitUeberweisung` erweitert `SendSEPARealtimeTransfer` um dieses Merkmal. +- Eine Statusabfrage bei der Bank (Nachfragen) ist noch offen: php-fints kennt dafür weder `HKIPS` noch `HKTAN` S für Nicht-Decoupled-Verfahren. +- Test: `tests/54_EchtzeitStatusOffenTest.php` (Antwort aus einem echten Lauf). + ## Überweisung erfassen: QR-Code einlesen (01.10.2026) - Neben „Empfänger“ steht ein Bereich, auf den sich ein Bild (z. B. Rechnungs-Screenshot) ziehen lässt. Ein Klick öffnet einen Dialog zum Einfügen aus der Zwischenablage (Strg+V) oder zur Dateiauswahl. diff --git a/src/Services/FinTsEchtzeitUeberweisung.php b/src/Services/FinTsEchtzeitUeberweisung.php new file mode 100644 index 0000000..a06b72f --- /dev/null +++ b/src/Services/FinTsEchtzeitUeberweisung.php @@ -0,0 +1,42 @@ +statusOffen; + } + + public function processResponse(Message $response) + { + try { + parent::processResponse($response); + } catch (UnexpectedResponseException $e) { + if ($response->findRueckmeldungen(self::STATUS_OFFEN) === []) { + throw $e; + } + $this->statusOffen = true; + } + } +} diff --git a/src/Services/FinTsUeberweisung.php b/src/Services/FinTsUeberweisung.php index 939ac97..ec9289b 100644 --- a/src/Services/FinTsUeberweisung.php +++ b/src/Services/FinTsUeberweisung.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace Finanzmanager\Services; use Fhp\Action\GetSEPAAccounts; -use Fhp\Action\SendSEPARealtimeTransfer; use Fhp\Action\SendSEPATransfer; use Fhp\BaseAction; use Fhp\FinTs; @@ -343,7 +342,7 @@ class FinTsUeberweisung $xml = SepaXml::ueberweisung($this->auftragsdaten($auftrag, $sepa), $schema); $aktion = $echtzeit // Keine Umwandlung in eine gewöhnliche Überweisung – siehe oben. - ? SendSEPARealtimeTransfer::create($sepa, $xml, false) + ? FinTsEchtzeitUeberweisung::create($sepa, $xml, false) : SendSEPATransfer::create($sepa, $xml); try { @@ -468,7 +467,7 @@ class FinTsUeberweisung return $this->tanNoetig($fints, $aktion, 'Überweisung', 'senden'); } - return $this->angenommen($fints); + return $this->angenommen($fints, $aktion); } /** @@ -575,14 +574,21 @@ class FinTsUeberweisung } /** @return array */ - private function angenommen(FinTs $fints): array + private function angenommen(FinTs $fints, ?BaseAction $aktion = null): array { $auftrag = Zahlungsauftrag::find($this->auftragId); $echtzeit = $auftrag !== null && self::istEchtzeit($auftrag); + // Die Bank hat die TAN angenommen, den Ausgang aber noch nicht gemeldet. + // Der Auftrag gilt als gesendet – ein erneutes Senden wäre eine + // Doppelzahlung –, der Nutzer prüft den Umsatz. + $statusOffen = $aktion instanceof FinTsEchtzeitUeberweisung && $aktion->istStatusOffen(); + Zahlungsauftrag::alsGesendetVermerken( $this->auftragId, - $echtzeit ? 'Von der Bank als Echtzeitüberweisung angenommen.' : 'Von der Bank angenommen.' + $statusOffen + ? 'Die Bank hat die Echtzeitüberweisung entgegengenommen, den Ausgang aber noch nicht bestätigt.' + : ($echtzeit ? 'Von der Bank als Echtzeitüberweisung angenommen.' : 'Von der Bank angenommen.') ); self::vergessen(); @@ -592,6 +598,14 @@ class FinTsUeberweisung // Der Dialog ist zu Ende; ein Fehler beim Abmelden ändert nichts. } + if ($statusOffen) { + return [ + 'status' => 'erfolg', + 'meldung' => 'Die Bank hat die Echtzeitüberweisung entgegengenommen, den Ausgang aber noch nicht bestätigt. ' + . 'Bitte im Kontoumsatz prüfen, ob sie ausgeführt wurde, und nicht erneut senden.', + ]; + } + return [ 'status' => 'erfolg', 'meldung' => $echtzeit diff --git a/tests/54_EchtzeitStatusOffenTest.php b/tests/54_EchtzeitStatusOffenTest.php new file mode 100644 index 0000000..d35323a --- /dev/null +++ b/tests/54_EchtzeitStatusOffenTest.php @@ -0,0 +1,33 @@ +assert(!$aktion->istStatusOffen(), 'Ohne Antwort ist der Status nicht offen'); + + $aktion->processResponse($antwort); + $this->assert($aktion->istStatusOffen(), 'Rückmeldung 3045 wird als „Status offen“ erkannt, nicht als Fehler'); + } +}