298 lines
12 KiB
HTML
298 lines
12 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}Kassensystem & Guthaben – {{ tenant.name }}{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container">
|
||
|
||
<!-- Header -->
|
||
<div class="kasse-header">
|
||
<div>
|
||
<span class="badge-role" style="margin-bottom: 6px;">💰 Kasse & Finanzen</span>
|
||
<h1>Brotzeit-Kasse & Guthaben</h1>
|
||
<p class="admin-sub">
|
||
Transparente Abrechnung, bargeldloses Bestellen und Übersicht aller Buchungen.
|
||
</p>
|
||
</div>
|
||
|
||
{% if current_user.role in ['admin', 'organizer'] %}
|
||
<div class="kasse-actions">
|
||
<a href="/einkauf" class="btn-secondary" style="text-decoration: none;">🛒 Zur Einkaufsliste</a>
|
||
<a href="/admin" class="btn-secondary" style="text-decoration: none;">⚙️ Zum Orga-Cockpit</a>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% if success %}
|
||
<div class="alert-success" style="margin-bottom: 24px;">
|
||
✓ {{ success }}
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if error %}
|
||
<div class="alert-error" style="margin-bottom: 24px;">
|
||
✕ {{ error }}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- ================= PERSÖNLICHE GUTHABEN-KARTE ================= -->
|
||
<div class="user-balance-hero">
|
||
<div class="balance-hero-left">
|
||
<div class="balance-hero-label">Dein persönliches Guthaben:</div>
|
||
<div class="balance-hero-amount {% if current_user.balance > 0 %}positive{% elif current_user.balance < 0 %}negative{% endif %}">
|
||
{{ "%.2f"|format(current_user.balance) }} €
|
||
</div>
|
||
<p class="balance-hero-hint">
|
||
{% if current_user.balance > 0 %}
|
||
Du hast ausreichend Guthaben für kommende Brotzeitrunden. Bei der Abholung kannst du einfach per 1-Klick abbuchen lassen!
|
||
{% elif current_user.balance == 0 %}
|
||
Dein Guthaben ist aktuell ausgeglichen (0,00 €). Du kannst Bar zahlen oder dein Konto beim Kassenwart aufladen.
|
||
{% else %}
|
||
Dein Konto ist im Minus. Bitte gleiche den Betrag beim Kassenwart oder Organisator aus.
|
||
{% endif %}
|
||
</p>
|
||
</div>
|
||
|
||
<div class="balance-hero-right">
|
||
<div class="info-bubble">
|
||
<span class="bubble-icon">💡</span>
|
||
<div>
|
||
<strong>Wie funktioniert das Guthaben?</strong>
|
||
<p>Gib deinem Kassenwart oder Organisator einfach deinen gewünschten Betrag (z. B. 20 € bar oder per PayPal). Dieser wird deinem Konto gutgeschrieben und bei jeder Brotzeit automatisch abgezogen.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ================= KASSENWART / ORGA BEREICH ================= -->
|
||
{% if current_user.role in ['admin', 'organizer'] %}
|
||
<div class="kassenwart-section">
|
||
|
||
<div class="section-badge-row">
|
||
<h2>👑 Kassenwart-Cockpit (Team-Kasse)</h2>
|
||
<span class="badge-role admin">Admin & Orga Ansicht</span>
|
||
</div>
|
||
|
||
<!-- Stats Grid -->
|
||
<div class="stats-grid">
|
||
<div class="stat-card">
|
||
<div class="stat-label">Gesamter Kassenbestand (Team)</div>
|
||
<div class="stat-value success">{{ "%.2f"|format(total_team_balance) }} €</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-label">Mitarbeiter mit Guthaben</div>
|
||
<div class="stat-value">{{ positive_users_count }} Personen</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-label">Gesamtzahl Buchungen</div>
|
||
<div class="stat-value">{{ all_transactions|length }} Vorgänge</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="kasse-columns">
|
||
|
||
<!-- Formular: Einzahlung buchen -->
|
||
<div class="admin-card">
|
||
<h2>➕ Guthaben für Mitarbeiter einzahlen</h2>
|
||
<p class="card-hint">Verbuche Barzahlungen, PayPal oder Überweisungen direkt auf das Mitarbeiter-Konto:</p>
|
||
|
||
<form action="/kasse/deposit" method="POST" class="admin-form">
|
||
<div class="input-group">
|
||
<label for="deposit_user_id">Mitarbeiter auswählen</label>
|
||
<select id="deposit_user_id" name="user_id" required>
|
||
<option value="">-- Kollege auswählen --</option>
|
||
{% for u in team_members %}
|
||
<option value="{{ u.id }}">
|
||
{{ u.first_name }} {{ u.last_name }} (Aktuell: {{ "%.2f"|format(u.balance) }} €)
|
||
</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
|
||
<div class="input-group">
|
||
<label for="deposit_amount">Betrag in Euro (€)</label>
|
||
<div class="quick-amounts">
|
||
<button type="button" class="quick-amt-btn" onclick="setDepositAmount('10.00')">+10 €</button>
|
||
<button type="button" class="quick-amt-btn" onclick="setDepositAmount('20.00')">+20 €</button>
|
||
<button type="button" class="quick-amt-btn" onclick="setDepositAmount('30.00')">+30 €</button>
|
||
<button type="button" class="quick-amt-btn" onclick="setDepositAmount('50.00')">+50 €</button>
|
||
</div>
|
||
<input type="number" id="deposit_amount" name="amount" step="0.50" min="0.50" max="500.00" placeholder="20.00" required>
|
||
</div>
|
||
|
||
<div class="input-group">
|
||
<label for="deposit_note">Zahlungsart / Bemerkung</label>
|
||
<input type="text" id="deposit_note" name="description" placeholder="z. B. Barzahlung an Kasse oder PayPal" value="Barzahlung">
|
||
</div>
|
||
|
||
<button type="submit" class="btn-primary" style="width: 100%; margin-top: 10px;">
|
||
💰 Guthaben jetzt gutschreiben
|
||
</button>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- Tabelle: Mitarbeiter-Salden -->
|
||
<div class="admin-card">
|
||
<h2>👥 Team-Guthaben im Überblick</h2>
|
||
<p class="card-hint">Aktuelle Salden aller registrierten Kollegen:</p>
|
||
|
||
<div class="orders-table-wrapper" style="max-height: 400px; overflow-y: auto;">
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Kollege</th>
|
||
<th>Rolle</th>
|
||
<th style="text-align: right;">Guthaben</th>
|
||
<th style="text-align: center;">Aktion</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for u in team_members %}
|
||
<tr>
|
||
<td>
|
||
<strong>{{ u.first_name }} {{ u.last_name }}</strong>
|
||
<br><small class="text-muted">{{ u.email }}</small>
|
||
</td>
|
||
<td>
|
||
{% if u.role == 'admin' %}
|
||
<span class="badge-role admin">Admin</span>
|
||
{% elif u.role == 'organizer' %}
|
||
<span class="badge-role organizer">Orga</span>
|
||
{% else %}
|
||
<span class="badge-role employee">Mitarbeiter</span>
|
||
{% endif %}
|
||
</td>
|
||
<td style="text-align: right; font-weight: 800; font-size: 1rem; {% if u.balance > 0 %}color: var(--success);{% elif u.balance < 0 %}color: var(--danger);{% endif %}">
|
||
{{ "%.2f"|format(u.balance) }} €
|
||
</td>
|
||
<td style="text-align: center;">
|
||
<button type="button" class="btn-fill-small" onclick="quickSelectUser({{ u.id }}, '{{ u.first_name }} {{ u.last_name }}')">
|
||
+ Aufladen
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr><td colspan="4" class="empty-cell">Keine Teammitglieder vorhanden.</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- Vollständiges Buchungsjournal (Für Kassenwart) -->
|
||
<div class="admin-card" style="margin-top: 28px;">
|
||
<h2>📜 Kassenbuch / Transaktions-Journal</h2>
|
||
<p class="card-hint">Revisionssichere Historie aller Einzahlungen und Abbuchungen:</p>
|
||
|
||
<div class="orders-table-wrapper">
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Datum & Zeit</th>
|
||
<th>Mitarbeiter</th>
|
||
<th>Typ</th>
|
||
<th>Beschreibung / Anlass</th>
|
||
<th>Verbucht durch</th>
|
||
<th style="text-align: right;">Betrag</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for t in all_transactions %}
|
||
<tr>
|
||
<td>{{ t.created_at.strftime('%d.%m.%Y %H:%M') }}</td>
|
||
<td>
|
||
<strong>{{ t.user.first_name }} {{ t.user.last_name }}</strong>
|
||
</td>
|
||
<td>
|
||
{% if t.transaction_type == 'deposit' %}
|
||
<span class="badge-role" style="background: rgba(34, 197, 94, 0.15); color: var(--success);">+ Einzahlung</span>
|
||
{% elif t.transaction_type == 'order_charge' %}
|
||
<span class="badge-role" style="background: rgba(56, 189, 248, 0.15); color: var(--accent);">- Brotzeit</span>
|
||
{% else %}
|
||
<span class="badge-role">{{ t.transaction_type }}</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>{{ t.description or '—' }}</td>
|
||
<td>
|
||
{% if t.created_by %}
|
||
{{ t.created_by.first_name }} {{ t.created_by.last_name }}
|
||
{% else %}
|
||
System
|
||
{% endif %}
|
||
</td>
|
||
<td style="text-align: right; font-weight: 800; {% if t.amount > 0 %}color: var(--success);{% else %}color: var(--text-main);{% endif %}">
|
||
{% if t.amount > 0 %}+{% endif %}{{ "%.2f"|format(t.amount) }} €
|
||
</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr><td colspan="6" class="empty-cell">Noch keine Buchungen im Journal vorhanden.</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- ================= PERSÖNLICHE BUCHUNGSHISTORIE ================= -->
|
||
<div class="admin-card" style="margin-top: 28px;">
|
||
<h2>📑 Deine persönlichen Buchungen</h2>
|
||
<p class="card-hint">Hier siehst du transparent alle Aufladungen und Abbuchungen deines Kontos:</p>
|
||
|
||
<div class="orders-table-wrapper">
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Datum</th>
|
||
<th>Vorgang</th>
|
||
<th>Notiz / Anlass</th>
|
||
<th style="text-align: right;">Betrag</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for t in user_transactions %}
|
||
<tr>
|
||
<td>{{ t.created_at.strftime('%d.%m.%Y %H:%M') }}</td>
|
||
<td>
|
||
{% if t.transaction_type == 'deposit' %}
|
||
<span class="badge-role" style="background: rgba(34, 197, 94, 0.15); color: var(--success);">+ Einzahlung</span>
|
||
{% elif t.transaction_type == 'order_charge' %}
|
||
<span class="badge-role" style="background: rgba(56, 189, 248, 0.15); color: var(--accent);">- Brotzeit</span>
|
||
{% else %}
|
||
<span class="badge-role">{{ t.transaction_type }}</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>{{ t.description or 'Brotzeit-Abrechnung' }}</td>
|
||
<td style="text-align: right; font-weight: 800; {% if t.amount > 0 %}color: var(--success);{% else %}color: var(--text-main);{% endif %}">
|
||
{% if t.amount > 0 %}+{% endif %}{{ "%.2f"|format(t.amount) }} €
|
||
</td>
|
||
</tr>
|
||
{% else %}
|
||
<tr><td colspan="4" class="empty-cell">Bisher sind noch keine Buchungen für dein Konto vorhanden.</td></tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
function setDepositAmount(val) {
|
||
document.getElementById('deposit_amount').value = val;
|
||
}
|
||
|
||
function quickSelectUser(userId, name) {
|
||
const select = document.getElementById('deposit_user_id');
|
||
if (select) {
|
||
select.value = userId;
|
||
select.focus();
|
||
document.getElementById('deposit_amount').focus();
|
||
}
|
||
}
|
||
</script>
|
||
{% endblock %}
|