Finanzmanager/src/Views/buchungen/kontoblatt.php
fds2610-bot e02bac22c0 Fix n.n.: Buchungen im Kontoblatt lassen sich manuell verknüpfen
Liegen zwei Buchungen, die eigentlich derselbe Vorgang sind, zu weit
auseinander oder gehörte eine ursprünglich einem anderen Ursprungskonto,
findet der automatische Abgleich sie nicht – die Umbuchung und ihre
einzeln erfasste Dublette stehen dann dauerhaft nebeneinander im
Kontoblatt.

Buchung::verknuepfen() führt eine Umbuchung (eigene Seite oder
Gegenseite auf diesem Konto) und eine einzeln erfasste Buchung mit
passendem Betrag zusammen: Die Umbuchung bleibt, die Dublette fällt
weg, ihr Bankumsatz hängt sich an die Umbuchung. Im Kontoblatt lässt
sich dafür jetzt auch die Gegenseite einer fremden Umbuchung ankreuzen;
ein neuer Knopf „Verknüpfen“ nutzt dieselbe Auswahl wie „Auswahl
löschen“.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-27 14:53:56 +02:00

528 lines
26 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
use Finanzmanager\Models\Wertpapier;
use Finanzmanager\View;
/** @var array<string,mixed> $konto */
/** @var list<array<string,mixed>> $zeilen */
/** @var int $gesamt */
/** @var int $seite */
/** @var int $seiten */
/** @var array<string,mixed> $filter */
/** @var list<array<string,mixed>> $kategorien */
/** @var list<array<string,mixed>> $klassen */
/** @var list<array<string,mixed>> $konten */
/** @var array<int, list<array<string,mixed>>> $splits */
/** @var bool $bargeld Ist ein Bargeld-Tool eingerichtet? */
$kategorieNamen = [];
foreach ($kategorien as $k) {
$kategorieNamen[(int)$k['id']] = $k['pfad'];
}
$klassenNamen = [];
foreach ($klassen as $k) {
$klassenNamen[(int)$k['id']] = $k['name'];
}
$kontenNamen = array_column($konten, 'name', 'id');
$eigenesKonto = (int)$konto['id'];
$gefiltert = ($filter['suche'] ?? '') !== '' || !empty($filter['von']) || !empty($filter['bis'])
|| !empty($filter['kategorie_id']) || !empty($filter['klasse_id']) || !empty($filter['nur_offene']);
$seitenPfad = static function (int $nr) use ($filter): string {
$p = array_filter([
'suche' => $filter['suche'] ?? '',
'von' => $filter['von'] ?? '',
'bis' => $filter['bis'] ?? '',
'kategorie_id' => $filter['kategorie_id'] ?? '',
'klasse_id' => $filter['klasse_id'] ?? '',
'nur_offene' => !empty($filter['nur_offene']) ? '1' : '',
'seite' => (string)$nr,
], static fn(string $v): bool => $v !== '');
return '?' . http_build_query($p);
};
// Nach dem Löschen soll dieselbe Ansicht wieder erscheinen – Filter und Seite
// hängen deshalb am Ziel des Löschformulars. Dasselbe gilt für die
// Schnellzuordnung: Ihre Ziele setzt kontoblatt.js aus dieser Abfrage zusammen.
$abfrage = $seitenPfad($seite);
$loeschZiel = '/konten/' . (int)$konto['id'] . '/buchungen/loeschen' . $abfrage;
$verknuepfenZiel = '/konten/' . (int)$konto['id'] . '/buchungen/verknuepfen' . $abfrage;
/**
* Die Zuordnung einer Zeile als Wert der gemeinsamen Auswahlliste – dieselben
* Präfixe wie in der Buchungsmaske.
*/
$zuordnungswert = static function (array $zeile): string {
if ((int)($zeile['gesplittet'] ?? 0) === 1) {
return 'split';
}
if (!empty($zeile['gegenkonto_id'])) {
return 'konto:' . (int)$zeile['gegenkonto_id'];
}
if (!empty($zeile['kategorie_id'])) {
return 'kategorie:' . (int)$zeile['kategorie_id'];
}
return '';
};
/**
* Die Splitzeilen einer Buchung, wie der Dialog sie braucht. Sie hängen als
* JSON an der Zelle statt nachgeladen zu werden: Es sind die Zeilen der wenigen
* aufgeteilten Buchungen dieser Seite, und ein zweiter Weg zu denselben Daten
* wäre auch ein zweiter Weg, sie falsch zu lesen.
*
* @param list<array<string,mixed>> $splitzeilen
*/
$splitdaten = static function (array $splitzeilen) use ($zuordnungswert): string {
return (string)json_encode(array_map(static fn(array $zeile): array => [
'zuordnung' => $zuordnungswert($zeile),
'klasse_id' => (int)($zeile['klasse_id'] ?? 0),
'notiz' => (string)$zeile['notiz'],
'betrag' => View::betrag($zeile['betrag'], false),
], $splitzeilen), JSON_UNESCAPED_UNICODE);
};
/**
* Der Tooltip einer Splitbuchung: je Zeile Kategorie und Betrag. In der Spalte
* steht nur „Splitbuchung“ – wohin das Geld geht, wäre sonst erst im Dialog zu
* sehen. Notiz und Klasse bleiben draußen; sie stünden im Weg.
*
* @param list<array<string,mixed>> $splitzeilen
*/
$splituebersicht = static function (array $splitzeilen) use ($kategorieNamen, $kontenNamen): string {
$texte = [];
foreach ($splitzeilen as $zeile) {
$name = match (true) {
!empty($zeile['gegenkonto_id']) => '[' . ($kontenNamen[(int)$zeile['gegenkonto_id']] ?? '?') . ']',
!empty($zeile['kategorie_id']) => (string)($kategorieNamen[(int)$zeile['kategorie_id']] ?? '?'),
default => '(ohne Kategorie)',
};
$texte[] = $name . ': ' . View::betrag($zeile['betrag']);
}
return implode("\n", $texte);
};
/**
* Das Symbol der Abgleichspalte, siehe View::bargeldMarke() – die Schnell-
* zuordnung (kontoblatt.js) braucht dasselbe Symbol für eine einzelne Zeile,
* ohne die Seite neu zu laden.
*
* @param array<string,mixed> $zeile
*/
$bargeldmarke = static fn(array $zeile): string
=> View::bargeldMarke($zeile['bargeld_status'] ?? null, $zeile['bargeld_hinweis'] ?? null);
include __DIR__ . '/../layout/kopf.php';
?>
<div class="tableiste">
<span class="tab aktiv">Kontoblatt</span>
<a href="/konten/<?= (int)$konto['id'] ?>/bearbeiten" class="tab">Kontodaten</a>
</div>
<div class="seitenaktionen">
<a href="/konten/<?= (int)$konto['id'] ?>/buchung" class="knopf primaer">Neue Buchung</a>
<a href="/konten" class="knopf">Kontenübersicht</a>
<?php if ($zeilen !== []): ?>
<!-- Das Formular steht hier, seine Ankreuzfelder stehen in der Tabelle;
verbunden sind sie über das form-Attribut. Ein Formular um die
ganze Tabelle würde das Filterformular verschachteln. -->
<form method="post" action="<?= View::h($loeschZiel) ?>" id="buchungsauswahl" class="inline"
data-bestaetigen="Die ausgewählten Buchungen wirklich löschen?"
data-frage-eine="Die ausgewählte Buchung wirklich löschen?"
data-frage-mehrere="{anzahl} ausgewählte Buchungen wirklich löschen?">
<input type="hidden" name="csrf_token" value="<?= View::h($csrfToken ?? '') ?>">
<?php /* Der erste Knopf im Formular bleibt „Auswahl löschen“ –
kontoblatt.js/app.js greift sich per querySelector den
ersten Absende-Knopf, um ihn nach Anzahl der Auswahl zu
beschriften und zu sperren. Verknüpfen nutzt dasselbe
Formular und Ankreuzfeld, nur mit anderem Ziel: zwei
ausgewählte Buchungen, die denselben Vorgang doppelt
zeigen (eine Umbuchung und ihre einzeln erfasste
Dublette), führt es zusammen. */ ?>
<button type="submit" class="knopf gefahr">Auswahl löschen</button>
<button type="submit" formaction="<?= View::h($verknuepfenZiel) ?>" class="knopf"
data-bestaetigen="Beide ausgewählten Buchungen zu einer Umbuchung verknüpfen? Die einzeln erfasste Buchung wird dabei gelöscht.">
Verknüpfen
</button>
</form>
<?php endif; ?>
<span class="fuellung"></span>
<span class="saldoanzeige">
Aktueller Saldo
<strong class="<?= View::betragKlasse($konto['saldo_aktuell']) ?>">
<?= View::betrag($konto['saldo_aktuell']) ?>
</strong>
</span>
<?php if (abs((float)$konto['saldo_erwartet'] - (float)$konto['saldo_aktuell']) > 0.005): ?>
<span class="saldoanzeige">
Erwartet
<strong class="<?= View::betragKlasse($konto['saldo_erwartet']) ?>">
<?= View::betrag($konto['saldo_erwartet']) ?>
</strong>
</span>
<?php endif; ?>
</div>
<form method="get" action="/konten/<?= (int)$konto['id'] ?>" class="filterleiste">
<input type="search" name="suche" placeholder="Empfänger, Text, Nummer"
value="<?= View::h($filter['suche'] ?? '') ?>">
<label>von <input type="date" name="von" value="<?= View::h($filter['von'] ?? '') ?>"></label>
<label>bis <input type="date" name="bis" value="<?= View::h($filter['bis'] ?? '') ?>"></label>
<select name="kategorie_id" aria-label="Kategorie" data-kombobox>
<option value="">Alle Kategorien</option>
<?php foreach ($kategorien as $k): ?>
<option value="<?= (int)$k['id'] ?>"
<?= (int)($filter['kategorie_id'] ?? 0) === (int)$k['id'] ? 'selected' : '' ?>>
<?= View::h($k['pfad']) ?>
</option>
<?php endforeach; ?>
</select>
<select name="klasse_id">
<option value="">Alle Klassen</option>
<?php foreach ($klassen as $k): ?>
<option value="<?= (int)$k['id'] ?>"
<?= (int)($filter['klasse_id'] ?? 0) === (int)$k['id'] ? 'selected' : '' ?>>
<?= View::h($k['name']) ?>
</option>
<?php endforeach; ?>
</select>
<label class="ankreuzfeld">
<input type="checkbox" name="nur_offene" value="1" <?= !empty($filter['nur_offene']) ? 'checked' : '' ?>>
nur nicht abgeglichene
</label>
<button type="submit" class="knopf klein">Filtern</button>
<?php if ($gefiltert): ?>
<a href="/konten/<?= (int)$konto['id'] ?>" class="knopf klein">Filter zurücksetzen</a>
<?php endif; ?>
</form>
<?php if ($zeilen === []): ?>
<p class="platzhalter">
<?= $gefiltert ? 'Keine Buchung passt zum Filter.' : 'Für dieses Konto ist noch nichts gebucht.' ?>
</p>
<?php else: ?>
<table class="liste kontoblatt" data-abfrage="<?= View::h($abfrage) ?>">
<thead>
<tr>
<th class="wahlspalte">
<input type="checkbox" class="alle-waehlen" data-waehlt="buchungsauswahl"
aria-label="Alle angezeigten Buchungen auswählen">
</th>
<th>Datum</th>
<th>Nr.</th>
<th>Empfänger</th>
<th>Buchungstext</th>
<th>Kategorie</th>
<th>Klasse</th>
<?php if ($bargeld): ?>
<th class="bargeldspalte" title="Abgleich mit dem Bargeld-Tool">Bar</th>
<?php endif; ?>
<th class="rechts">Betrag</th>
<th class="rechts">Saldo</th>
<th>✓</th>
</tr>
</thead>
<tbody>
<?php foreach ($zeilen as $zeile): ?>
<tr class="<?= $zeile['gegensicht'] ? 'gegensicht' : '' ?>">
<td class="wahlspalte">
<?php
// Auch die Gegenseite einer fremden Umbuchung lässt sich
// ankreuzen – gelöscht wird sie dadurch nicht (das prüft
// der Controller anhand ihres wirklichen Kontos), aber
// verknüpfen lässt sie sich mit einer einzeln erfassten
// Dublette auf diesem Konto.
$auswahlLabel = $zeile['gegensicht']
? 'Umbuchung vom ' . View::datum($zeile['datum']) . ' auswählen'
: 'Buchung vom ' . View::datum($zeile['datum']) . ' auswählen';
?>
<input type="checkbox" name="buchungen[]" value="<?= (int)$zeile['id'] ?>"
form="buchungsauswahl"
aria-label="<?= View::h($auswahlLabel) ?>">
</td>
<td class="nowrap"><?= View::datum($zeile['datum']) ?></td>
<td class="nebentext"><?= View::h($zeile['nummer']) ?></td>
<td>
<?php /* Der Text steht in einem eigenen Kasten, nicht direkt in
der Zelle: Abgeschnitten wird über zwei Zeilen, und das
kann nur ein Element, das kein Tabellenfeld ist. Die
Marke steht neben dem Kasten – im Kasten zählte sie als
eine der beiden Zeilen. */ ?>
<?php if ($zeile['gegensicht']): ?>
<div class="zellenreihe">
<div class="zellentext"><?= View::h($zeile['empfaenger']) ?></div>
<span class="marke" title="Gegenseite einer Umbuchung – bearbeitbar auf dem Ursprungskonto">
Umbuchung
</span>
</div>
<?php else: ?>
<div class="zellentext">
<a href="/buchungen/<?= (int)$zeile['id'] ?>"><?= View::h($zeile['empfaenger'] ?: '(ohne Empfänger)') ?></a>
</div>
<?php endif; ?>
</td>
<?php /* Ungekürzt: Was nicht auf zwei Zeilen passt, schneidet das
Stylesheet ab – der vollständige Text bleibt für den
Tooltip erhalten. */ ?>
<td class="nebentext buchungstext"><div class="zellentext"><?= View::h($zeile['buchungstext']) ?></div></td>
<?php
// Die Gegenseite einer fremden Umbuchung nennt das Konto, von dem
// sie kommt – und zwar dort, wo sonst die Kategorie steht. Ohne
// diese Angabe stünde auf dem Gegenkonto eine Umbuchung ohne
// erkennbares Gegenüber.
$gegenkonto = empty($zeile['gegenkonto_id'])
? ''
: '[' . ($kontenNamen[(int)$zeile['gegenkonto_id']] ?? '?') . ']';
$beschriftung = match (true) {
(int)$zeile['gesplittet'] === 1 => 'Splitbuchung',
$gegenkonto !== '' => $gegenkonto,
default => (string)($kategorieNamen[(int)$zeile['kategorie_id']] ?? ''),
};
// Eine Splitbuchung nennt im Tooltip ihre Zeilen, alles andere
// seine eigene – womöglich abgeschnittene – Beschriftung.
$zuordnungstitel = match (true) {
isset($splits[(int)$zeile['id']]) => $splituebersicht($splits[(int)$zeile['id']]),
$beschriftung !== '' => $beschriftung,
default => 'Kategorie oder Gegenkonto zuordnen',
};
?>
<td class="zuordnungszelle">
<?php if ($zeile['gegensicht']): ?>
<?php /* Auch die Gegenseite führt zu ihrer Buchung – sie liegt
auf dem anderen Konto und wird nur dort geändert. */ ?>
<a href="/buchungen/<?= (int)$zeile['id'] ?>" class="zuordnungsfeld gegenseite"
title="Umbuchung von <?= View::h($kontenNamen[(int)$zeile['gegenkonto_id']] ?? '?') ?> – dort zu ändern">
<?= View::h($beschriftung) ?>
</a>
<?php else: ?>
<?php /* Ohne JavaScript führt der Klick in die Buchungsmaske;
kontoblatt.js macht daraus die Auswahl an Ort und Stelle. */ ?>
<?php
// Wertpapierabrechnungen führen ISIN und WKN im
// Verwendungszweck mit. Wer die Zeile ins Depot bucht,
// bekommt das Papier deshalb vorgeschlagen, statt es
// in einer langen Liste zu suchen.
$papier = Wertpapier::ausText(
(string)$zeile['buchungstext'] . ' ' . (string)$zeile['empfaenger']
);
?>
<a href="/buchungen/<?= (int)$zeile['id'] ?>" class="zuordnungsfeld"
data-buchung="<?= (int)$zeile['id'] ?>"
data-wert="<?= View::h($zuordnungswert($zeile)) ?>"
data-betrag="<?= View::h(number_format((float)$zeile['betrag'], 2, '.', '')) ?>"
data-datum="<?= View::h(substr((string)$zeile['datum'], 0, 10)) ?>"
data-zweck="<?= View::h((string)$zeile['buchungstext']) ?>"
data-isin="<?= View::h($papier['isin']) ?>"
data-wkn="<?= View::h($papier['wkn']) ?>"
data-bezeichnung="<?= View::h(View::datum($zeile['datum']) . ' · ' . ($zeile['empfaenger'] ?: '(ohne Empfänger)')) ?>"
<?php if (isset($splits[(int)$zeile['id']])): ?>
data-splits="<?= View::h($splitdaten($splits[(int)$zeile['id']])) ?>"
<?php endif; ?>
title="<?= View::h($zuordnungstitel) ?>">
<?php if ($beschriftung === 'Splitbuchung'): ?>
<span class="marke">Splitbuchung</span>
<?php else: ?>
<?= View::h($beschriftung) ?>
<?php endif; ?>
</a>
<?php endif; ?>
</td>
<?php $klassenname = (string)($klassenNamen[(int)$zeile['klasse_id']] ?? ''); ?>
<td class="klassenzelle nebentext">
<?php if ($zeile['gegensicht']): ?>
<?= View::h($klassenname) ?>
<?php else: ?>
<?php /* Wie bei der Zuordnung: Ohne JavaScript führt der Klick
in die Buchungsmaske, mit ihm öffnet sich die Auswahl
an Ort und Stelle. */ ?>
<a href="/buchungen/<?= (int)$zeile['id'] ?>" class="klassenfeld"
data-buchung="<?= (int)$zeile['id'] ?>"
data-klasse="<?= (int)$zeile['klasse_id'] ?>"
title="<?= View::h($klassenname !== '' ? $klassenname : 'Klasse zuordnen') ?>">
<?= View::h($klassenname) ?>
</a>
<?php endif; ?>
</td>
<?php if ($bargeld): ?>
<td class="bargeldspalte"><?= $bargeldmarke($zeile) ?></td>
<?php endif; ?>
<td class="rechts <?= View::betragKlasse($zeile['betrag']) ?>"><?= View::betrag($zeile['betrag']) ?></td>
<td class="rechts <?= $zeile['saldo'] === null ? 'nebentext' : View::betragKlasse($zeile['saldo']) ?>">
<?= $zeile['saldo'] === null ? '–' : View::betrag($zeile['saldo']) ?>
</td>
<td class="nebentext"><?= (int)$zeile['abgeglichen'] === 1 ? '✓' : '' ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="blaettern">
<span class="nebentext"><?= (int)$gesamt ?> Buchungen</span>
<?php if ($seiten > 1): ?>
<?php if ($seite > 1): ?>
<a href="<?= View::h($seitenPfad($seite - 1)) ?>" class="knopf klein">Neuer</a>
<?php endif; ?>
<span class="nebentext">Seite <?= (int)$seite ?> von <?= (int)$seiten ?></span>
<?php if ($seite < $seiten): ?>
<a href="<?= View::h($seitenPfad($seite + 1)) ?>" class="knopf klein">Älter</a>
<?php endif; ?>
<?php endif; ?>
</div>
<?php /* Die Auswahlliste gibt es einmal; kontoblatt.js hängt sie in die
angeklickte Zelle. Eine Liste je Zeile wären bei hundert Zeilen
ebenso viele vollständige Kategoriebäume. */ ?>
<form method="post" action="" id="zuordnungsauswahl" class="zuordnungsauswahl" hidden>
<input type="hidden" name="csrf_token" value="<?= View::h($csrfToken) ?>">
<select name="zuordnung" aria-label="Kategorie oder Gegenkonto" data-kombobox>
<?php $gewaehlt = ''; $mitSplit = true; $mitDepot = true; ?>
<?php include __DIR__ . '/zuordnungsliste.php'; ?>
</select>
<noscript><button type="submit" class="knopf klein">Übernehmen</button></noscript>
</form>
<?php /* Dieselbe Bauart für die Klassenspalte: eine Liste, die in die
angeklickte Zelle wandert. Der erste Eintrag löscht die Klasse. */ ?>
<form method="post" action="" id="klassenauswahl" class="zuordnungsauswahl" hidden>
<input type="hidden" name="csrf_token" value="<?= View::h($csrfToken) ?>">
<select name="klasse_id" aria-label="Klasse">
<option value="">— keine —</option>
<?php foreach ($klassen as $k): ?>
<option value="<?= (int)$k['id'] ?>"><?= View::h($k['name']) ?></option>
<?php endforeach; ?>
</select>
<noscript><button type="submit" class="knopf klein">Übernehmen</button></noscript>
</form>
<dialog id="splitdialog" class="splitdialog">
<form method="post" action="" id="splitformular">
<input type="hidden" name="csrf_token" value="<?= View::h($csrfToken) ?>">
<h2>Splitbuchung</h2>
<p class="feldhinweis">
<span id="splitbezeichnung"></span> über
<output id="splitbuchungsbetrag" class="betrag">0,00</output>
</p>
<table class="liste splittabelle">
<thead>
<tr>
<th>Kategorie / Gegenkonto</th><th>Klasse</th><th>Notiz</th>
<th class="rechts">Betrag</th>
</tr>
</thead>
<tbody id="splitdialogzeilen">
<?php for ($nr = 0; $nr < 4; $nr++): ?>
<tr>
<td>
<select name="split[<?= $nr ?>][zuordnung]">
<?php $gewaehlt = ''; $mitSplit = false; $mitDepot = false; ?>
<?php include __DIR__ . '/zuordnungsliste.php'; ?>
</select>
</td>
<td>
<select name="split[<?= $nr ?>][klasse_id]">
<option value="">— keine —</option>
<?php foreach ($klassen as $k): ?>
<option value="<?= (int)$k['id'] ?>"><?= View::h($k['name']) ?></option>
<?php endforeach; ?>
</select>
</td>
<td><input type="text" name="split[<?= $nr ?>][notiz]" value=""></td>
<td>
<input type="text" class="splitbetrag rechts" inputmode="decimal"
name="split[<?= $nr ?>][betrag]" value="">
</td>
</tr>
<?php endfor; ?>
</tbody>
<tfoot>
<tr>
<td colspan="3" class="rechts">Summe der Zeilen / Restbetrag</td>
<td class="rechts"><output id="splitdialogsumme" class="betrag">0,00</output></td>
</tr>
</tfoot>
</table>
<p class="feldhinweis">
Die Summe der Zeilen muss dem Buchungsbetrag entsprechen. Bleiben alle
Zeilen leer, wird die Splitbuchung aufgehoben.
</p>
<div class="knopfleiste">
<button type="submit" class="knopf primaer">Splitbuchung speichern</button>
<button type="button" class="knopf" id="splitzeileMehr">Zeile hinzufügen</button>
<button type="button" class="knopf" id="splitabbrechen">Abbrechen</button>
<?php /* Rechts außen, abseits der übrigen Knöpfe: hebt die Aufteilung
samt aller Zuordnungen auf. Nur sichtbar, wenn es etwas
aufzuheben gibt – kontoblatt.js blendet ihn ein. */ ?>
<button type="submit" class="knopf gefahr" id="splitloeschen"
name="aktion" value="loeschen" formnovalidate hidden>
Splitbuchung löschen
</button>
</div>
</form>
</dialog>
<?php /* Ein Wertpapiergeschäft entsteht dort, wo seine Abrechnung ankommt:
am Girokonto. Die Auswahl eines Depots in der Zuordnungsliste öffnet
diesen Dialog; die Nebenkosten liegen dahinter im zweiten. */ ?>
<dialog id="wertpapierdialog" class="splitdialog wertpapierdialog">
<form method="post" action="" id="wertpapierformular" class="formular">
<input type="hidden" name="csrf_token" value="<?= View::h($csrfToken) ?>">
<input type="hidden" name="depot_id" id="wp_depot" value="">
<h2>Wertpapiergeschäft</h2>
<p class="feldhinweis">
<span id="wp_bezeichnung"></span> über
<output id="wp_buchungsbetrag" class="betrag">0,00</output> ·
Depot <strong id="wp_depotname"></strong>
</p>
<p class="feldhinweis verwendungszweck" id="wp_verwendungszweck"></p>
<?php $datumsvorgabe = date('Y-m-d'); ?>
<?php $vorschlag = ['isin' => '', 'wkn' => '']; ?>
<?php include __DIR__ . '/wertpapierfelder.php'; ?>
<div class="knopfleiste">
<button type="submit" class="knopf primaer">Geschäft buchen</button>
<button type="button" class="knopf" id="wp_kostenknopf">
Kosten … (<output id="wp_kostenknopfsumme" class="betrag">0,00</output>)
</button>
<button type="button" class="knopf" id="wp_abbrechen">Abbrechen</button>
<span class="fuellung"></span>
<span class="saldoanzeige">
Einstandspreis <strong><output id="wp_einstand" class="betrag">0,00</output></strong>
</span>
</div>
</form>
</dialog>
<?php /* Die Kostenfelder gehören zum Formular oben, stehen aber in einem
eigenen Dialog – verbunden über das form-Attribut, wie schon die
Ankreuzfelder der Sammellöschung. */ ?>
<dialog id="kostendialog" class="splitdialog">
<h2>Kosten</h2>
<?php $kostenFormular = 'wertpapierformular'; ?>
<?php include __DIR__ . '/wertpapierkosten.php'; ?>
<div class="knopfleiste">
<button type="button" class="knopf primaer" id="wp_kostenfertig">Übernehmen</button>
</div>
</dialog>
<script src="/static/betrag.js" defer></script>
<script src="/static/wertpapierbuchung.js" defer></script>
<script src="/static/kombobox.js" defer></script>
<script src="/static/kontoblatt.js" defer></script>
<?php endif; ?>
<?php include __DIR__ . '/../layout/fuss.php'; ?>