diff --git a/Changes.md b/Changes.md index caf890d..e483945 100644 --- a/Changes.md +++ b/Changes.md @@ -1,5 +1,15 @@ # Änderungen +## Bekannte Umsätze bekommen IBAN und Bank der Gegenseite nachgetragen (29.09.2026) + +- Der Tooltip im Kontoblatt blieb bei älteren Umsätzen leer: IBAN und Bank der Gegenseite + werden erst seit dem 28.09. gespeichert, und ein erneuter Abruf übersprang bekannte Umsätze + ganz. Jetzt trägt `Bankumsatz::anlegen()` bei einem bekannten Umsatz nach, was ihm fehlt + (`Bankumsatz::ergaenzen()`), und gibt es an die verbundene Buchung weiter. Nur leere Felder + werden gefüllt, vorhandene bleiben. +- Auch `Bankumsatz::verbinden()` überträgt die Angaben auf eine von Hand erfasste Buchung. +- Neu: Test 49. + ## E2E-Läufe lassen die Einstellungen unverändert (29.09.2026) - Die Browser-Tests laufen gegen die echte Instanz; der Bargeld-Test hatte dabei die Adresse diff --git a/src/Models/Bankumsatz.php b/src/Models/Bankumsatz.php index ef2dd69..6d438c7 100644 --- a/src/Models/Bankumsatz.php +++ b/src/Models/Bankumsatz.php @@ -80,11 +80,17 @@ class Bankumsatz /** * @param array $daten * @return int|null Die neue Kennung, oder null wenn der Umsatz schon da war. + * Ein bekannter Umsatz bekommt dabei nachgetragen, was ihm bisher + * fehlte (siehe ergaenzen()). */ public static function anlegen(array $daten): ?int { $externId = (string)($daten['extern_id'] ?? ''); - if ($externId === '' || self::existiert($externId)) { + if ($externId === '') { + return null; + } + if (self::existiert($externId)) { + self::ergaenzen($externId, $daten); return null; } @@ -117,6 +123,53 @@ class Bankumsatz return Database::lastInsertId(); } + /** + * Trägt IBAN und Bank der Gegenseite bei einem bereits bekannten Umsatz + * nach. Ältere Abrufe haben sie noch nicht gespeichert; der erneute Abruf + * liefert sie, und der Fingerabdruck erkennt den Umsatz nur als bekannt. + * Nur leere Felder werden gefüllt – was schon dasteht, bleibt. Hängt der + * Umsatz an einer Buchung, bekommt auch sie die Angaben, soweit sie dort + * fehlen. + * + * @param array $daten + */ + public static function ergaenzen(string $externId, array $daten): void + { + $iban = mb_substr(strtoupper(trim((string)($daten['gegen_iban'] ?? ''))), 0, 34); + $bankname = mb_substr(trim((string)($daten['gegen_bankname'] ?? '')), 0, 128); + if ($iban === '' && $bankname === '') { + return; + } + + Database::execute( + "UPDATE bank_umsaetze + SET gegen_iban = IF(gegen_iban = '', ?, gegen_iban), + gegen_bankname = IF(gegen_bankname = '', ?, gegen_bankname) + WHERE benutzer_id = ? AND extern_id = ?", + [$iban, $bankname, Mandant::id(), $externId] + ); + + $buchungId = Database::fetchValue( + 'SELECT buchung_id FROM bank_umsaetze WHERE benutzer_id = ? AND extern_id = ?', + [Mandant::id(), $externId] + ); + if ($buchungId !== null && $buchungId !== false) { + self::herkunftUebertragen((int)$buchungId, $iban, $bankname); + } + } + + /** Füllt IBAN und Bank einer Buchung, soweit sie dort noch leer sind. */ + private static function herkunftUebertragen(int $buchungId, string $iban, string $bankname): void + { + Database::execute( + "UPDATE buchungen + SET iban = IF(iban = '', ?, iban), + bankname = IF(bankname = '', ?, bankname) + WHERE id = ? AND benutzer_id = ?", + [$iban, $bankname, $buchungId, Mandant::id()] + ); + } + public static function find(int $id): ?array { $zeile = Database::fetchOne( @@ -323,11 +376,20 @@ class Bankumsatz ); // Was von der Bank kommt, ist dort gebucht – das Kennzeichen der - // verbundenen Buchung wird nachgezogen. + // verbundenen Buchung wird nachgezogen, ebenso IBAN und Bank der + // Gegenseite, falls die Buchung sie nicht kennt. Database::execute( 'UPDATE buchungen SET abgeglichen = 1 WHERE id = ? AND benutzer_id = ?', [$buchungId, Mandant::id()] ); + $umsatz = self::find($id); + if ($umsatz !== null) { + self::herkunftUebertragen( + $buchungId, + (string)($umsatz['gegen_iban'] ?? ''), + (string)($umsatz['gegen_bankname'] ?? '') + ); + } } public static function verwerfen(int $id): void diff --git a/tests/49_UmsatzHerkunftNachtragenTest.php b/tests/49_UmsatzHerkunftNachtragenTest.php new file mode 100644 index 0000000..8f149cb --- /dev/null +++ b/tests/49_UmsatzHerkunftNachtragenTest.php @@ -0,0 +1,119 @@ +mandantAnlegen(); + + $this->giro = Konto::anlegen([ + 'name' => 'Nachtragsgiro', 'kontoart' => 'bank', 'anfangssaldo' => 0, 'aktiv' => 1, + ]); + $this->zugang = Bankzugang::anlegen([ + 'name' => 'Nachtragsbank', 'bankname' => 'Nachtragsbank AG', 'blz' => '20041111', + 'fints_url' => 'https://banking.example.org/fints', 'benutzerkennung' => 'testkunde', + 'pin' => 'geheim1234', 'abrufzeitraum' => 'letzter_abruf', 'aktiv' => 1, + ]); + $this->bankkonto = (int)Bankzugang::findeOderLegeKontoAn( + $this->zugang, + 'DE53200411110847626900', + '847626900', + 'Girokonto' + )['id']; + Bankzugang::ordneKontoZu($this->bankkonto, $this->giro); + + $this->bekannterUmsatzBekommtHerkunft(); + $this->vorhandeneAngabenBleiben(); + $this->verbundeneBuchungBekommtHerkunft(); + $this->verbindenMitErfasstenBuchungUebertraegt(); + } + + /** @return array */ + private function daten(string $zweck, string $iban = '', string $bank = ''): array + { + return [ + 'zugang_id' => $this->zugang, + 'konto_id' => $this->giro, + 'bank_konto_id' => $this->bankkonto, + 'extern_id' => Bankumsatz::externId($this->bankkonto, '2026-09-04', 0.01, $zweck), + 'buchungstag' => '2026-09-04', + 'wertstellung' => '2026-09-04', + 'betrag' => 0.01, + 'empfaenger' => 'Testabsender', + 'verwendungszweck' => $zweck, + 'gegen_iban' => $iban, + 'gegen_bankname' => $bank, + ]; + } + + private function bekannterUmsatzBekommtHerkunft(): void + { + $id = Bankumsatz::anlegen($this->daten('Cent eins')); + $this->assertNotEmpty($id, 'Umsatz ohne Herkunft angelegt'); + + $zweiter = Bankumsatz::anlegen($this->daten('Cent eins', 'de89370400440532013000', 'Testbank')); + $this->assertNull($zweiter, 'Zweiter Abruf legt keinen neuen Umsatz an'); + + $umsatz = Bankumsatz::find((int)$id); + $this->assertEquals('DE89370400440532013000', $umsatz['gegen_iban'], 'IBAN nachgetragen'); + $this->assertEquals('Testbank', $umsatz['gegen_bankname'], 'Bank nachgetragen'); + } + + private function vorhandeneAngabenBleiben(): void + { + $id = Bankumsatz::anlegen($this->daten('Cent zwei', 'DE02120300000000202051', 'Erste Bank')); + Bankumsatz::anlegen($this->daten('Cent zwei', 'DE89370400440532013000', 'Andere Bank')); + + $umsatz = Bankumsatz::find((int)$id); + $this->assertEquals('DE02120300000000202051', $umsatz['gegen_iban'], 'Vorhandene IBAN bleibt'); + $this->assertEquals('Erste Bank', $umsatz['gegen_bankname'], 'Vorhandene Bank bleibt'); + } + + private function verbundeneBuchungBekommtHerkunft(): void + { + $id = (int)Bankumsatz::anlegen($this->daten('Cent drei')); + $buchungId = Bankumsatz::uebernehmen($id, []); + + $vorher = Buchung::find($buchungId); + $this->assertEquals('', (string)$vorher['iban'], 'Buchung zunächst ohne IBAN'); + + Bankumsatz::anlegen($this->daten('Cent drei', 'DE89370400440532013000', 'Testbank')); + + $buchung = Buchung::find($buchungId); + $this->assertEquals('DE89370400440532013000', $buchung['iban'], 'IBAN in der Buchung'); + $this->assertEquals('Testbank', $buchung['bankname'], 'Bank in der Buchung'); + } + + private function verbindenMitErfasstenBuchungUebertraegt(): void + { + $buchungId = Buchung::anlegen([ + 'konto_id' => $this->giro, 'datum' => '2026-09-04', 'betrag' => 0.01, + 'empfaenger' => 'Von Hand', 'buchungstext' => 'Cent vier', + ]); + $id = (int)Bankumsatz::anlegen($this->daten('Cent vier', 'DE02120300000000202051', 'Handbank')); + + Bankumsatz::verbinden($id, $buchungId); + + $buchung = Buchung::find($buchungId); + $this->assertEquals('DE02120300000000202051', $buchung['iban'], 'Verbinden überträgt die IBAN'); + $this->assertEquals('Handbank', $buchung['bankname'], 'Verbinden überträgt die Bank'); + } +}