Finanzmanager/tests/37_UeberweisungBankZuordnungTest.php
fds2610-bot e7c965c219 Fix #58: Auftragskonto über die Kontenzuordnung finden
Beim Senden suchte FinTsUeberweisung unter den Konten, die die Bank meldet,
nach der IBAN aus dem Konto selbst. Die ist in der Kontenverwaltung als
optional ausgewiesen und bleibt meist leer – dann scheiterte die Überweisung
an "Die Bank führt kein Konto mit der IBAN des Auftragskontos", obwohl der
Umsatzabruf für dasselbe Konto längst lief.

Maßgeblich ist jetzt dieselbe Zuordnung wie beim Abruf: die Zeile in
bank_konten, die dem Konto zugewiesen ist. Dort steht die IBAN, die die Bank
selbst gemeldet hat, und die Kontonummer für die Häuser, die keine IBAN
liefern. Die IBAN am Konto bleibt als Weg bestehen, ebenso der Fall, dass die
Bank nur ein einziges Konto führt.

passendesKonto() ist dafür public static geworden und nimmt den Zugang als
Parameter – so lässt sich die Wahl mit echten SEPAAccount-Objekten prüfen,
ohne eine Bank zu brauchen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013iURiWYZJQYVGQNXFFGyWu
2026-09-01 20:08:54 +02:00

