Steuerkram/src/Views/bank/index.php

234 lines
12 KiB
PHP

<?php
use Steuerkram\View;
$h = fn($v) => View::h($v);
$fmt = fn($v) => View::formatMoney($v);
$kTextClr = fn(string $hex): string => (0.299*hexdec(substr($hex,1,2))+0.587*hexdec(substr($hex,3,2))+0.114*hexdec(substr($hex,5,2))) > 128 ? '#1e293b' : '#ffffff';
$months = ['Jan','Feb','Mär','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez'];
?>
<!-- Filter-Leiste -->
<form method="get" action="/bank" class="card mb-2">
<div class="card-body" style="padding:.6rem 1rem">
<div style="display:flex;gap:.75rem;align-items:center;flex-wrap:wrap">
<input type="text" name="search"
placeholder="Suche…"
value="<?= $h($filter['search'] ?? '') ?>"
style="flex:1;min-width:160px;max-width:280px">
<select name="year" style="width:90px">
<option value="">Jahr</option>
<?php for ($y = (int)date('Y'); $y >= (int)date('Y') - 5; $y--): ?>
<option value="<?= $y ?>"<?= ($filter['year'] ?? '') == $y ? ' selected' : '' ?>><?= $y ?></option>
<?php endfor; ?>
</select>
<select name="month" style="width:80px">
<option value="">Monat</option>
<?php foreach ($months as $i => $m): ?>
<option value="<?= $i+1 ?>"<?= ($filter['month'] ?? '') == $i+1 ? ' selected' : '' ?>><?= $m ?></option>
<?php endforeach; ?>
</select>
<?php
$selFmt = (int)($filter['format_id'] ?? 0);
$selFmtRow = null;
foreach ($formats as $f) { if ((int)$f['id'] === $selFmt) { $selFmtRow = $f; break; } }
?>
<div class="bank-fmt-filter" style="position:relative">
<input type="hidden" name="format_id" id="filter-format-id" value="<?= $selFmt ?: '' ?>">
<button type="button" id="filter-format-btn"
style="display:inline-flex;align-items:center;gap:.4rem;height:32px;padding:0 .6rem;border:1px solid var(--color-border);border-radius:var(--radius);background:#fff;cursor:pointer;white-space:nowrap;font-size:.875rem">
<?php if ($selFmtRow): ?>
<?php $fc = $selFmtRow['farbe'] ?? '#6366f1'; ?>
<span class="kuerzel-badge" style="background:<?= $h($fc) ?>;color:<?= $kTextClr($fc) ?>"><?= $h($selFmtRow['kuerzel']) ?></span>
<?= $h($selFmtRow['name']) ?>
<?php else: ?>
<span style="color:var(--color-muted)">Alle Konten</span>
<?php endif; ?>
</button>
<div id="filter-format-drop"
style="display:none;position:absolute;top:100%;left:0;margin-top:2px;z-index:200;background:#fff;border:1px solid var(--color-border);border-radius:var(--radius);box-shadow:0 4px 12px rgba(0,0,0,.08);min-width:180px;padding:.3rem 0">
<div class="fmt-opt" data-id="" style="display:flex;align-items:center;gap:.5rem;padding:.35rem .75rem;cursor:pointer;font-size:.875rem">
<span style="color:var(--color-muted)">Alle Konten</span>
</div>
<?php foreach ($formats as $f):
$fc = $f['farbe'] ?? '#6366f1'; ?>
<div class="fmt-opt" data-id="<?= (int)$f['id'] ?>"
style="display:flex;align-items:center;gap:.5rem;padding:.35rem .75rem;cursor:pointer;font-size:.875rem<?= $selFmt === (int)$f['id'] ? ';background:var(--color-bg)' : '' ?>">
<span class="kuerzel-badge" style="background:<?= $h($fc) ?>;color:<?= $kTextClr($fc) ?>"><?= $h($f['kuerzel']) ?></span>
<?= $h($f['name']) ?>
</div>
<?php endforeach; ?>
</div>
</div>
<select name="typ" style="width:110px">
<option value="">Alle</option>
<option value="einnahme"<?= ($filter['typ'] ?? '') === 'einnahme' ? ' selected' : '' ?>>Einnahmen</option>
<option value="ausgabe"<?= ($filter['typ'] ?? '') === 'ausgabe' ? ' selected' : '' ?>>Ausgaben</option>
</select>
<select name="status" style="width:140px">
<option value="pending"<?= ($filter['status'] ?? 'pending') === 'pending' ? ' selected' : '' ?>>Offen &amp; Teilweise</option>
<option value="offen"<?= ($filter['status'] ?? '') === 'offen' ? ' selected' : '' ?>>Nur Offen</option>
<option value="teilweise"<?= ($filter['status'] ?? '') === 'teilweise' ? ' selected' : '' ?>>Teilweise</option>
<option value="gebucht"<?= ($filter['status'] ?? '') === 'gebucht' ? ' selected' : '' ?>>Gebucht</option>
<option value="ignoriert"<?= ($filter['status'] ?? '') === 'ignoriert' ? ' selected' : '' ?>>Ignoriert</option>
<option value=""<?= ($filter['status'] ?? 'pending') === '' ? ' selected' : '' ?>>Alle</option>
</select>
<select name="sort" style="width:140px">
<option value="wertstellung"<?= ($filter['sort'] ?? 'wertstellung') === 'wertstellung' ? ' selected' : '' ?>>Wertstellung</option>
<option value="buchungstag"<?= ($filter['sort'] ?? '') === 'buchungstag' ? ' selected' : '' ?>>Buchungstag</option>
<option value="betrag"<?= ($filter['sort'] ?? '') === 'betrag' ? ' selected' : '' ?>>Betrag</option>
<option value="buchungstext"<?= ($filter['sort'] ?? '') === 'buchungstext' ? ' selected' : '' ?>>Buchungstext</option>
<option value="status"<?= ($filter['sort'] ?? '') === 'status' ? ' selected' : '' ?>>Status</option>
</select>
<select name="dir" style="width:110px">
<option value="desc"<?= ($filter['dir'] ?? 'desc') === 'desc' ? ' selected' : '' ?>>↓ Absteigend</option>
<option value="asc"<?= ($filter['dir'] ?? 'desc') === 'asc' ? ' selected' : '' ?>>↑ Aufsteigend</option>
</select>
<button type="submit" class="btn btn-secondary btn-sm">Filtern</button>
<a href="/bank" class="btn btn-secondary btn-sm">Zurücksetzen</a>
</div>
</div>
</form>
<!-- Tabelle -->
<div class="card">
<div class="card-header">
<div class="card-title">
Bankbuchungen
<?php if ($total > 0): ?>
<span class="text-muted" style="font-weight:normal;font-size:.875rem">(<?= $total ?>)</span>
<?php endif; ?>
</div>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th style="width:110px">Wertstellung</th>
<th>Buchungstext</th>
<th class="td-right" style="width:130px">Wert</th>
<th style="width:100px">Status</th>
<th style="width:90px"></th>
</tr>
</thead>
<tbody>
<?php if (empty($rows)): ?>
<tr>
<td colspan="5" class="text-muted" style="text-align:center;padding:2rem">
Keine Buchungen gefunden.
</td>
</tr>
<?php else: ?>
<?php foreach ($rows as $row):
$wert = (float)$row['wert'];
$isPlus = $wert >= 0;
$color = $isPlus ? 'var(--color-success)' : 'var(--color-danger)';
$ziel = '/bank/' . (int)$row['id'];
if ($row['status'] === 'gebucht' && !empty($row['beleg_id']) && ($row['beleg_count'] ?? 1) == 1) {
$ziel = '/belege/' . (int)$row['beleg_id'];
}
?>
<tr style="cursor:pointer" onclick="window.location='<?= $ziel ?>'">
<td class="td-mono"><?= $h(View::formatDate($row['wertstellung'])) ?></td>
<td>
<div style="display:flex;align-items:baseline;gap:.4rem;flex-wrap:wrap">
<?php if (!empty($row['kuerzel'])): ?>
<?php $kf = $row['farbe'] ?? '#6366f1'; ?>
<span class="kuerzel-badge" style="background:<?= $h($kf) ?>;color:<?= $kTextClr($kf) ?>"><?= $h($row['kuerzel']) ?></span>
<?php endif; ?>
<span><?= $h($row['buchungstext']) ?></span>
</div>
<?php if ($row['vorgang']): ?>
<div class="text-muted text-sm"><?= $h($row['vorgang']) ?></div>
<?php endif; ?>
</td>
<td class="td-right td-mono" style="color:<?= $color ?>;font-weight:600">
<?= $isPlus ? '+' : '' ?><?= $h(number_format($wert, 2, ',', '.')) ?> €
</td>
<td>
<?php
$badgeClass = match($row['status']) {
'gebucht' => 'badge-green',
'teilweise' => 'badge-warning',
'ignoriert' => 'badge-gray',
default => 'badge-blue',
};
?>
<span class="badge <?= $badgeClass ?>"><?= $h(ucfirst($row['status'])) ?></span>
<?php if (in_array($row['status'], ['gebucht','teilweise']) && !empty($row['beleg_nr'])): ?>
<div class="text-muted text-sm" style="margin-top:.25rem">
<?php if (($row['beleg_count'] ?? 1) > 1): ?>
<?= (int)$row['beleg_count'] ?> Belege
<?php else: ?>
<?= $h($row['beleg_nr']) ?>
<?php endif; ?>
</div>
<?php endif; ?>
</td>
<td onclick="event.stopPropagation()">
<?php if ($row['status'] !== 'ignoriert'): ?>
<form method="post" action="/bank/<?= (int)$row['id'] ?>/status">
<input type="hidden" name="status" value="ignoriert">
<button type="submit" class="btn btn-secondary btn-sm">Ignorieren</button>
</form>
<?php else: ?>
<form method="post" action="/bank/<?= (int)$row['id'] ?>/status">
<input type="hidden" name="status" value="offen">
<button type="submit" class="btn btn-secondary btn-sm">Aktivieren</button>
</form>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if ($pages > 1): ?>
<div class="card-body" style="padding:.75rem 1rem;display:flex;gap:.5rem;align-items:center">
<?php for ($p = 1; $p <= $pages; $p++): ?>
<?php $q = http_build_query(array_merge($filter, ['page' => $p])); ?>
<a href="/bank?<?= $q ?>" class="btn btn-secondary btn-sm<?= $p === $page ? ' btn-primary' : '' ?>"><?= $p ?></a>
<?php endfor; ?>
</div>
<?php endif; ?>
</div>
<script>
(function () {
const btn = document.getElementById('filter-format-btn');
const drop = document.getElementById('filter-format-drop');
const input = document.getElementById('filter-format-id');
if (!btn || !drop) return;
btn.addEventListener('click', (e) => {
e.stopPropagation();
drop.style.display = drop.style.display === 'none' ? 'block' : 'none';
});
drop.addEventListener('click', (e) => {
const opt = e.target.closest('.fmt-opt');
if (!opt) return;
input.value = opt.dataset.id || '';
btn.closest('form').submit();
});
document.addEventListener('click', () => { drop.style.display = 'none'; });
drop.querySelectorAll('.fmt-opt').forEach(opt => {
opt.addEventListener('mouseenter', () => opt.style.background = 'var(--color-bg)');
opt.addEventListener('mouseleave', () => {
opt.style.background = opt.dataset.id === input.value ? 'var(--color-bg)' : '';
});
});
})();
</script>