Fix n.n.: Bekannte Umsätze bekommen IBAN und Bank der Gegenseite nachgetragen

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
fds2610-bot 2026-09-29 11:48:13 +02:00
parent 5695e2e3df
commit 5d39293328
3 changed files with 193 additions and 2 deletions

View file

@ -1,5 +1,15 @@
# Änderungen # Ä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) ## 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 - Die Browser-Tests laufen gegen die echte Instanz; der Bargeld-Test hatte dabei die Adresse

View file

@ -80,11 +80,17 @@ class Bankumsatz
/** /**
* @param array<string,mixed> $daten * @param array<string,mixed> $daten
* @return int|null Die neue Kennung, oder null wenn der Umsatz schon da war. * @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 public static function anlegen(array $daten): ?int
{ {
$externId = (string)($daten['extern_id'] ?? ''); $externId = (string)($daten['extern_id'] ?? '');
if ($externId === '' || self::existiert($externId)) { if ($externId === '') {
return null;
}
if (self::existiert($externId)) {
self::ergaenzen($externId, $daten);
return null; return null;
} }
@ -117,6 +123,53 @@ class Bankumsatz
return Database::lastInsertId(); 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<string,mixed> $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 public static function find(int $id): ?array
{ {
$zeile = Database::fetchOne( $zeile = Database::fetchOne(
@ -323,11 +376,20 @@ class Bankumsatz
); );
// Was von der Bank kommt, ist dort gebucht – das Kennzeichen der // 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( Database::execute(
'UPDATE buchungen SET abgeglichen = 1 WHERE id = ? AND benutzer_id = ?', 'UPDATE buchungen SET abgeglichen = 1 WHERE id = ? AND benutzer_id = ?',
[$buchungId, Mandant::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 public static function verwerfen(int $id): void

View file

@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
use Finanzmanager\Database;
use Finanzmanager\Models\Bankumsatz;
use Finanzmanager\Models\Bankzugang;
use Finanzmanager\Models\Buchung;
use Finanzmanager\Models\Konto;
/**
* IBAN und Bank der Gegenseite werden beim erneuten Abruf nachgetragen – auch
* wenn der Umsatz schon bekannt ist und deshalb nicht neu angelegt wird.
*/
class UmsatzHerkunftNachtragenTest extends TestCase
{
protected string $testName = 'Test 49: Herkunft eines bekannten Umsatzes nachtragen';
private int $giro = 0;
private int $zugang = 0;
private int $bankkonto = 0;
public function run(): void
{
$this->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<string,mixed> */
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');
}
}