Finanzmanager/tests/51_DepotBankbestandTest.php
fds2610-bot b4a1b0acbc Fix n.n.: Depotabruf per FinTS und Nachweis zur Überweisung
Depotaufstellung (HKWPD, Version 6) unter Wertpapiere mit Vergleich zum eigenen
Bestand; Prüfsumme und Textnachweis für das Übermittlungsprotokoll einer
Überweisung; phpFinTS v4.1.5.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-30 15:10:43 +02:00

150 lines
6 KiB
PHP
Raw 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\StatementOfHoldings\Holding;
use Finanzmanager\Models\Bankprotokoll;
use Finanzmanager\Models\Depotbuchung;
use Finanzmanager\Models\Konto;
use Finanzmanager\Models\Wertpapier;
use Finanzmanager\Models\Zahlungsauftrag;
use Finanzmanager\Services\Depotabgleich;
/**
* Depotbestand laut Bank (FinTS-Depotaufstellung) und Nachweis zur Überweisung.
*
* Geprüft wird, was nach dem Abruf in der Datenbank steht: der Bestand des
* letzten Abrufs, der Kurs bekannter Papiere, der Vergleich mit dem eigenen
* Bestand – und beim Nachweis die Prüfsumme des Protokolls.
*/
class DepotBankbestandTest extends TestCase
{
protected string $testName = 'Test 51: Depotbestand laut Bank, Nachweis';
private int $depot = 0;
public function run(): void
{
$this->mandantAnlegen();
$this->depot = Konto::anlegen([
'name' => 'Bankdepot', 'kontoart' => 'depot', 'kontonummer' => '502022353',
'anfangssaldo' => 0, 'aktiv' => 1,
]);
$this->bestandUndKurs();
$this->vergleichZeigtAbweichungen();
$this->zweiterAbrufErsetztDenErsten();
$this->nachweisMitPruefsumme();
}
private function position(string $isin, string $name, float $stueck, float $kurs): Holding
{
return (new Holding())
->setISIN($isin)
->setName($name)
->setAmount($stueck)
->setPrice($kurs)
->setValue($stueck * $kurs)
->setCurrency('EUR')
->setDate(new \DateTime('2026-09-30'));
}
private function bestandUndKurs(): void
{
$bekannt = Wertpapier::findenOderAnlegen([
'name' => 'Bekannte AG', 'isin' => 'DE0001111111', 'art' => 'aktie',
]);
$ergebnis = Depotabgleich::uebernehmen($this->depot, [
$this->position('DE0001111111', 'Bekannte AG', 10.0, 12.5),
$this->position('DE0002222222', 'Unbekannte AG', 4.0, 100.0),
]);
$this->assertEquals(2, $ergebnis['positionen'], 'Beide Positionen stehen im Bankbestand');
$this->assertEquals(1, $ergebnis['kurse'], 'Nur das bekannte Papier bekommt einen Kurs');
$this->assertEquals(
12.5,
(float)Wertpapier::letzterKurs($bekannt)['kurs'],
'Der Bewertungskurs der Bank ist der Kurs des Papiers'
);
$this->assertNull(
Wertpapier::finde('DE0002222222'),
'Das unbekannte Papier wird nicht angelegt'
);
$this->assertNotEmpty(Depotabgleich::abgerufenAm($this->depot), 'Der Abrufzeitpunkt ist vermerkt');
}
private function vergleichZeigtAbweichungen(): void
{
$bekannt = Wertpapier::finde('DE0001111111');
Depotbuchung::anlegen([
'konto_id' => $this->depot, 'wertpapier_id' => (int)$bekannt['id'],
'vorgang' => 'kauf', 'datum' => '2026-01-05', 'stueck' => 8, 'kurs' => 10, 'betrag' => 80,
]);
$verkauftes = Wertpapier::findenOderAnlegen([
'name' => 'Alte AG', 'isin' => 'DE0003333333', 'art' => 'aktie',
]);
Depotbuchung::anlegen([
'konto_id' => $this->depot, 'wertpapier_id' => $verkauftes,
'vorgang' => 'kauf', 'datum' => '2026-01-05', 'stueck' => 3, 'kurs' => 10, 'betrag' => 30,
]);
$je = [];
foreach (Depotabgleich::vergleich($this->depot) as $zeile) {
$je[$zeile['isin']] = $zeile;
}
$this->assertEquals(2.0, $je['DE0001111111']['abweichung'], 'Die Bank hat 2 Stück mehr als gebucht');
$this->assertEquals(4.0, $je['DE0002222222']['abweichung'], 'Nur bei der Bank: ganze Position abweichend');
$this->assertNull($je['DE0002222222']['eigen'], 'Für das unbekannte Papier gibt es keinen eigenen Bestand');
$this->assertEquals(-3.0, $je['DE0003333333']['abweichung'], 'Nur im Finanzmanager: Bank führt es nicht');
$this->assertNull($je['DE0003333333']['bank'], 'Bei der Bank fehlt das Papier');
}
private function zweiterAbrufErsetztDenErsten(): void
{
Depotabgleich::uebernehmen($this->depot, [
$this->position('DE0001111111', 'Bekannte AG', 8.0, 13.0),
]);
$bank = array_filter(
Depotabgleich::vergleich($this->depot),
static fn(array $zeile): bool => $zeile['bank'] !== null
);
$this->assertEquals(1, count($bank), 'Der neue Abruf ersetzt den alten Bankbestand');
}
private function nachweisMitPruefsumme(): void
{
$verrechnung = Konto::anlegen([
'name' => 'Nachweisgiro', 'kontoart' => 'bank', 'anfangssaldo' => 100, 'aktiv' => 1,
]);
$auftragId = Zahlungsauftrag::anlegen([
'konto_id' => $verrechnung, 'empfaenger' => 'Beweis GmbH',
'iban' => 'DE02120300000000202051', 'betrag' => 12.34,
'verwendungszweck' => 'Rechnung 42',
]);
$dialog = "> HNHBK:1:3+000000000100'\n< HIRMG:2:2+0010::Nachricht entgegengenommen'";
$id = Bankprotokoll::speichern(null, 'ueberweisung', 'erfolg', 'übergeben', $dialog, $auftragId);
$this->assertNotEmpty($id, 'Das Protokoll wird gespeichert');
$auftrag = Zahlungsauftrag::find($auftragId);
$nachweis = Bankprotokoll::nachweis($auftrag);
$this->assert(str_contains($nachweis, 'Beweis GmbH'), 'Der Nachweis nennt den Empfänger');
$this->assert(str_contains($nachweis, $dialog), 'Der Nachweis enthält den vollständigen Dialog');
$this->assert(str_contains($nachweis, hash('sha256', $dialog)), 'Der Nachweis nennt die Prüfsumme');
$this->assert(str_contains($nachweis, 'unverändert seit dem Speichern'), 'Unveränderter Text wird bestätigt');
\Finanzmanager\Database::execute(
'UPDATE bank_protokolle SET protokoll = ? WHERE id = ?',
[$dialog . "\nnachträglich", $id]
);
$this->assert(
str_contains(Bankprotokoll::nachweis($auftrag), 'ABWEICHUNG'),
'Ein nachträglich geänderter Text fällt im Nachweis auf'
);
}
}