Der Plan ging davon aus, Umbuchungen stünden zweimal im Export und müssten zusammengeführt werden. Der Vollexport zeigt das Gegenteil: Eine Umbuchung steht einmal, in dem Konto, in dem sie erfasst wurde; die Gegenseite ist nur durch L[Zielkonto] benannt und muss beim Import erzeugt werden. Von 965 Umbuchungen hat keine ein zweites Vorkommen. Mit erzeugter Gegenseite stimmen acht gegen den FinanzManager geprüfte Kontosalden auf den Cent. Datenmodell: Migration 003 mit buchungen, buchung_splits und buchungsadressen. Eine Umbuchung ist ein Datensatz mit gegenkonto_id, kein gespiegeltes Paar - zwei Zeilen laufen früher oder später auseinander. Das Kontoblatt des Gegenkontos zeigt sie mit umgekehrtem Vorzeichen. Auch einzelne Splitzeilen dürfen ein Gegenkonto tragen. Konten führen zusätzlich in_summe, die Sigma-Spalte der Kontenübersicht, und saldo_online für Phase 5. Oberfläche: Kontoblatt mit laufendem Saldo, Filter auf Zeitraum, Kategorie, Klasse und Abgleich, Volltextsuche und Seitenblättern. Buchungsmaske für Einnahme, Ausgabe und Umbuchung in beide Richtungen, mit Splitzeilen und Restbetragsanzeige. Import: QifBuchungsImport arbeitet mit Stichtag. Alles davor wird je Konto zum Anfangssaldo aufsummiert, Gegenbuchungen eingerechnet; alles danach bis zum Importtag kommt einzeln in die Datenbank. Spätere Buchungen sind Vorausberechnungen des FinanzManagers und bleiben draußen. Wertpapiergeschäfte schlagen vorerst nur mit ihrem Geldfluss auf dem Verrechnungskonto zu Buche. Am Vollexport geprüft: 833 Buchungen ab 01.01.2026, 7946 in die Anfangssalden von 35 Konten verrechnet. Behoben: Die confirm()-Rückfragen vor dem Löschen waren wirkungslos. public/static/app.js brachte den Mechanismus mit, war aber nie im Layout eingebunden; die ersatzweise gesetzten onsubmit-Attribute verstoßen gegen die eigene Content-Security-Policy und wurden vom Browser verworfen - gelöscht wurde ohne Rückfrage. Ein E2E-Test besteht jetzt auf dem Dialog. Tests: 307 PHP-Prüfungen und 21 Playwright-Tests, alle grün. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
118 lines
5.1 KiB
PHP
118 lines
5.1 KiB
PHP
<?php
|
||
|
||
use Finanzmanager\Models\Konto;
|
||
use Finanzmanager\View;
|
||
|
||
/** @var array<string,mixed>|null $konto */
|
||
/** @var list<array<string,mixed>> $gruppen */
|
||
/** @var string $csrfToken */
|
||
|
||
$neu = $konto === null;
|
||
$ziel = $neu ? '/konten/neu' : '/konten/' . (int)$konto['id'] . '/bearbeiten';
|
||
$wert = static fn(string $feld, string $standard = ''): string
|
||
=> View::h((string)($konto[$feld] ?? $standard));
|
||
|
||
include __DIR__ . '/../layout/kopf.php';
|
||
?>
|
||
|
||
<div class="seitenaktionen">
|
||
<a href="/konten" class="knopf">Zurück zur Kontenübersicht</a>
|
||
</div>
|
||
|
||
<form method="post" action="<?= View::h($ziel) ?>" class="formular breit">
|
||
<input type="hidden" name="csrf_token" value="<?= View::h($csrfToken) ?>">
|
||
|
||
<label for="name">Name des Kontos</label>
|
||
<input type="text" id="name" name="name" value="<?= $wert('name') ?>" required autofocus>
|
||
|
||
<label for="kontoart">Kontoart</label>
|
||
<select id="kontoart" name="kontoart">
|
||
<?php foreach (Konto::ARTEN as $schluessel => $beschriftung): ?>
|
||
<option value="<?= View::h($schluessel) ?>"
|
||
<?= ($konto['kontoart'] ?? 'bank') === $schluessel ? 'selected' : '' ?>>
|
||
<?= View::h($beschriftung) ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
|
||
<label for="kontengruppe_id">Kontengruppe</label>
|
||
<select id="kontengruppe_id" name="kontengruppe_id">
|
||
<option value="">Nicht gruppiert</option>
|
||
<?php foreach ($gruppen as $gruppe): ?>
|
||
<option value="<?= (int)$gruppe['id'] ?>"
|
||
<?= (int)($konto['kontengruppe_id'] ?? 0) === (int)$gruppe['id'] ? 'selected' : '' ?>>
|
||
<?= View::h($gruppe['name']) ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
|
||
<label for="bankname">Bank <span class="optional">(optional)</span></label>
|
||
<input type="text" id="bankname" name="bankname" value="<?= $wert('bankname') ?>">
|
||
|
||
<label for="kontonummer">Kontonummer <span class="optional">(optional)</span></label>
|
||
<input type="text" id="kontonummer" name="kontonummer" value="<?= $wert('kontonummer') ?>">
|
||
|
||
<label for="iban">IBAN <span class="optional">(optional)</span></label>
|
||
<input type="text" id="iban" name="iban" value="<?= $wert('iban') ?>">
|
||
|
||
<label for="bic">BIC <span class="optional">(optional)</span></label>
|
||
<input type="text" id="bic" name="bic" value="<?= $wert('bic') ?>">
|
||
|
||
<label for="anfangssaldo">Anfangssaldo</label>
|
||
<input type="text" id="anfangssaldo" name="anfangssaldo" inputmode="decimal"
|
||
value="<?= View::h(View::betrag($konto['anfangssaldo'] ?? 0, false)) ?>">
|
||
<p class="feldhinweis">
|
||
Kontostand vor der ersten erfassten Buchung. Der QIF-Export enthält ihn nicht –
|
||
nach einem Import wird er hier je Konto nachgetragen.
|
||
</p>
|
||
|
||
<label for="kreditlimit">Kreditlimit <span class="optional">(optional)</span></label>
|
||
<input type="text" id="kreditlimit" name="kreditlimit" inputmode="decimal"
|
||
value="<?= $konto === null || $konto['kreditlimit'] === null
|
||
? '' : View::h(View::betrag($konto['kreditlimit'], false)) ?>">
|
||
|
||
<label for="beschreibung">Beschreibung <span class="optional">(optional)</span></label>
|
||
<input type="text" id="beschreibung" name="beschreibung" value="<?= $wert('beschreibung') ?>">
|
||
|
||
<label for="sortierung">Sortierung</label>
|
||
<input type="number" id="sortierung" name="sortierung" value="<?= $wert('sortierung', '0') ?>" step="1">
|
||
<p class="feldhinweis">Kleinere Werte stehen in der Kontenübersicht weiter oben.</p>
|
||
|
||
<label class="ankreuzfeld">
|
||
<input type="checkbox" name="aktiv" value="1" <?= $neu || (int)$konto['aktiv'] === 1 ? 'checked' : '' ?>>
|
||
Konto ist aktiv
|
||
</label>
|
||
|
||
<label class="ankreuzfeld">
|
||
<input type="checkbox" name="in_summe" value="1"
|
||
<?= $neu || (int)$konto['in_summe'] === 1 ? 'checked' : '' ?>>
|
||
Zählt zur Summe der Kontostände
|
||
</label>
|
||
<p class="feldhinweis">
|
||
Entspricht der Σ-Spalte im FinanzManager. Konten, die kein eigenes Vermögen
|
||
darstellen – durchlaufende Auslagen etwa –, bleiben so aus der Gesamtsumme heraus,
|
||
erscheinen aber weiter in der Übersicht.
|
||
</p>
|
||
|
||
<div class="knopfleiste">
|
||
<button type="submit" class="knopf primaer"><?= $neu ? 'Konto anlegen' : 'Speichern' ?></button>
|
||
<a href="/konten" class="knopf">Abbrechen</a>
|
||
</div>
|
||
</form>
|
||
|
||
<?php if (!$neu): ?>
|
||
<form method="post" action="/konten/<?= (int)$konto['id'] ?>/loeschen" class="formular breit"
|
||
data-bestaetigen="Konto „<?= View::h($konto['name']) ?>“ wirklich löschen?">
|
||
<input type="hidden" name="csrf_token" value="<?= View::h($csrfToken) ?>">
|
||
<h3>Konto löschen</h3>
|
||
<p class="feldhinweis">
|
||
Ein Konto, das nur nicht mehr genutzt wird, sollte auf inaktiv gesetzt statt
|
||
gelöscht werden – gelöschte Konten nehmen später ihre Buchungen mit.
|
||
</p>
|
||
<div class="knopfleiste">
|
||
<button type="submit" class="knopf gefahr">Konto löschen</button>
|
||
</div>
|
||
</form>
|
||
<?php endif; ?>
|
||
|
||
<?php include __DIR__ . '/../layout/fuss.php'; ?>
|