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>
127 lines
4.9 KiB
PHP
127 lines
4.9 KiB
PHP
<?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>
|