In „Depotbuchung erfassen“ ergibt sich bei Kauf und Verkauf aus zwei der drei Felder Betrag, Stück und Kurs das dritte (Betrag = Stück × Kurs). Der ausgerechnete Wert ist hervorgehoben und zieht nach, wenn sich die anderen Felder ändern. Serverseitig rechnet Depotbuchung::dritterWert() dieselbe Regel für den Weg ohne JavaScript. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
340 lines
18 KiB
Markdown
340 lines
18 KiB
Markdown
# Wertpapiere und Depots
|
||
|
||
Ein Depot ist ein Konto der Kontoart **Depot**. Was darin liegt, steht nicht in
|
||
`buchungen`, sondern in `depot_buchungen` – dort gehören Stückzahl und Kurs dazu, und der
|
||
Saldo ist keine Summe von Beträgen, sondern der bewertete Bestand.
|
||
|
||
## Zwei Seiten je Geschäft
|
||
|
||
Jedes Wertpapiergeschäft erzeugt bis zu zwei Datensätze:
|
||
|
||
* die **Depotbuchung** mit Stück, Kurs, Gebühren und Steuern,
|
||
* die **Geldbuchung** auf dem Verrechnungskonto – eine gewöhnliche Buchung, die im
|
||
Kontoblatt steht wie jede andere.
|
||
|
||
Verbunden sind sie über `buchung_id`. So bleibt das Girokonto stimmig, ohne dass
|
||
Kontoblätter Stückzahlen führen müssten.
|
||
|
||
Beide Richtungen kommen vor, und `buchung_erzeugt` hält fest, welche es war:
|
||
|
||
* **Depotbuchung zuerst** (Maske unter *Wertpapiere*, QIF-Import): Sie legt die
|
||
Geldbuchung an. Wird sie gelöscht, geht die Geldbuchung mit – sie hat ohne das Geschäft
|
||
keinen Sinn.
|
||
* **Geldbuchung zuerst** (Bankabruf, siehe *Geschäft aus einer Kontobuchung*): Die
|
||
Depotbuchung hängt sich an die vorhandene Buchung. Wird sie gelöscht, bleibt die Buchung
|
||
der Bank stehen und ist nur wieder unzugeordnet – sie ist deren Beleg, nicht unsere.
|
||
|
||
Ein- und Auslieferungen rühren kein Konto an und haben deshalb keine Geldbuchung.
|
||
|
||
## Geschäft aus einer Kontobuchung
|
||
|
||
Der Weg, den der Alltag nimmt: Der Bankabruf liefert die Wertpapierabrechnung als Umsatz
|
||
auf dem Girokonto – ein Betrag, ein Verwendungszweck, sonst nichts. Im Kontoblatt öffnet
|
||
die Zuordnungsliste unter *Wertpapiergeschäft im Depot* die Wertpapiermaske; die schlichte
|
||
Umbuchung auf dasselbe Depot steht daneben weiter zur Wahl.
|
||
|
||
Zwei Dialoge, wie im FinanzManager: vorne Vorgangsart, Datum, Wertpapier, Stück und Kurs,
|
||
dahinter hinter *Kosten …* die Nebenkosten nach Arten. ISIN und WKN liest
|
||
`Wertpapier::ausText()` aus dem Verwendungszweck („WKN LYX01W … ISIN LU1834983477“) und
|
||
wählt das Papier vor. Ohne JavaScript führt derselbe Weg auf
|
||
`/buchungen/{id}/wertpapier` – dieselben Felder, untereinander statt hinter einem Knopf.
|
||
|
||
**Der Buchungsbetrag ist die feste Größe.** Er kommt von der Bank und wird nicht
|
||
nachgerechnet. Die Nebenkosten gehen aus ihm heraus, der Rest ist der Kurswert und damit
|
||
der Einstandspreis:
|
||
|
||
```
|
||
Kurswert = Buchungsbetrag + Summe der Nebenkosten
|
||
|
||
Kauf −255,00 € + 5,00 € = −250,00 € → Einstand 250,00 €
|
||
Verkauf 3.103,17 € + 84,83 € = 3.188,00 € → Erlös 3.188,00 €
|
||
```
|
||
|
||
Dieselbe Rechnung in beiden Richtungen: Nebenkosten sind Aufwand, ob gekauft oder verkauft
|
||
wird. Der Kurs ist demgegenüber eine Angabe der Abrechnung; fehlt er, ergibt er sich aus
|
||
Einstand und Stückzahl – nie umgekehrt.
|
||
|
||
Im Kontoblatt entsteht daraus eine **Splitbuchung**: je belegter Kostenart eine Zeile in
|
||
ihrer Kategorie, der Rest als Umbuchung ins Depot. Die Zeilen ergeben genau den
|
||
Buchungsbetrag. Ohne Nebenkosten bleibt es bei der schlichten Umbuchung – eine
|
||
Splitbuchung mit einer einzigen Zeile wäre nur umständlicher zu lesen.
|
||
|
||
**Klasse.** Die Maske bietet eine Klasse an, vorbelegt mit der bisherigen Klasse der
|
||
Buchung. Sie gilt für das ganze Geschäft: an der Buchung und an jeder Splitzeile, damit
|
||
das Kontoblatt keine gemischten Klassen zeigt und die Nebenkosten in der Auswertung nach
|
||
Klassen nicht fehlen. Eine leere Auswahl entfernt die Klasse. Auch die Maske *Depotbuchung
|
||
erfassen* unter *Wertpapiere* hat das Feld; dort steht die Klasse an der Geldbuchung auf
|
||
dem Verrechnungskonto.
|
||
|
||
### Kostenarten und ihre Kategorien
|
||
|
||
`Services\Wertpapierkosten` kennt sechs Gebühren- und sechs Steuerarten – dieselben, die
|
||
der FinanzManager im Kostendialog führt:
|
||
|
||
| Gebühren | Steuern |
|
||
|---|---|
|
||
| Bankprovision | Zinsabschlagsteuer |
|
||
| Maklercourtage | Kapitalertragsteuer |
|
||
| Börsenplatzentgelt | Solidaritätszuschlag |
|
||
| Spesen | Ausländische Quellensteuer |
|
||
| Spesen Ausland | Abgeltungsteuer |
|
||
| Sonstige Kosten | Kirchensteuer |
|
||
|
||
Welche Kategorie zu welcher Art gehört, steht **einmal** unter *Einstellungen →
|
||
Wertpapiere* (`wertpapiere_kategorie_<art>`); der Dialog fragt dann nur noch Beträge ab.
|
||
Einzige Ausnahme ist *Sonstige Kosten*: Deren Kategorie lässt sich an Ort und Stelle
|
||
wählen, weil sie nicht immer dieselbe ist. Arten ohne hinterlegte Kategorie werden zu
|
||
Splitzeilen ohne Zuordnung, nicht zu einem Fehler.
|
||
|
||
In der Depotbuchung stehen die Kosten nur als zwei Summen (`gebuehren`, `steuern`) – für
|
||
den Einstand genügt das. Die Aufschlüsselung lebt in den Splitzeilen, wo sie gebraucht
|
||
wird.
|
||
|
||
## Was in welchem Feld steht
|
||
|
||
`betrag` ist der **Bruttowert**: Stück × Kurs, bei Dividenden der Bruttoertrag. Gebühren
|
||
und Steuern stehen *daneben*, nicht darin. Daraus folgt der Geldfluss:
|
||
|
||
| Vorgang | Verrechnungskonto |
|
||
|---|---|
|
||
| Kauf | −(Betrag + Gebühren + Steuern) |
|
||
| Verkauf | +(Betrag − Gebühren − Steuern) |
|
||
| Dividende | +(Betrag − Steuern) |
|
||
|
||
Am echten Export nachgerechnet: Ein Verkauf über 4 × 797,00 € mit 15,37 € Gebühren und
|
||
69,46 € Steuern ergibt 3.103,17 € Gutschrift – genau der Wert, den das QIF im Feld `$`
|
||
nennt. Diese Trennung ist nötig, damit der Einstand die Nebenkosten **genau einmal**
|
||
enthält.
|
||
|
||
`stueck` trägt die Richtung: Zugänge positiv, Abgänge negativ, Dividenden null.
|
||
|
||
In der Maske *Depotbuchung erfassen* genügen bei Kauf und Verkauf zwei der drei Angaben
|
||
Stück, Kurs und Betrag: Sobald zwei eingetragen sind, rechnet `public/static/depotbuchung.js`
|
||
das dritte aus (Betrag auf zwei, Stück und Kurs auf sechs Nachkommastellen) und hebt es
|
||
farblich hervor. Ein ausgerechneter Wert wird nachgezogen, wenn sich eines der anderen
|
||
Felder ändert; tippt man ihn selbst um, gilt er als eingetragen. Ohne JavaScript rechnet
|
||
`Depotbuchung::dritterWert()` dieselbe Regel beim Speichern.
|
||
|
||
## Einstand nach gleitendem Durchschnitt
|
||
|
||
Bei jedem Kauf steigt der Einstand um Kaufpreis samt Gebühren, bei jedem Verkauf sinkt er
|
||
anteilig um den bis dahin durchschnittlichen Stückpreis. So rechnet auch der
|
||
FinanzManager, und die Frage, *welches* Stück verkauft wurde, stellt sich gar nicht.
|
||
|
||
Der **realisierte Gewinn** eines Verkaufs ist Erlös minus Kosten minus anteiligem
|
||
Einstand.
|
||
|
||
> Für die Steuererklärung gilt in Deutschland FIFO. Maßgeblich ist dort die Abrechnung der
|
||
> Bank – diese Übersicht ersetzt sie nicht.
|
||
|
||
Vollständig verkaufte Positionen verschwinden aus der Bestandsliste; ihr realisierter
|
||
Gewinn zählt weiter mit.
|
||
|
||
## Detailseite eines Wertpapiers
|
||
|
||
Der Name in der Bestandsübersicht, in der Liste der Depotbuchungen und in den Stammdaten
|
||
führt auf `/wertpapiere/papier/{id}` – alles zu einem Papier auf einer Seite:
|
||
|
||
* **Kennzahlen** über die ganze Historie: Bestand, Wert, Einstand, nicht realisierter und
|
||
realisierter Gewinn, Erträge. Wer wissen will, was ein Papier gebracht hat, meint alles
|
||
seit dem ersten Kauf – nicht den gewählten Berichtszeitraum.
|
||
* **Stammdaten und Kurs** in je einer Kachel: ISIN, WKN, Art, Währung, Kürzel, Status,
|
||
erste und letzte Buchung; daneben der letzte Kurs mit Datum und Quelle, Hoch und Tief
|
||
der letzten zwölf Monate (`Wertpapier::kurseckwerte()`) und der Einstand je Stück.
|
||
* **Bestand je Depot**: Ein Papier kann in mehreren Depots liegen; gerechnet wird je Depot,
|
||
weil auch der Einstand je Depot geführt wird. Ist alles verkauft, bleibt die Seite
|
||
bestehen – realisierte Gewinne und Erträge zählen weiter.
|
||
* **Wertentwicklung** – dasselbe Diagramm wie in der Depotübersicht, nur auf dieses Papier
|
||
gerechnet, und nur, wenn es Kurshistorie gibt.
|
||
* **Buchungen** eines Zeitraums, voreingestellt das laufende Jahr, mit denselben Vorgaben
|
||
wie die Auswertungen (`Services\Zeitraum`). Darunter eine Zeile mit Zu- und Abgängen und
|
||
den Erträgen des Zeitraums.
|
||
|
||
Gerechnet wird nicht neu: `Depot::bestand()` nimmt eine Wertpapiernummer entgegen und
|
||
liefert dieselben Zahlen wie die Übersicht, nur auf ein Papier eingegrenzt. Damit können
|
||
Detailseite und Übersicht nicht auseinanderlaufen.
|
||
|
||
## Kurse
|
||
|
||
Ein Kurs je Wertpapier und Tag. Mehr Auflösung braucht eine Vermögensübersicht nicht, und
|
||
die Freikontingente der Kursdienste geben nicht mehr her.
|
||
|
||
**Ohne Kurs wird nicht bewertet**: Die Position zeigt „kein Kurs“ und fällt aus der
|
||
Wertsumme; die Übersicht sagt ausdrücklich, dass sie unvollständig ist. Der Einstand als
|
||
Ersatzwert wäre bequem, verstellte aber den Blick.
|
||
|
||
### Kursabruf
|
||
|
||
`Services\Kursabruf` kennt drei Anbieter (siehe `Aktienkurse.md`):
|
||
|
||
* **Leeway.tech** – 50 Abrufe/Tag, kennt ISINs direkt
|
||
* **Financial Modeling Prep** – 250 Abrufe/Tag, im Gratistarif ohne ISIN-Suche und ohne
|
||
deutsche Börsenplätze (beides antwortet mit HTTP 402)
|
||
* **Yahoo Finanzen** – kein Schlüssel, kein Kontingent, dafür muss das Kürzel in den
|
||
Stammdaten stehen
|
||
|
||
Die ersten beiden arbeiten in zwei Schritten: ISIN → Kürzel, Kürzel → Kurs. Das gefundene
|
||
Kürzel wird am Wertpapier gespeichert, damit der erste Schritt künftig entfällt – das
|
||
halbiert den Verbrauch. Abgerufen wird nur für Papiere **mit Bestand**, und nur, wenn für
|
||
heute noch kein Kurs vorliegt.
|
||
|
||
**Yahoo Finanzen** hat keine offene Schnittstelle mehr: `query1.finance.yahoo.com`
|
||
antwortet ohne Sitzung mit HTTP 429, auch mit Cookie und Crumb, und `/lookup` wie `/search`
|
||
sind abgeschaltet. Gelesen wird deshalb die Kursseite `de.finance.yahoo.com/quote/<Kürzel>/`.
|
||
|
||
* Das **Kürzel** muss den Handelsplatz nennen – `ALV.DE` für Xetra, `PTX.F` für Frankfurt.
|
||
Ohne Kürzel meldet der Abruf das ausdrücklich, denn raten lässt sich hier nichts.
|
||
* Yahoo liefert die Seite in zwei Ausprägungen aus: mal mit `<fin-streamer>` samt
|
||
`data-value`, mal nur mit dem angezeigten, deutsch formatierten Kurs. `yahooKurs()` liest
|
||
beides und prüft am Seitentitel, dass die Seite zum Kürzel gehört – sonst käme bei einem
|
||
Tippfehler der Kurs eines fremden Papiers in den Bestand.
|
||
* Zwischen zwei Seiten liegt **eine Sekunde**; auf schnelle Serienabrufe antwortet Yahoo
|
||
mit HTTP 503. Dieser 503 ist vorübergehend – er trifft auch Kürzel, die im Browser zur
|
||
selben Zeit funktionieren –, deshalb bis zu drei Versuche je Papier.
|
||
* Der angezeigte Kursstand („Markt geöffnet“, „Bei Börsenschluss“) geht ins Debug-Log:
|
||
Über sein Auslieferungsnetz gibt Yahoo gelegentlich eine ältere Fassung der Seite heraus.
|
||
|
||
Weil hier fremdes Markup gelesen wird, ist dieser Weg der empfindlichste der drei – ein
|
||
Umbau bei Yahoo fällt als Fehler auf, nicht als falscher Kurs.
|
||
|
||
Anbieter und Schlüssel stehen im Einstellungsdialog unter *Wertpapiere*. Der tägliche Lauf
|
||
gehört in einen Cron-Eintrag:
|
||
|
||
```bash
|
||
docker compose exec -u www-data php php scripts/kursabruf.php
|
||
```
|
||
|
||
Das Skript bricht ab, solange der automatische Abruf in den Einstellungen nicht
|
||
eingeschaltet ist – ein vergessener Cron-Eintrag kostet so kein Kontingent. Der Knopf auf
|
||
der Wertpapierseite arbeitet unabhängig davon.
|
||
|
||
Die Feldnamen der Antworten werden nachsichtig gelesen (`close`, `price`, `last` …): Die
|
||
Dienste benennen sie unterschiedlich und gelegentlich um. Der API-Schlüssel steht in der
|
||
Adresse und wird vor jedem Logeintrag maskiert.
|
||
|
||
## QIF-Import (`!Type:Invst`)
|
||
|
||
Der Import legt Wertpapier, Depotbuchung und Geldbuchung in einem Zug an. Zwei
|
||
Eigenheiten des Formats:
|
||
|
||
* **Das `L`-Feld trägt bei Wertpapiergeschäften mehr als sonst.** Dem Verrechnungskonto
|
||
kann eine Kategorie vorangehen: `LKursgewinne:Realisierte Gewinne|[Verrechnungskonto]`.
|
||
Gelesen wird deshalb nur der Teil hinter dem letzten senkrechten Strich.
|
||
* **Das Feld `O` führt zwölf Beträge.** Sicher zugeordnet sind Position 4 (Provision) und
|
||
10 (fremde Spesen) – beides Gebühren. Alles Übrige sind Abzüge des Fiskus und werden zu
|
||
„Steuern“ zusammengefasst. Damit fällt keine Position heraus, ohne dass jede einzeln
|
||
gedeutet werden müsste.
|
||
|
||
Fehlt der Kurs – bei Dividenden immer –, wird der Bruttobetrag aus der Gutschrift plus den
|
||
Abzügen zurückgerechnet.
|
||
|
||
`DivIn` und `DivAus` landen beide auf „dividende“; die ursprüngliche Bezeichnung bleibt in
|
||
`vorgang_qif` erhalten, weil die Zuordnung nicht in jedem Fall umkehrbar ist.
|
||
|
||
**Dabei behoben:** Bis Phase 5 buchte der Import den Geldfluss *jedes*
|
||
Wertpapiergeschäfts als Abgang – auch bei Verkäufen und Dividenden. Das QIF nennt im Feld
|
||
`$` immer einen positiven Betrag; die Richtung steckt allein im Vorgang. Verrechnungskonten
|
||
von Depots wiesen dadurch zu niedrige Salden aus.
|
||
|
||
## CSV-Import einer Bestandsliste
|
||
|
||
*Wertpapiere → Bestände → Import* übernimmt die Depotbestandsliste, wie Banken sie zum
|
||
Herunterladen anbieten (erprobt mit der DKB). Erwartet wird eine Kopfzeile mit den Spalten
|
||
*Wertpapierbezeichnung*, *WKN*, *ISIN*, *Einstiegskurs*, *Bewertungskurs*, *Stückzahl* und
|
||
*Assetklasse*; *Datum der Erstellung* und *Depotnummer* werden mitgelesen, wenn vorhanden.
|
||
Trennzeichen (`;`, `,`, Tabulator) und Zeichensatz (UTF-8 mit oder ohne BOM,
|
||
Windows-1252) erkennt der Import selbst.
|
||
|
||
**Der Export kennt keine Historie.** Er beschreibt einen Stichtag: was im Depot liegt und
|
||
was es im Schnitt gekostet hat. Die Geschäfte, die dorthin geführt haben, fehlen. Der
|
||
Import legt deshalb je Position **eine** Depotbuchung an – einen Kauf zum Stichtag über
|
||
die volle Stückzahl zum Einstiegskurs. Der Einstand stimmt damit auf den Cent, frühere
|
||
Käufe, Verkäufe und Dividenden bleiben naturgemäß leer und lassen sich später von Hand
|
||
nachtragen.
|
||
|
||
**Zwei Kurse, zwei Rollen.** Der *Einstiegskurs* ist der durchschnittliche Kaufpreis und
|
||
wird zum Kurs der Buchung; der *Bewertungskurs* ist der aktuelle Kurs und landet als Kurs
|
||
des Stichtags in `kurse`. Erst dadurch ist der frisch übernommene Bestand sofort bewertbar
|
||
und zeigt denselben Gewinn wie die Bank – bis auf wenige Cent, die in den auf zwei Stellen
|
||
gerundeten Kursen des Exports stecken.
|
||
|
||
**Kein Geldfluss.** Ein Verrechnungskonto ist wählbar, bleibt aber in aller Regel leer:
|
||
Das Geld ist vor Jahren geflossen, eine Belastung zum heutigen Tag würde das Girokonto
|
||
verfälschen. Ohne Verrechnungskonto entsteht keine Geldbuchung.
|
||
|
||
**Assetklasse → Art**: *Aktien* → Aktie, *ETFs* und *Fonds* → Fonds, *Anleihen* →
|
||
Anleihe, *Zertifikate* → Zertifikat, alles Übrige → Sonstiges.
|
||
|
||
Das Zieldepot lässt sich wählen; ohne Auswahl sucht der Import das Depot, dessen
|
||
**Kontonummer** der Depotnummer aus der Datei entspricht. Jede Position bekommt einen
|
||
`import_hash` aus Depot, Datum, Vorgang, Stück, Betrag und ISIN – dieselbe Datei zweimal
|
||
eingelesen legt nichts doppelt an. Ein *anderer* Stichtag gilt dagegen als neuer Vorgang.
|
||
|
||
Der Ablauf ist zweistufig: Der Upload zeigt erst eine Vorschau mit allen Positionen,
|
||
Einstand, Wert und dem Status je Zeile; erst *Jetzt übernehmen* schreibt. Die Datei bleibt
|
||
im Importverzeichnis liegen.
|
||
|
||
## Depotbuchungen löschen
|
||
|
||
Die Liste der Depotbuchungen löscht einzeln – *Löschen* je Zeile – oder mehrere auf
|
||
einmal, genau wie das Kontoblatt: Ankreuzfeld je Zeile, das Feld in der Titelzeile wählt
|
||
alle angezeigten, *Auswahl löschen* in der Aktionsleiste schickt sie an
|
||
`POST /wertpapiere/buchungen/loeschen`. Der Knopf nennt die Anzahl und bleibt ohne
|
||
Auswahl gesperrt; die Rückfrage kommt vor dem Absenden. Ohne JavaScript funktioniert
|
||
alles weiter – dann ist der Knopf offen und der Controller weist eine leere Auswahl ab.
|
||
|
||
Der eingestellte Filter hängt am Ziel beider Formulare, deshalb steht danach wieder
|
||
dieselbe Ansicht da. Zusammengesetzt wird er im Controller neu, damit kein fremdes Ziel
|
||
eingeschleust werden kann.
|
||
|
||
Mit jeder Depotbuchung geht ihre **selbst erzeugte** Geldbuchung (siehe *Zwei Seiten je
|
||
Geschäft*). Hing sie dagegen an einer Buchung des Bankabrufs, bleibt diese stehen und
|
||
verliert nur die Aufteilung des Geschäfts – sie steht danach wieder unzugeordnet im
|
||
Kontoblatt. Am
|
||
Bestand ist nichts nachzuziehen: Er wird bei jedem Aufruf aus den Depotbuchungen
|
||
gerechnet und gibt die Löschung von selbst wieder her – ebenso realisierter Gewinn,
|
||
Veräußerungen und Erträge. Das Wertpapier selbst bleibt in den Stammdaten stehen.
|
||
|
||
## Kontenübersicht
|
||
|
||
Depots erscheinen dort mit ihrem **bewerteten Bestand**, nicht mit einem Buchungssaldo. Der
|
||
Anfangssaldo bleibt dabei: Führt ein Depotkonto eine Barreserve, steht sie dort.
|
||
|
||
Damit ist die Voraussetzung erfüllt, um die Summe der Kontostände mit dem FinanzManager zu
|
||
vergleichen (`PLAN.md`, Kapitel 7) – sobald Kurse für alle Positionen vorliegen.
|
||
|
||
## Tests
|
||
|
||
`tests/17_WertpapiereTest.php` – Wiederfinden über ISIN, WKN und Name samt Nachtragen
|
||
fehlender Felder; Bestand und Einstand über mehrere Käufe; Geldseite mit richtigem
|
||
Vorzeichen; Verkauf mit anteiligem Einstand und realisiertem Gewinn; Dividende ohne
|
||
Bestandsänderung; fehlende Kurse; Veräußerungsrechnung über die Historie hinweg; der
|
||
QIF-Import eines echten Ausschnitts (Kauf, Dividende, Verkauf) inklusive Idempotenz;
|
||
Löschen samt Geldbuchung.
|
||
|
||
`tests/22_DepotbuchungenLoeschenTest.php` – das Sammellöschen: die Geldbuchungen gehen
|
||
mit, das Verrechnungskonto stimmt danach wieder, Bestand, Einstand, realisierter Gewinn,
|
||
Erträge und die Veräußerungsauswertung geben die Löschung her, unbekannte Nummern in der
|
||
Auswahl bleiben folgenlos. Den Ablauf im Browser prüft `tests/e2e/specs/module.spec.js`.
|
||
|
||
`tests/25_WertpapiergeschaeftTest.php` – das Geschäft aus einer Kontobuchung: das Depot als
|
||
eigener Wert der Zuordnungsliste, ISIN und WKN aus dem Verwendungszweck, Kauf mit Kosten
|
||
(Einstand, Splitzeilen, Summe gleich dem Buchungsbetrag, keine zweite Buchung daneben),
|
||
Kauf ohne Kosten als schlichte Umbuchung mit errechnetem Kurs, Verkauf mit Gebühren und
|
||
Steuern, das Löschen, das die Bankbuchung stehen lässt, sowie die Abweisungen: doppeltes
|
||
Geschäft, Kosten in Höhe der Buchung, Konto statt Depot, Kauf ohne Stückzahl. Die beiden
|
||
Dialoge prüft `tests/e2e/specs/wertpapiergeschaeft.spec.js`, die Klasse des Geschäfts
|
||
`tests/62_WertpapierKlasseTest.php`.
|
||
|
||
`tests/26_WertpapierDetailTest.php` – die Detailseite: der auf ein Papier eingegrenzte
|
||
Bestand (fremde Papiere bleiben draußen, mehrere Depots werden je Position geführt, Depot
|
||
und Papier lassen sich zugleich eingrenzen), Hoch und Tief der Kurse, die Datumsgrenzen
|
||
der Depotbuchungen, das Format der Stückzahlen sowie die gerenderte Seite: Stammdaten,
|
||
Kennzahlen und eine Buchungsliste, die dem Zeitraum folgt, während die Kennzahlen es nicht
|
||
tun. Den Klick vom Namen in der Übersicht prüft `tests/e2e/specs/wertpapierdetail.spec.js`.
|
||
|
||
`tests/21_DepotbestandImportTest.php` – der CSV-Bestandsimport an einem Ausschnitt des
|
||
echten DKB-Exports: Vorschau ohne Nebenwirkung, Einstand und Wert aus beiden Kursen,
|
||
Bruchstückzahlen, Assetklasse → Art, hinterlegte Bewertungskurse, Idempotenz beim zweiten
|
||
Lauf, Zuordnung des Depots über die Depotnummer, Geldseite mit und ohne
|
||
Verrechnungskonto, sowie die Abwehr einer fremden oder leeren Datei.
|