Finanzmanager/src/Views/verwaltung/teil-import-qif.php
fds2610-bot 2c86e8522b Import und Export in QIF und CSV, Housekeeping für Importdateien
Aus der Kachel QIF-Import wird Import/Export mit je einem Knopf für beide
Richtungen; davor steht die Wahl des Dateiformats (QIF oder CSV), die als
Dialog und ohne Skript als eigene Seite funktioniert.

- CsvBuchungsImport/CsvTabelle: Buchungen samt Splitzeilen, Spalten über die
  Kopfzeile, dieselbe Dublettenerkennung und der Trockenlauf wie beim QIF.
- BuchungsExport/ExportController: Filter nach Zeitraum, Konto, Kategorie und
  Klasse; die Datei entsteht im Speicher, ein Exportverzeichnis gibt es nicht.
- Housekeeping: Hochgeladene Importdateien und data/tmp werden nach der
  eingestellten Frist gelöscht (Vorgabe 14 Tage, 0 schaltet ab).

Migration 013, Test 29 sowie E2E-Spec importexport.spec.js.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 23:01:30 +02:00

244 lines
11 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\View;
/**
* Ergebnis und Erläuterungen des QIF-Imports. Eingebunden aus import.php,
* das die Datei- und Uploadkacheln beisteuert.
*/
/** @var array<string,mixed>|null $ergebnis */
/** @var array<string,mixed>|null $buchungen */
/** @var array<string,mixed>|null $vorlagen */
/** @var string $stichtag */
/** @var string $bis */
/** @var string $datei */
/** @var array<string,int> $dateikonten */
/** @var list<string> $auswahl */
/** @var string $csrfToken */
$bereiche = [
'klassen' => 'Klassen',
'kategorien' => 'Kategorien',
'konten' => 'Konten',
];
?>
<?php if ($ergebnis !== null): ?>
<div class="kachel">
<header>
<h2><?= $ergebnis['trockenlauf'] ? 'Trockenlauf' : 'Import abgeschlossen' ?></h2>
<span class="phasenmarke"><?= View::h($datei) ?></span>
</header>
<?php if ($ergebnis['trockenlauf']): ?>
<p class="hinweis">
Nichts wurde gespeichert. Die Zahlen zeigen, was ein Import anlegen würde.
</p>
<?php endif; ?>
<table class="liste">
<thead>
<tr>
<th>Bereich</th>
<th class="rechts"><?= $ergebnis['trockenlauf'] ? 'Neu' : 'Angelegt' ?></th>
<th class="rechts">Schon vorhanden</th>
<th class="rechts">Fehler</th>
</tr>
</thead>
<tbody>
<?php foreach ($bereiche as $schluessel => $beschriftung): ?>
<tr>
<td><?= View::h($beschriftung) ?></td>
<td class="rechts"><?= (int)$ergebnis[$schluessel]['neu'] ?></td>
<td class="rechts nebentext"><?= (int)$ergebnis[$schluessel]['vorhanden'] ?></td>
<td class="rechts <?= (int)$ergebnis[$schluessel]['fehler'] > 0 ? 'betrag negativ' : 'nebentext' ?>">
<?= (int)$ergebnis[$schluessel]['fehler'] ?>
</td>
</tr>
<?php endforeach; ?>
<tr>
<td>Kontengruppen</td>
<td class="rechts nebentext" colspan="3">
nicht im QIF-Export enthalten – <a href="/verwaltung/kontengruppen">hier zuordnen</a>
</td>
</tr>
</tbody>
</table>
<p class="hinweis">
Der FinanzManager exportiert je Konto nur Name, Kontoart, Beschreibung und
Kreditlimit. Die Zuordnung zu einer Kontengruppe steht weder im QIF noch in der
<code>QSD</code>-Datei; sie wird deshalb einmalig in der Verwaltung nachgetragen.
</p>
<?php if ($buchungen !== null): ?>
<h3>Buchungen</h3>
<table class="liste">
<tbody>
<tr>
<td><?= $buchungen['trockenlauf'] ? 'Neu' : 'Angelegt' ?></td>
<td class="rechts"><?= (int)$buchungen['buchungen']['neu'] ?></td>
</tr>
<tr>
<td class="nebentext">schon vorhanden</td>
<td class="rechts nebentext"><?= (int)$buchungen['buchungen']['vorhanden'] ?></td>
</tr>
<tr>
<td class="nebentext">
vor dem Stichtag – in den Anfangssaldo von
<?= (int)$buchungen['anfangssalden']['konten'] ?> Konten verrechnet
</td>
<td class="rechts nebentext"><?= (int)$buchungen['buchungen']['vor_stichtag'] ?></td>
</tr>
<tr>
<td class="nebentext">nach dem Enddatum – vorausberechnet, verworfen</td>
<td class="rechts nebentext"><?= (int)$buchungen['buchungen']['zukunft'] ?></td>
</tr>
<tr>
<td class="nebentext">darunter Wertpapiergeschäfte (Depot- und Geldbuchung)</td>
<td class="rechts nebentext"><?= (int)$buchungen['buchungen']['wertpapier'] ?></td>
</tr>
<?php if ((int)($buchungen['buchungen']['nicht_gewaehlt'] ?? 0) > 0): ?>
<tr>
<td class="nebentext">gehört zu keinem der gewählten Konten</td>
<td class="rechts nebentext"><?= (int)$buchungen['buchungen']['nicht_gewaehlt'] ?></td>
</tr>
<?php endif; ?>
<tr>
<td>Fehler</td>
<td class="rechts <?= (int)$buchungen['buchungen']['fehler'] > 0 ? 'betrag negativ' : 'nebentext' ?>">
<?= (int)$buchungen['buchungen']['fehler'] ?>
</td>
</tr>
</tbody>
</table>
<p class="hinweis">
Zeitraum <?= View::datum($buchungen['stichtag']) ?> bis <?= View::datum($buchungen['bis']) ?>.
Alles davor ist nicht verloren: es steckt im Anfangssaldo der Konten.
<?php if (($buchungen['konten'] ?? null) !== null): ?>
Übernommen wurden nur die Buchungen von
<strong><?= View::h(implode(', ', $buchungen['konten'])) ?></strong> – einschließlich
der Umbuchungen, die auf einem anderen Konto erfasst wurden und hierher zeigen.
Die Anfangssalden der übrigen Konten bleiben unangetastet.
<?php endif; ?>
</p>
<?php endif; ?>
<?php if (($vorlagen ?? null) !== null): ?>
<h3>Buchungsvorlagen</h3>
<table class="liste">
<tbody>
<tr>
<td><?= $buchungen !== null && $buchungen['trockenlauf'] ? 'Neu' : 'Angelegt' ?></td>
<td class="rechts"><?= (int)$vorlagen['neu'] ?></td>
</tr>
<tr>
<td class="nebentext">bereits vorhanden</td>
<td class="rechts nebentext"><?= (int)$vorlagen['vorhanden'] ?></td>
</tr>
<tr>
<td>Fehler</td>
<td class="rechts <?= (int)$vorlagen['fehler'] > 0 ? 'betrag negativ' : 'nebentext' ?>">
<?= (int)$vorlagen['fehler'] ?>
</td>
</tr>
</tbody>
</table>
<p class="hinweis">
Der FinanzManager merkt sich jede erfasste Zahlung als Vorlage – im
Beispielexport sind das mehrere hundert. Sie stehen danach unter
<a href="/vorlagen">Buchungsvorlagen</a>.
</p>
<?php endif; ?>
<?php if (!empty($ergebnis['abschnitte'])): ?>
<h3>Inhalt der Datei</h3>
<p class="hinweis">
<?php $teile = [];
foreach ($ergebnis['abschnitte'] as $typ => $anzahl) {
$teile[] = View::h((string)$typ) . ': ' . (int)$anzahl;
}
echo implode(' · ', $teile); ?>
</p>
<?php endif; ?>
<?php if (!empty($ergebnis['meldungen'])): ?>
<h3>Protokoll</h3>
<ul class="protokoll">
<?php foreach ($ergebnis['meldungen'] as $meldung): ?>
<li class="<?= View::h($meldung['typ']) ?>"><?= View::h($meldung['text']) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php if ($ergebnis['trockenlauf']): ?>
<form method="post" action="/verwaltung/import/ausfuehren" class="formular">
<input type="hidden" name="csrf_token" value="<?= View::h($csrfToken) ?>">
<input type="hidden" name="datei" value="<?= View::h($datei) ?>">
<input type="hidden" name="stichtag" value="<?= View::h($stichtag) ?>">
<input type="hidden" name="bis" value="<?= View::h($bis) ?>">
<?php if ($dateikonten !== []): ?>
<h3>Konten</h3>
<p class="feldhinweis">
Zur Wahl stehen die Konten, die in dieser Datei vorkommen. Ohne Häkchen
zählt die ganze Datei. Eine Umbuchung kommt mit, sobald eines der beiden
Konten gewählt ist – sie steht im Export nur einmal, beim erfassenden
Konto. Die Zahl nennt die Einträge, die das Konto berühren.
</p>
<div class="auswahlliste">
<?php foreach ($dateikonten as $name => $anzahl): ?>
<label class="ankreuzfeld">
<input type="checkbox" name="konten[]" value="<?= View::h((string)$name) ?>"
<?= $auswahl === [] || in_array((string)$name, $auswahl, true) ? 'checked' : '' ?>>
<span>
<?= View::h((string)$name) ?>
<span class="nebentext"><?= (int)$anzahl ?></span>
</span>
</label>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div class="knopfleiste">
<button type="submit" name="modus" value="import" class="knopf primaer">Jetzt übernehmen</button>
<?php if ($dateikonten !== []): ?>
<button type="submit" name="modus" value="vorschau" class="knopf">
Trockenlauf mit dieser Auswahl
</button>
<?php endif; ?>
</div>
</form>
<?php else: ?>
<div class="knopfleiste">
<a href="/konten" class="knopf primaer">Kontenübersicht</a>
<a href="/verwaltung/kontengruppen" class="knopf">Kontengruppen</a>
<a href="/verwaltung/kategorien" class="knopf">Kategorien</a>
</div>
<p class="hinweis">
Die Anfangssalden wurden aus dem Zeitraum vor dem Stichtag berechnet. In der
Kontenübersicht lässt sich jeder Wert nachträglich ändern – ebenso das
Kennzeichen, ob ein Konto zur Summe der Kontostände zählt.
</p>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="kachel">
<header><h2>So läuft der QIF-Import</h2></header>
<p class="hinweis">
Übernommen werden Buchungen ab <strong><?= View::datum($stichtag) ?></strong> bis
<strong><?= View::datum($bis) ?></strong>. Alles davor fließt in den Anfangssaldo der
Konten, alles danach gilt als Vorausberechnung des FinanzManagers und bleibt draußen.
Der Stichtag steht in den <a href="/einstellungen?bereich=import">Einstellungen</a>.
</p>
<p class="hinweis">
Soll nur ein Teil der Konten übernommen werden, zuerst den <strong>Trockenlauf</strong>
starten: danach steht fest, welche Konten die Datei führt, und sie stehen einzeln zur
Wahl. <strong>Importieren</strong> übernimmt die ganze Datei – Klassen, Kategorien,
Konten, Buchungen und Buchungsvorlagen in einem Zug.
</p>
</div>