Finanzmanager/tests/62_WertpapierKlasseTest.php
fds2610-bot d3f298f26c Fix #125: Klasse beim Buchen von Wertpapieren wählen
Die Wertpapiermaske (Dialog im Kontoblatt und Seite ohne JavaScript) sowie
„Depotbuchung erfassen“ bieten eine Klasse an. Im Kontoblatt ist sie mit der
bisherigen Klasse der Buchung vorbelegt und gilt für die Buchung samt aller
Splitzeilen der Nebenkosten; bei der Depotbuchung steht sie an der erzeugten
Geldbuchung. Unbekannte Klassen werden abgewiesen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:35:31 +02:00

202 lines
7.4 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 Finanzmanager\Models\Buchung;
use Finanzmanager\Models\Depotbuchung;
use Finanzmanager\Models\Einstellung;
use Finanzmanager\Models\Kategorie;
use Finanzmanager\Models\Klasse;
use Finanzmanager\Models\Konto;
use Finanzmanager\Models\Wertpapier;
use Finanzmanager\Services\Wertpapiergeschaeft;
use Finanzmanager\Services\Wertpapierkosten;
/**
* Fix #125: Beim Buchen von Wertpapieren lässt sich eine Klasse wählen – im
* Wertpapiergeschäft aus dem Kontoblatt wie in der Depotbuchung mit eigener
* Geldbuchung.
*/
class WertpapierKlasseTest extends TestCase
{
protected string $testName = 'Test 62: Klasse beim Buchen von Wertpapieren';
private int $giro = 0;
private int $depot = 0;
private int $klasse = 0;
private int $andereKlasse = 0;
public function run(): void
{
$this->mandantAnlegen();
$this->giro = Konto::anlegen([
'name' => 'KL Giro', 'kontoart' => 'bank', 'anfangssaldo' => 10000, 'aktiv' => 1,
]);
$this->depot = Konto::anlegen([
'name' => 'KL Depot', 'kontoart' => 'depot', 'anfangssaldo' => 0, 'aktiv' => 1,
]);
$this->klasse = Klasse::anlegen('KL Altersvorsorge');
$this->andereKlasse = Klasse::anlegen('KL Haushalt');
Einstellung::set(
Wertpapierkosten::schluessel('bankprovision'),
(string)Kategorie::anlegen(['name' => 'KL Bankprovision', 'art' => 'ausgabe']),
'wertpapiere'
);
$this->geschaeftOhneKosten();
$this->geschaeftMitKosten();
$this->leereAuswahlLoeschtDieKlasse();
$this->ohneAngabeBleibtDieKlasse();
$this->unbekannteKlasse();
$this->depotbuchungMitGeldbuchung();
}
private function buchung(float $betrag, string $datum, ?int $klasse = null): int
{
return Buchung::anlegen([
'konto_id' => $this->giro,
'datum' => $datum,
'betrag' => $betrag,
'klasse_id' => $klasse,
'quelle' => 'banking',
]);
}
private function geschaeftOhneKosten(): void
{
$id = $this->buchung(-500.0, '2026-09-01');
Wertpapiergeschaeft::buchen(Buchung::find($id), [
'depot_id' => $this->depot,
'wertpapier_name' => 'KL Ohne Kosten AG',
'stueck' => 5,
'klasse_id' => $this->klasse,
]);
$buchung = Buchung::find($id);
$this->assertEquals($this->depot, (int)$buchung['gegenkonto_id'], 'Die Umbuchung ins Depot entsteht');
$this->assertEquals($this->klasse, (int)$buchung['klasse_id'], 'Die gewählte Klasse steht an der Buchung');
}
private function geschaeftMitKosten(): void
{
$id = $this->buchung(-255.0, '2026-09-02');
Wertpapiergeschaeft::buchen(Buchung::find($id), [
'depot_id' => $this->depot,
'wertpapier_name' => 'KL Mit Kosten AG',
'stueck' => 2,
'kosten' => ['bankprovision' => 5.0],
'klasse_id' => $this->klasse,
]);
$buchung = Buchung::find($id);
$this->assertEquals($this->klasse, (int)$buchung['klasse_id'], 'Auch die Splitbuchung trägt die Klasse');
$splits = Buchung::splits($id);
$this->assertEquals(2, count($splits), 'Depot und Bankprovision');
foreach ($splits as $nr => $zeile) {
$this->assertEquals($this->klasse, (int)$zeile['klasse_id'], 'Splitzeile ' . $nr . ' trägt die Klasse');
}
$anzeige = Buchung::splitKlasse($splits, (int)$buchung['klasse_id']);
$this->assert(!$anzeige['gemischt'], 'Das Kontoblatt zeigt keine gemischten Klassen');
$this->assertEquals($this->klasse, $anzeige['klasse_id'], 'Sondern die eine gewählte Klasse');
}
/** Die Maske ist mit der Klasse der Buchung vorbelegt – leer heißt: keine. */
private function leereAuswahlLoeschtDieKlasse(): void
{
$id = $this->buchung(-100.0, '2026-09-03', $this->andereKlasse);
Wertpapiergeschaeft::buchen(Buchung::find($id), [
'depot_id' => $this->depot,
'wertpapier_name' => 'KL Leer AG',
'stueck' => 1,
'klasse_id' => null,
]);
$this->assertNull(Buchung::find($id)['klasse_id'], 'Die leere Auswahl entfernt die Klasse');
}
private function ohneAngabeBleibtDieKlasse(): void
{
$id = $this->buchung(-110.0, '2026-09-04', $this->andereKlasse);
Wertpapiergeschaeft::buchen(Buchung::find($id), [
'depot_id' => $this->depot,
'wertpapier_name' => 'KL Ohne Angabe AG',
'stueck' => 1,
'kosten' => ['bankprovision' => 10.0],
]);
$this->assertEquals(
$this->andereKlasse,
(int)Buchung::find($id)['klasse_id'],
'Ohne Angabe bleibt die Klasse der Buchung'
);
foreach (Buchung::splits($id) as $zeile) {
$this->assertEquals($this->andereKlasse, (int)$zeile['klasse_id'], 'Und die Splitzeilen übernehmen sie');
}
}
private function unbekannteKlasse(): void
{
$id = $this->buchung(-120.0, '2026-09-05');
$abgewiesen = false;
try {
Wertpapiergeschaeft::buchen(Buchung::find($id), [
'depot_id' => $this->depot,
'wertpapier_name' => 'KL Unbekannt AG',
'stueck' => 1,
'klasse_id' => 999999999,
]);
} catch (\InvalidArgumentException $e) {
$abgewiesen = true;
}
$this->assert($abgewiesen, 'Eine unbekannte Klasse wird abgewiesen');
$this->assertNull(Depotbuchung::findeZuBuchung($id), 'Und es entsteht kein Geschäft');
}
/** Die Depotbuchung unter Wertpapiere legt die Geldbuchung selbst an. */
private function depotbuchungMitGeldbuchung(): void
{
$papier = Wertpapier::findenOderAnlegen(['name' => 'KL Depot AG', 'isin' => 'DE000KL00001']);
$depotbuchungId = Depotbuchung::anlegen([
'konto_id' => $this->depot,
'wertpapier_id' => $papier,
'datum' => '2026-09-06',
'vorgang' => 'kauf',
'stueck' => 10,
'kurs' => 20,
'betrag' => 200,
'verrechnungskonto_id' => $this->giro,
'klasse_id' => $this->klasse,
]);
$geldbuchung = Buchung::find((int)Depotbuchung::find($depotbuchungId)['buchung_id']);
$this->assertNotEmpty($geldbuchung, 'Die Geldbuchung entsteht');
$this->assertEquals($this->klasse, (int)$geldbuchung['klasse_id'], 'Mit der gewählten Klasse');
$abgewiesen = false;
try {
Depotbuchung::anlegen([
'konto_id' => $this->depot,
'wertpapier_id' => $papier,
'datum' => '2026-09-07',
'vorgang' => 'kauf',
'stueck' => 1,
'betrag' => 20,
'verrechnungskonto_id' => $this->giro,
'klasse_id' => 999999999,
]);
} catch (\InvalidArgumentException $e) {
$abgewiesen = true;
}
$this->assert($abgewiesen, 'Auch hier wird eine unbekannte Klasse abgewiesen');
}
}