213 lines
7.6 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
use Fhp\Model\SEPAAccount;
use Finanzmanager\Models\Bankzugang;
use Finanzmanager\Models\Konto;
use Finanzmanager\Models\Zahlungsauftrag;
use Finanzmanager\Services\FinTsUeberweisung;
/**
* Automatische Bankauswahl im Zahlungsverkehr.
*
* Beim Erfassen einer Überweisung wählt der Nutzer nur noch das Auftragskonto;
* die Bankverbindung ergibt sich aus `Bankzugang::zugangJeKonto()`. Konten ohne
* konfigurierte Bank tauchen dort nicht auf und dürfen deshalb auch nicht als
* Auftragskonto zur Wahl stehen.
*/
class UeberweisungBankZuordnungTest extends TestCase
{
protected string $testName = 'Test 37: Überweisung – automatische Bankzuordnung';
private int $giro = 0;
private int $ohneBank = 0;
private int $zugang = 0;
public function run(): void
{
$this->mandantAnlegen();
$this->stammdaten();
$this->kontoOhneBankStehtNichtInDerZuordnung();
$this->zugeordnetesKontoStehtInDerZuordnung();
$this->einDeaktivierterZugangFaelltHeraus();
$this->derAuftragUebernimmtDieBankAutomatisch();
$this->dasAuftragskontoFindetSichUeberDieZuordnung();
$this->ohneZuordnungGreiftDieIbanAmKonto();
}
/**
* Beim Senden muss unter den Konten, die die Bank meldet, das richtige
* gefunden werden. Maßgeblich ist dieselbe Zuordnung wie beim Umsatzabruf –
* die IBAN am Konto selbst ist optional und bleibt meist leer.
*/
private function dasAuftragskontoFindetSichUeberDieZuordnung(): void
{
$gemeldet = [
$this->sepaKonto('DE29120300000000202052', '202052'),
$this->sepaKonto('DE02120300000000202051', '202051'),
];
// Das Girokonto ist über `bank_konten` zugeordnet (siehe oben), hat aber
// selbst keine IBAN.
$treffer = FinTsUeberweisung::passendesKonto($gemeldet, $this->giro, $this->zugang);
$this->assertEquals(
'DE29120300000000202052',
$treffer?->getIban(),
'Das zugeordnete Bankkonto wird gefunden, obwohl am Konto keine IBAN steht'
);
// Ein Konto ohne Zuordnung darf keines der gemeldeten erwischen.
$this->assertNull(
FinTsUeberweisung::passendesKonto($gemeldet, $this->ohneBank, $this->zugang),
'Ein nicht zugeordnetes Konto trifft auf nichts'
);
// Meldet die Bank keine IBAN, bleibt die Kontonummer.
$ohneIban = [$this->sepaKonto('', '202052'), $this->sepaKonto('', '999999')];
$this->assertEquals(
'202052',
FinTsUeberweisung::passendesKonto($ohneIban, $this->giro, $this->zugang)?->getAccountNumber(),
'Ohne IBAN führt die Kontonummer zum Ziel'
);
}
/**
* Der bisherige Weg bleibt bestehen: Wer die IBAN am Konto gepflegt hat,
* kommt auch ohne Zuordnung zum Ziel – und bei genau einem Konto bei der
* Bank ist die Sache ohnehin eindeutig.
*/
private function ohneZuordnungGreiftDieIbanAmKonto(): void
{
$eigenes = Konto::anlegen([
'name' => 'Zahlungsverkehr-Giro mit IBAN', 'kontoart' => 'bank',
'iban' => 'DE02120300000000202051', 'anfangssaldo' => 0, 'aktiv' => 1,
]);
$gemeldet = [
$this->sepaKonto('DE29120300000000202052', '202052'),
$this->sepaKonto('DE02120300000000202051', '202051'),
];
$this->assertEquals(
'DE02120300000000202051',
FinTsUeberweisung::passendesKonto($gemeldet, $eigenes, $this->zugang)?->getIban(),
'Die IBAN am Konto genügt auch ohne Zuordnung'
);
$einzelnes = [$this->sepaKonto('DE29120300000000202052', '202052')];
$this->assertNotEmpty(
FinTsUeberweisung::passendesKonto($einzelnes, $this->ohneBank, $this->zugang),
'Führt die Bank nur ein Konto, ist es eindeutig'
);
}
private function sepaKonto(string $iban, string $kontonummer): SEPAAccount
{
$konto = new SEPAAccount();
$konto->setIban($iban);
$konto->setBic('GENODEF1TES');
$konto->setAccountNumber($kontonummer);
$konto->setBlz('12030000');
return $konto;
}
private function stammdaten(): void
{
$this->giro = Konto::anlegen([
'name' => 'Zahlungsverkehr-Giro', 'kontoart' => 'bank', 'anfangssaldo' => 0, 'aktiv' => 1,
]);
$this->ohneBank = Konto::anlegen([
'name' => 'Zahlungsverkehr-Sparbuch', 'kontoart' => 'sparkonto', 'anfangssaldo' => 0, 'aktiv' => 1,
]);
$this->zugang = Bankzugang::anlegen([
'name' => 'Zahlungsverkehr-Bank',
'bankname' => 'Zahlungsverkehr-Bank AG',
'blz' => '12030000',
'fints_url' => 'https://banking.example.org/fints',
'benutzerkennung' => 'testkunde',
'pin' => 'geheim1234',
'abrufzeitraum' => 'letzter_abruf',
'aktiv' => 1,
]);
}
private function kontoOhneBankStehtNichtInDerZuordnung(): void
{
$this->assert(
!array_key_exists($this->ohneBank, Bankzugang::zugangJeKonto()),
'Ein Konto ohne Bankverbindung fehlt in der Zuordnung'
);
}
private function zugeordnetesKontoStehtInDerZuordnung(): void
{
$bankkonto = Bankzugang::findeOderLegeKontoAn(
$this->zugang,
'DE29120300000000202052',
'202052',
'Girokonto'
);
Bankzugang::ordneKontoZu((int)$bankkonto['id'], $this->giro);
$this->assertEquals(
$this->zugang,
Bankzugang::zugangJeKonto()[$this->giro] ?? null,
'Das zugeordnete Konto verweist auf seine Bank'
);
}
private function einDeaktivierterZugangFaelltHeraus(): void
{
Bankzugang::speichern($this->zugang, [
'name' => 'Zahlungsverkehr-Bank',
'bankname' => 'Zahlungsverkehr-Bank AG',
'blz' => '12030000',
'fints_url' => 'https://banking.example.org/fints',
'benutzerkennung' => 'testkunde',
'abrufzeitraum' => 'letzter_abruf',
'aktiv' => 0,
]);
$this->assert(
!array_key_exists($this->giro, Bankzugang::zugangJeKonto()),
'Ein deaktivierter Zugang taugt nicht als automatische Auswahl'
);
Bankzugang::speichern($this->zugang, [
'name' => 'Zahlungsverkehr-Bank',
'bankname' => 'Zahlungsverkehr-Bank AG',
'blz' => '12030000',
'fints_url' => 'https://banking.example.org/fints',
'benutzerkennung' => 'testkunde',
'abrufzeitraum' => 'letzter_abruf',
'aktiv' => 1,
]);
}
/**
* Genau das, was der Controller beim Anlegen tut: die Bank kommt aus der
* Zuordnung, nicht aus einer eigenen Formularauswahl.
*/
private function derAuftragUebernimmtDieBankAutomatisch(): void
{
$zugangId = Bankzugang::zugangJeKonto()[$this->giro] ?? null;
$auftragId = Zahlungsauftrag::anlegen([
'konto_id' => $this->giro,
'zugang_id' => $zugangId,
'empfaenger' => 'Beispielempfänger',
'iban' => 'DE02120300000000202051',
'betrag' => 42.50,
]);
$this->assertEquals(
$this->zugang,
(int)Zahlungsauftrag::find($auftragId)['zugang_id'],
'Der Auftrag trägt die automatisch ermittelte Bank'
);
}
}