Finanzmanager/src/Views/verwaltung/teil-import-csv.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

127 lines
4.9 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\Services\BuchungsExport;
use Finanzmanager\View;
/**
* Ergebnis und Erläuterungen des CSV-Imports. Eingebunden aus import.php,
* das die Datei- und Uploadkacheln beisteuert.
*/
/** @var array<string,mixed>|null $buchungen */
/** @var array<string,mixed>|null $ueberblick */
/** @var string $datei */
?>
<?php if ($buchungen !== null): ?>
<div class="kachel">
<header>
<h2><?= $buchungen['trockenlauf'] ? 'Trockenlauf' : 'Import abgeschlossen' ?></h2>
<span class="phasenmarke"><?= View::h($datei) ?></span>
</header>
<?php if ($buchungen['trockenlauf']): ?>
<p class="hinweis">
Nichts wurde gespeichert. Die Zahlen zeigen, was ein Import anlegen würde.
</p>
<?php endif; ?>
<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 – am Fingerabdruck erkannt</td>
<td class="rechts nebentext"><?= (int)$buchungen['buchungen']['vorhanden'] ?></td>
</tr>
<tr>
<td class="nebentext">darunter Splitzeilen</td>
<td class="rechts nebentext"><?= (int)$buchungen['buchungen']['splitzeilen'] ?></td>
</tr>
<tr>
<td>Fehler</td>
<td class="rechts <?= (int)$buchungen['buchungen']['fehler'] > 0 ? 'betrag negativ' : 'nebentext' ?>">
<?= (int)$buchungen['buchungen']['fehler'] ?>
</td>
</tr>
</tbody>
</table>
<?php if (!empty($ueberblick['konten'])): ?>
<h3>Konten in der Datei</h3>
<p class="hinweis">
<?php $teile = [];
foreach ($ueberblick['konten'] as $name => $anzahl) {
$teile[] = View::h((string)$name) . ': ' . (int)$anzahl;
}
echo implode(' · ', $teile); ?>
</p>
<?php endif; ?>
<?php if (!empty($buchungen['meldungen'])): ?>
<h3>Protokoll</h3>
<ul class="protokoll">
<?php foreach ($buchungen['meldungen'] as $meldung): ?>
<li class="<?= View::h($meldung['typ']) ?>"><?= View::h($meldung['text']) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<div class="knopfleiste">
<a href="/konten" class="knopf primaer">Kontenübersicht</a>
<a href="/buchungen" class="knopf">Buchungen</a>
</div>
</div>
<?php endif; ?>
<div class="kachel">
<header><h2>So muss die CSV aussehen</h2></header>
<p class="hinweis">
Semikolon als Trenner, Komma als Dezimalzeichen, Datum als <code>TT.MM.JJJJ</code> –
genau so, wie der Export sie schreibt. Eine Datei aus einem anderen Programm darf die
Spalten umsortieren: Zugeordnet wird über die Kopfzeile, nicht über die Position.
Unverzichtbar sind <strong>Datum</strong>, <strong>Konto</strong> und
<strong>Betrag</strong>.
</p>
<table class="liste">
<thead>
<tr><?php foreach (BuchungsExport::SPALTEN as $spalte): ?>
<th><?= View::h($spalte) ?></th>
<?php endforeach; ?></tr>
</thead>
<tbody>
<tr>
<td>Buchung</td><td>18.03.2026</td><td></td><td>Girokonto</td><td>042</td>
<td>Stadtwerke</td><td>Abschlag März</td><td>Wohnen:Strom</td><td>Haus 1</td>
<td></td><td class="rechts">-84,00</td><td>ja</td>
</tr>
<tr>
<td>Buchung</td><td>20.03.2026</td><td></td><td>Girokonto</td><td></td>
<td>Eigenübertrag</td><td>Rücklage</td><td></td><td></td>
<td>Tagesgeld</td><td class="rechts">-500,00</td><td>nein</td>
</tr>
<tr class="nebentext">
<td>Splitzeile</td><td></td><td></td><td></td><td></td>
<td></td><td>Anteil Wasser</td><td>Wohnen:Wasser</td><td>Haus 1</td>
<td></td><td class="rechts">-30,00</td><td></td>
</tr>
</tbody>
</table>
<p class="hinweis">
Eine <strong>Umbuchung</strong> trägt statt der Kategorie ein Gegenkonto und steht –
wie im Datenmodell – nur einmal in der Datei, beim erfassenden Konto.
<strong>Splitzeilen</strong> folgen unmittelbar auf ihre Buchung; ihre Beträge müssen
deren Summe ergeben. Konten legt der Import nicht an, Kategorien und Klassen schon.
</p>
<p class="hinweis">
Was schon vorhanden ist, wird übersprungen: Gleiches Konto, Datum, Betrag, Nummer und
Buchungstext gelten als dieselbe Buchung. Ein zweiter Lauf derselben Datei ändert also
nichts.
</p>
</div>