This commit is contained in:
cay 2026-03-27 14:01:46 +00:00
parent b59f256e7b
commit f6b60af78b
3 changed files with 110 additions and 0 deletions

View File

@ -1301,3 +1301,45 @@ body:not(.admin-body) > * {
background: #fee2e2;
border-color: var(--error);
}
/* ---- Rechnungshistorie in Mitglied-Detail ---- */
.karte-header-sub {
margin-left: auto;
font-size: 0.78rem;
font-weight: 600;
color: var(--text-muted);
background: var(--bg);
padding: 2px 10px;
border-radius: 20px;
border: 1px solid var(--border);
}
.invoice-history-wrap {
max-height: 272px; /* ca. 6 Zeilen */
overflow-y: auto;
border-radius: 0 0 14px 14px;
}
.invoice-history-table {
width: 100%;
border-collapse: collapse;
font-size: 0.88rem;
}
.invoice-history-table thead th {
position: sticky;
top: 0;
z-index: 1;
padding: 10px 16px;
background: var(--bg);
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-muted);
border-bottom: 1.5px solid var(--border);
}
.invoice-history-table td {
padding: 10px 16px;
border-bottom: 1px solid var(--border);
vertical-align: middle;
}
.invoice-history-row:last-child td { border-bottom: none; }
.invoice-history-row:hover td { background: #f8f8ff; cursor: default; }

View File

@ -175,6 +175,11 @@ router.get('/members/:id', requireAdmin, async (req, res) => {
'SELECT * FROM membership_pauses WHERE membership_id = ? ORDER BY pause_start DESC',
[req.params.id]
);
const [invoices] = await db.query(`
SELECT * FROM invoices
WHERE membership_id = ?
ORDER BY period DESC, created_at DESC
`, [req.params.id]);
const [tariffs] = await db.query(
'SELECT * FROM tariffs WHERE active = 1 ORDER BY name ASC'
);
@ -183,6 +188,7 @@ router.get('/members/:id', requireAdmin, async (req, res) => {
member: rows[0],
pauses,
tariffs,
invoices,
admin: req.session.adminUser,
success: req.query.success || null,
error: req.query.error || null

View File

@ -303,6 +303,68 @@
</div>
</div>
<!-- ===== KARTE 5: Rechnungshistorie ===== -->
<div class="karte karte-full">
<div class="karte-header">
<span class="karte-icon">🧾</span>
<h3>Rechnungshistorie</h3>
<span class="karte-header-sub"><%= invoices.length %> Rechnung(en)</span>
</div>
<div class="karte-body" style="padding:0">
<% if (invoices.length === 0) { %>
<p class="karte-empty" style="padding:20px">Noch keine Rechnungen vorhanden.</p>
<% } else { %>
<div class="invoice-history-wrap">
<table class="invoice-history-table">
<thead>
<tr>
<th>Nr.</th>
<th>Periode</th>
<th>Betrag</th>
<th>Status</th>
<th>Datum</th>
<th>Aktion</th>
</tr>
</thead>
<tbody>
<% invoices.forEach(inv => { %>
<tr class="invoice-history-row">
<td class="invoice-nr">PF24-<%= String(inv.id).padStart(6,'0') %></td>
<td><strong><%= inv.period %></strong></td>
<td>
<% if (inv.status === 'cancelled') { %>
<span style="text-decoration:line-through;color:var(--text-muted)">
<%= Number(inv.amount).toFixed(2).replace('.', ',') %> €
</span>
<% } else { %>
<strong><%= Number(inv.amount).toFixed(2).replace('.', ',') %> €</strong>
<% } %>
</td>
<td>
<span class="invoice-status <%= inv.status === 'paid' ? 'paid' : inv.status === 'cancelled' ? 'cancelled' : 'open' %>">
<%= inv.status === 'paid' ? '✅ Bezahlt' : inv.status === 'cancelled' ? '🚫 Storniert' : '🔴 Offen' %>
</span>
</td>
<td><small class="text-muted"><%= new Date(inv.created_at).toLocaleDateString('de-DE') %></small></td>
<td>
<% if (inv.status === 'cancelled') { %>
<a href="/admin/billing/export/storno-pdf/<%= inv.id %>"
class="btn btn-sm btn-storno" target="_blank">🚫 Storno-PDF</a>
<% } else { %>
<a href="/admin/billing/export/pdf/<%= inv.id %>"
class="btn btn-sm btn-outline" target="_blank">📄 Rechnung</a>
<% } %>
</td>
</tr>
<% }) %>
</tbody>
</table>
</div>
<% } %>
</div>
</div>
</div><!-- end karteikarte-grid -->
</form>