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>
150 lines
6 KiB
PHP
150 lines
6 KiB
PHP
<?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'
|
||
);
|
||
}
|
||
}
|