234 lines
12 KiB
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 & 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>
|