Phase 2: Destinatär-Timeline, Nachweis-Board, Zahlungs-Pipeline & Pächter-Workflow
2a. Destinatär-Timeline (/destinataere/<pk>/timeline/)
- Chronologische Ansicht aller Events (Zahlungen, Nachweise, E-Mails, Notizen)
- Filter nach Typ via GET-Parameter
2b. Nachweis-Board (/nachweis-board/)
- Quartals-Übersicht aller aktiver Destinatäre (Q1–Q4) in einer Tabellenansicht
- Batch-Erinnerung: erzeugt Audit-Log-Einträge für säumige Destinatäre
- Semester-Logik erhalten (15.03 / 15.09 Fristen)
2c. Zahlungs-Pipeline (/zahlungs-pipeline/)
- 5-Stufen-Kanban: Offen → Nachweis eingereicht → Freigegeben → Überwiesen → Abgeschlossen
- Vier-Augen-Prinzip: can_be_freigegeben() prüft anderen Nutzer als Ersteller
- SEPA pain.001 XML-Export (/sepa-export/) für freigegebene Zahlungen
- Neue Status-Werte: nachweis_eingereicht, freigegeben, abgeschlossen
- Neue Felder: freigegeben_von, freigegeben_am, erstellt_von
2d. Pächter-Workflow (/paechter/workflow/)
- Pipeline nach Restlaufzeit: abgelaufen / <6M / 6–24M / >24M / unbefristet
- Ausstehende Jahresabrechnungen (Vorjahr ohne Abrechnung)
- Pachtanpassungen fällig (Verträge > 5 Jahre laufend)
- Top-Pächter nach Gesamtfläche
Sidebar-Navigation um Pipeline, Nachweis-Board und Pacht-Workflow erweitert.
Migration 0047 erzeugt und angewendet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -617,6 +617,10 @@
|
||||
<i class="fas fa-calculator"></i>
|
||||
<span>Abrechnungen</span>
|
||||
</a>
|
||||
<a class="sidebar-link" href="{% url 'stiftung:paechter_workflow' %}">
|
||||
<i class="fas fa-tractor"></i>
|
||||
<span>Pacht-Workflow</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Finanzen -->
|
||||
@@ -626,6 +630,14 @@
|
||||
<i class="fas fa-hand-holding-usd"></i>
|
||||
<span>Unterstuetzungen</span>
|
||||
</a>
|
||||
<a class="sidebar-link" href="{% url 'stiftung:zahlungs_pipeline' %}">
|
||||
<i class="fas fa-tasks"></i>
|
||||
<span>Zahlungs-Pipeline</span>
|
||||
</a>
|
||||
<a class="sidebar-link" href="{% url 'stiftung:nachweis_board' %}">
|
||||
<i class="fas fa-th"></i>
|
||||
<span>Nachweis-Board</span>
|
||||
</a>
|
||||
<a class="sidebar-link" href="{% url 'stiftung:geschaeftsfuehrung' %}">
|
||||
<i class="fas fa-briefcase"></i>
|
||||
<span>Geschaeftsfuehrung</span>
|
||||
|
||||
151
app/templates/stiftung/destinataer_timeline.html
Normal file
151
app/templates/stiftung/destinataer_timeline.html
Normal file
@@ -0,0 +1,151 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Timeline – {{ destinataer.get_full_name }} – Stiftungsverwaltung{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<!-- Header -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3">
|
||||
<i class="fas fa-stream text-primary me-2"></i>
|
||||
Timeline: {{ destinataer.get_full_name }}
|
||||
</h1>
|
||||
<div>
|
||||
<a href="{% url 'stiftung:destinataer_detail' pk=destinataer.pk %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left me-2"></i>Zum Destinatär
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter Bar -->
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-body py-2">
|
||||
<div class="d-flex gap-2 flex-wrap align-items-center">
|
||||
<span class="text-muted small fw-bold me-2">Filter:</span>
|
||||
<a href="?typ=" class="btn btn-sm {% if not typ_filter %}btn-primary{% else %}btn-outline-primary{% endif %}">
|
||||
<i class="fas fa-list me-1"></i>Alle
|
||||
</a>
|
||||
<a href="?typ=zahlung" class="btn btn-sm {% if typ_filter == 'zahlung' %}btn-success{% else %}btn-outline-success{% endif %}">
|
||||
<i class="fas fa-money-bill-wave me-1"></i>Zahlungen
|
||||
</a>
|
||||
<a href="?typ=nachweis" class="btn btn-sm {% if typ_filter == 'nachweis' %}btn-warning{% else %}btn-outline-warning{% endif %}">
|
||||
<i class="fas fa-file-alt me-1"></i>Nachweise
|
||||
</a>
|
||||
<a href="?typ=email" class="btn btn-sm {% if typ_filter == 'email' %}btn-info{% else %}btn-outline-info{% endif %}">
|
||||
<i class="fas fa-envelope me-1"></i>E-Mails
|
||||
</a>
|
||||
<a href="?typ=notiz" class="btn btn-sm {% if typ_filter == 'notiz' %}btn-secondary{% else %}btn-outline-secondary{% endif %}">
|
||||
<i class="fas fa-sticky-note me-1"></i>Notizen
|
||||
</a>
|
||||
<span class="ms-auto text-muted small">{{ events|length }} Einträge</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Timeline -->
|
||||
{% if events %}
|
||||
<div class="timeline-wrapper">
|
||||
{% for event in events %}
|
||||
<div class="timeline-item mb-3">
|
||||
<div class="card shadow-sm border-start border-4 border-{{ event.farbe }}">
|
||||
<div class="card-body py-2 px-3">
|
||||
<div class="d-flex align-items-start gap-3">
|
||||
<!-- Icon -->
|
||||
<div class="timeline-icon text-{{ event.farbe }} pt-1">
|
||||
<i class="fas {{ event.icon }} fa-lg"></i>
|
||||
</div>
|
||||
<!-- Content -->
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<span class="fw-semibold">{{ event.titel }}</span>
|
||||
{% if event.beschreibung %}
|
||||
<span class="text-muted ms-2 small">{{ event.beschreibung }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="text-end ms-3 text-nowrap">
|
||||
<div class="small text-muted">{{ event.datum|date:"d.m.Y" }}</div>
|
||||
{% if event.status %}
|
||||
<span class="badge bg-{{ event.farbe }} text-white small">{{ event.status }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Extra info per type -->
|
||||
{% if event.typ == 'zahlung' %}
|
||||
<div class="small mt-1">
|
||||
{% with u=event.objekt %}
|
||||
Konto: {{ u.konto.kontoname }}
|
||||
{% if u.empfaenger_iban %} · IBAN: {{ u.empfaenger_iban|slice:":4" }}…{% endif %}
|
||||
{% if u.ausgezahlt_von %} · Ausgezahlt von: {{ u.ausgezahlt_von.get_full_name|default:u.ausgezahlt_von.username }}{% endif %}
|
||||
{% if u.freigegeben_von %} · Freigegeben: {{ u.freigegeben_von.get_full_name|default:u.freigegeben_von.username }} ({{ u.freigegeben_am|date:"d.m.Y" }}){% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% elif event.typ == 'nachweis' %}
|
||||
<div class="small mt-1">
|
||||
{% with n=event.objekt %}
|
||||
Fortschritt: {{ n.get_completion_percentage }}%
|
||||
{% if n.geprueft_von %} · Geprüft von: {{ n.geprueft_von.get_full_name|default:n.geprueft_von.username }}{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% elif event.typ == 'email' %}
|
||||
<div class="small mt-1">
|
||||
{% with e=event.objekt %}
|
||||
Von: {{ e.absender_name|default:e.absender_email }}
|
||||
{% if e.quartalsnachweis %} · Zugeordnet: Q{{ e.quartalsnachweis.quartal }}/{{ e.quartalsnachweis.jahr }}{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
Keine Timeline-Einträge vorhanden{% if typ_filter %} für den gewählten Filter{% endif %}.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.timeline-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
.timeline-wrapper::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
background: #dee2e6;
|
||||
z-index: 0;
|
||||
}
|
||||
.timeline-item {
|
||||
position: relative;
|
||||
padding-left: 48px;
|
||||
}
|
||||
.timeline-icon {
|
||||
position: absolute;
|
||||
left: -28px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #dee2e6;
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
170
app/templates/stiftung/nachweis_board.html
Normal file
170
app/templates/stiftung/nachweis_board.html
Normal file
@@ -0,0 +1,170 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Nachweis-Board – Stiftungsverwaltung{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<!-- Header -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3">
|
||||
<i class="fas fa-th text-primary me-2"></i>
|
||||
Nachweis-Board {{ jahr_filter }}
|
||||
</h1>
|
||||
<div class="d-flex gap-2">
|
||||
{% if overdue_count > 0 %}
|
||||
<form method="post" action="{% url 'stiftung:batch_erinnerung_senden' %}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="jahr" value="{{ jahr_filter }}">
|
||||
<button type="submit" class="btn btn-danger" onclick="return confirm('{{ overdue_count }} säumige Destinatäre markieren?')">
|
||||
<i class="fas fa-bell me-2"></i>{{ overdue_count }} Erinnerung(en)
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter Bar -->
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-body py-2">
|
||||
<form method="get" class="d-flex gap-3 flex-wrap align-items-center">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<label class="text-muted small fw-bold mb-0">Jahr:</label>
|
||||
<select name="jahr" class="form-select form-select-sm" style="width: auto;" onchange="this.form.submit()">
|
||||
{% for j in verfuegbare_jahre %}
|
||||
<option value="{{ j }}" {% if j == jahr_filter %}selected{% endif %}>{{ j }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<label class="text-muted small fw-bold mb-0">Status:</label>
|
||||
<select name="status" class="form-select form-select-sm" style="width: auto;" onchange="this.form.submit()">
|
||||
<option value="">Alle</option>
|
||||
{% for code, label in status_choices %}
|
||||
<option value="{{ code }}" {% if code == status_filter %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<span class="ms-auto text-muted small">{{ board|length }} Destinatäre</span>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Semester-Hinweis -->
|
||||
<div class="alert alert-info py-2 mb-4 small">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
<strong>Semester-Logik:</strong>
|
||||
Studiennachweis-Frist Q1/Q2 → 15. März · Q3/Q4 → 15. September.
|
||||
Zahlungsfrist Q1 → 15. Dez (Vorjahr) · Q2 → 15. Mär · Q3 → 15. Jun · Q4 → 15. Sep.
|
||||
</div>
|
||||
|
||||
<!-- Board Table -->
|
||||
{% if board %}
|
||||
<div class="card shadow">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-sm mb-0">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th style="min-width:180px">Destinatär</th>
|
||||
<th class="text-center">Q1 (Jan–Mär)</th>
|
||||
<th class="text-center">Q2 (Apr–Jun)</th>
|
||||
<th class="text-center">Q3 (Jul–Sep)</th>
|
||||
<th class="text-center">Q4 (Okt–Dez)</th>
|
||||
<th class="text-center">Gesamt</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in board %}
|
||||
<tr>
|
||||
<td class="align-middle">
|
||||
<a href="{% url 'stiftung:destinataer_detail' pk=row.destinataer.pk %}" class="text-decoration-none fw-semibold">
|
||||
{{ row.destinataer.get_full_name }}
|
||||
</a>
|
||||
<div class="small text-muted">
|
||||
<a href="{% url 'stiftung:destinataer_timeline' pk=row.destinataer.pk %}" class="text-muted">
|
||||
<i class="fas fa-stream"></i> Timeline
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
{% for q in "1234" %}
|
||||
{% with nachweis=row.quartale|get_item:q|default:None %}
|
||||
<td class="text-center align-middle">
|
||||
{% if nachweis %}
|
||||
{% if nachweis.status == 'geprueft' or nachweis.status == 'auto_geprueft' %}
|
||||
<span class="badge bg-success" title="{{ nachweis.get_status_display }}">
|
||||
<i class="fas fa-check"></i>
|
||||
</span>
|
||||
{% elif nachweis.status == 'eingereicht' %}
|
||||
<span class="badge bg-info" title="{{ nachweis.get_status_display }}">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
</span>
|
||||
{% elif nachweis.status == 'teilweise' %}
|
||||
<span class="badge bg-warning text-dark" title="{{ nachweis.get_status_display }}">
|
||||
<i class="fas fa-circle-half-stroke"></i>
|
||||
</span>
|
||||
{% elif nachweis.status == 'nachbesserung' %}
|
||||
<span class="badge bg-orange" title="{{ nachweis.get_status_display }}" style="background:#fd7e14">
|
||||
<i class="fas fa-redo"></i>
|
||||
</span>
|
||||
{% elif nachweis.status == 'abgelehnt' %}
|
||||
<span class="badge bg-danger" title="{{ nachweis.get_status_display }}">
|
||||
<i class="fas fa-times"></i>
|
||||
</span>
|
||||
{% elif nachweis.is_overdue %}
|
||||
<span class="badge bg-danger" title="Überfällig">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary" title="{{ nachweis.get_status_display }}">
|
||||
<i class="fas fa-clock"></i>
|
||||
</span>
|
||||
{% endif %}
|
||||
<div class="small mt-1">
|
||||
<a href="{% url 'stiftung:quarterly_confirmation_edit' pk=nachweis.pk %}" class="text-muted">
|
||||
{{ nachweis.get_completion_percentage }}%
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted small">–</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
<td class="text-center align-middle">
|
||||
{% with total=0 done=0 %}
|
||||
{% for q, nachweis in row.quartale.items %}
|
||||
{% if nachweis %}
|
||||
{% if nachweis.status == 'geprueft' or nachweis.status == 'auto_geprueft' %}
|
||||
<i class="fas fa-check-circle text-success"></i>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Legend -->
|
||||
<div class="mt-3 d-flex flex-wrap gap-3 small text-muted">
|
||||
<span><span class="badge bg-success"><i class="fas fa-check"></i></span> Geprüft</span>
|
||||
<span><span class="badge bg-info"><i class="fas fa-paper-plane"></i></span> Eingereicht</span>
|
||||
<span><span class="badge bg-warning text-dark"><i class="fas fa-circle-half-stroke"></i></span> Teilweise</span>
|
||||
<span><span class="badge bg-danger"><i class="fas fa-exclamation-triangle"></i></span> Überfällig</span>
|
||||
<span><span class="badge bg-secondary"><i class="fas fa-clock"></i></span> Offen</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
Keine aktiven Destinatäre gefunden.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
227
app/templates/stiftung/paechter_workflow.html
Normal file
227
app/templates/stiftung/paechter_workflow.html
Normal file
@@ -0,0 +1,227 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Pächter-Workflow – Stiftungsverwaltung{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<!-- Header -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3">
|
||||
<i class="fas fa-tractor text-primary me-2"></i>
|
||||
Pächter-Workflow
|
||||
</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{% url 'stiftung:land_abrechnung_create' %}" class="btn btn-outline-primary">
|
||||
<i class="fas fa-plus me-2"></i>Neue Abrechnung
|
||||
</a>
|
||||
<a href="{% url 'stiftung:paechter_list' %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-list me-2"></i>Alle Pächter
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pipeline: Vertragsfristen -->
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header bg-dark text-white py-2">
|
||||
<h5 class="mb-0 small fw-bold">
|
||||
<i class="fas fa-calendar-alt me-2"></i>Pipeline: Vertragsfristen
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="row g-0">
|
||||
{% for stage in pipeline_stages %}
|
||||
<div class="col-xl col-lg-4 col-md-6 border-end">
|
||||
<div class="p-3">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h6 class="mb-0 text-{{ stage.farbe }} fw-bold small">
|
||||
<i class="fas {{ stage.icon }} me-1"></i>{{ stage.label }}
|
||||
</h6>
|
||||
<span class="badge bg-{{ stage.farbe }} {% if stage.farbe == 'warning' %}text-dark{% else %}text-white{% endif %}">
|
||||
{{ stage.count }}
|
||||
</span>
|
||||
</div>
|
||||
{% if stage.verpachtungen %}
|
||||
{% for v in stage.verpachtungen %}
|
||||
<div class="card mb-2 border-start border-3 border-{{ stage.farbe }}" style="font-size:0.78rem;">
|
||||
<div class="card-body py-2 px-2">
|
||||
<div class="fw-semibold">
|
||||
<a href="{% url 'stiftung:land_verpachtung_detail' pk=v.pk %}" class="text-decoration-none text-dark">
|
||||
{{ v.land.lfd_nr }} – {{ v.paechter.get_full_name }}
|
||||
</a>
|
||||
</div>
|
||||
{% if v.pachtende %}
|
||||
<div class="text-muted mt-1">
|
||||
Ende: {{ v.pachtende|date:"d.m.Y" }}
|
||||
{% if v.pachtende >= heute %}
|
||||
<span class="ms-1">({{ v.pachtende|timeuntil:heute }})</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger ms-1">abgelaufen</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="text-muted">
|
||||
{% if v.verpachtete_flaeche %}{{ v.verpachtete_flaeche|floatformat:0 }} m²{% endif %}
|
||||
{% if v.pachtzins_pauschal %} · €{{ v.pachtzins_pauschal|floatformat:2 }}/J{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="text-muted text-center small py-3">
|
||||
<i class="fas fa-check-circle fa-lg d-block mb-1 opacity-25"></i>
|
||||
Keine
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Ausstehende Jahresabrechnungen -->
|
||||
<div class="col-lg-6">
|
||||
<div class="card shadow h-100">
|
||||
<div class="card-header bg-warning text-dark py-2">
|
||||
<h5 class="mb-0 small fw-bold">
|
||||
<i class="fas fa-file-invoice me-2"></i>
|
||||
Ausstehende Jahresabrechnungen {{ letztes_jahr }}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if laender_ohne_abrechnung %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Lfd. Nr.</th>
|
||||
<th>Gemeinde</th>
|
||||
<th>Pächter</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for land in laender_ohne_abrechnung %}
|
||||
<tr>
|
||||
<td>{{ land.lfd_nr }}</td>
|
||||
<td>{{ land.gemeinde|default:"–" }}</td>
|
||||
<td>{{ land.paechter_name|default:"–" }}</td>
|
||||
<td>
|
||||
<a href="{% url 'stiftung:land_abrechnung_create' %}?land={{ land.pk }}&jahr={{ letztes_jahr }}" class="btn btn-xs btn-outline-primary" style="font-size:0.7rem;padding:2px 6px;">
|
||||
<i class="fas fa-plus"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-muted text-center py-4">
|
||||
<i class="fas fa-check-circle fa-2x mb-2 d-block text-success opacity-50"></i>
|
||||
Alle Jahresabrechnungen {{ letztes_jahr }} vorhanden.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pachtanpassungen fällig (> 5 Jahre) -->
|
||||
<div class="col-lg-6">
|
||||
<div class="card shadow h-100">
|
||||
<div class="card-header bg-info text-white py-2">
|
||||
<h5 class="mb-0 small fw-bold">
|
||||
<i class="fas fa-chart-line me-2"></i>
|
||||
Pachtanpassung fällig (> 5 Jahre)
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if lang_laufend %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Land</th>
|
||||
<th>Pächter</th>
|
||||
<th>Beginn</th>
|
||||
<th>Dauer</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for v in lang_laufend %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{% url 'stiftung:land_verpachtung_detail' pk=v.pk %}">
|
||||
{{ v.land.lfd_nr }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ v.paechter.get_full_name }}</td>
|
||||
<td>{{ v.pachtbeginn|date:"d.m.Y" }}</td>
|
||||
<td>
|
||||
<span class="badge bg-warning text-dark">
|
||||
{{ v.pachtbeginn|timesince:heute }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-muted text-center py-4">
|
||||
<i class="fas fa-check-circle fa-2x mb-2 d-block text-success opacity-50"></i>
|
||||
Keine Pachtanpassungen fällig.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Top-Pächter -->
|
||||
<div class="card shadow mt-4">
|
||||
<div class="card-header bg-primary text-white py-2">
|
||||
<h5 class="mb-0 small fw-bold">
|
||||
<i class="fas fa-crown me-2"></i>Top-Pächter nach Fläche
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if top_paechter %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Pächter</th>
|
||||
<th>Verträge</th>
|
||||
<th>Gesamtfläche (m²)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for p in top_paechter %}
|
||||
<tr>
|
||||
<td class="text-muted">{{ forloop.counter }}</td>
|
||||
<td>
|
||||
<a href="{% url 'stiftung:paechter_detail' pk=p.pk %}">
|
||||
{{ p.get_full_name }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ p.anzahl_vertraege }}</td>
|
||||
<td>{{ p.flaeche|floatformat:0|default:"–" }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-muted text-center py-4">Keine Pächter gefunden.</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
162
app/templates/stiftung/zahlungs_pipeline.html
Normal file
162
app/templates/stiftung/zahlungs_pipeline.html
Normal file
@@ -0,0 +1,162 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Zahlungs-Pipeline – Stiftungsverwaltung{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<!-- Header -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3">
|
||||
<i class="fas fa-tasks text-primary me-2"></i>
|
||||
Zahlungs-Pipeline
|
||||
</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{% url 'stiftung:sepa_xml_export' %}" class="btn btn-outline-success">
|
||||
<i class="fas fa-file-code me-2"></i>SEPA XML exportieren
|
||||
</a>
|
||||
<a href="{% url 'stiftung:unterstuetzung_create' %}" class="btn btn-primary">
|
||||
<i class="fas fa-plus me-2"></i>Neue Zahlung
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filter Bar -->
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-body py-2">
|
||||
<form method="get" class="d-flex gap-3 flex-wrap align-items-center">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<label class="text-muted small fw-bold mb-0">Destinatär:</label>
|
||||
<select name="destinataer" class="form-select form-select-sm" style="width:200px" onchange="this.form.submit()">
|
||||
<option value="">Alle</option>
|
||||
{% for d in destinataere %}
|
||||
<option value="{{ d.pk }}" {% if d.pk|stringformat:'s' == destinataer_filter %}selected{% endif %}>{{ d.get_full_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<label class="text-muted small fw-bold mb-0">Konto:</label>
|
||||
<select name="konto" class="form-select form-select-sm" style="width:180px" onchange="this.form.submit()">
|
||||
<option value="">Alle</option>
|
||||
{% for k in konten %}
|
||||
<option value="{{ k.pk }}" {% if k.pk|stringformat:'s' == konto_filter %}selected{% endif %}>{{ k.kontoname }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% if destinataer_filter or konto_filter %}
|
||||
<a href="{% url 'stiftung:zahlungs_pipeline' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-times"></i> Filter zurücksetzen
|
||||
</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4-Augen Hinweis -->
|
||||
<div class="alert alert-warning py-2 mb-4 small">
|
||||
<i class="fas fa-shield-alt me-2"></i>
|
||||
<strong>Vier-Augen-Prinzip:</strong>
|
||||
Zahlungen können nur von einem <em>anderen</em> Nutzer als dem Ersteller freigegeben werden.
|
||||
SEPA-Export ist nur für Zahlungen im Status „Freigegeben" verfügbar.
|
||||
</div>
|
||||
|
||||
<!-- Pipeline Kanban -->
|
||||
<div class="row g-3">
|
||||
{% for stage in pipeline_stages %}
|
||||
<div class="col-xl col-lg-4 col-md-6">
|
||||
<div class="card shadow h-100">
|
||||
<div class="card-header bg-{{ stage.farbe }} {% if stage.farbe == 'secondary' or stage.farbe == 'dark' %}text-white{% elif stage.farbe == 'warning' %}text-dark{% else %}text-white{% endif %} py-2">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="fw-semibold small">
|
||||
<i class="fas {{ stage.icon }} me-1"></i>{{ stage.label }}
|
||||
</span>
|
||||
<span class="badge bg-white text-dark">{{ stage.zahlungen|length }}</span>
|
||||
</div>
|
||||
{% if stage.gesamt > 0 %}
|
||||
<div class="small opacity-75 mt-1">€{{ stage.gesamt|floatformat:2 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body p-2" style="max-height: 600px; overflow-y: auto;">
|
||||
{% if stage.zahlungen %}
|
||||
{% for z in stage.zahlungen %}
|
||||
<div class="card mb-2 border shadow-sm" style="font-size:0.8rem;">
|
||||
<div class="card-body py-2 px-2">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="fw-semibold">
|
||||
<a href="{% url 'stiftung:destinataer_detail' pk=z.destinataer.pk %}" class="text-decoration-none text-dark">
|
||||
{{ z.destinataer.get_full_name }}
|
||||
</a>
|
||||
</div>
|
||||
<span class="badge bg-{{ stage.farbe }} {% if stage.farbe == 'warning' %}text-dark{% else %}text-white{% endif %}">
|
||||
€{{ z.betrag|floatformat:2 }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-muted mt-1">
|
||||
Fällig: {{ z.faellig_am|date:"d.m.Y" }}
|
||||
{% if z.is_overdue %}
|
||||
<span class="badge bg-danger ms-1">überfällig</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if z.beschreibung %}
|
||||
<div class="text-muted small mt-1">{{ z.beschreibung }}</div>
|
||||
{% endif %}
|
||||
{% if z.freigegeben_von %}
|
||||
<div class="small text-success mt-1">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
{{ z.freigegeben_von.get_full_name|default:z.freigegeben_von.username }}
|
||||
({{ z.freigegeben_am|date:"d.m.Y" }})
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Actions -->
|
||||
<div class="mt-2 d-flex gap-1 flex-wrap">
|
||||
{% if stage.key == 'offen' %}
|
||||
<form method="post" action="{% url 'stiftung:unterstuetzung_nachweis_eingereicht' pk=z.pk %}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="next" value="{% url 'stiftung:zahlungs_pipeline' %}">
|
||||
<button type="submit" class="btn btn-xs btn-outline-info" style="font-size:0.7rem;padding:2px 6px;" title="Nachweis eingereicht">
|
||||
<i class="fas fa-file-alt"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% elif stage.key == 'nachweis_eingereicht' %}
|
||||
<form method="post" action="{% url 'stiftung:unterstuetzung_freigeben' pk=z.pk %}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="next" value="{% url 'stiftung:zahlungs_pipeline' %}">
|
||||
<button type="submit" class="btn btn-xs btn-outline-warning" style="font-size:0.7rem;padding:2px 6px;" title="4-Augen-Freigabe">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% elif stage.key == 'freigegeben' %}
|
||||
<a href="{% url 'stiftung:unterstuetzung_mark_paid' pk=z.pk %}" class="btn btn-xs btn-outline-success" style="font-size:0.7rem;padding:2px 6px;" title="Als überwiesen markieren">
|
||||
<i class="fas fa-university"></i>
|
||||
</a>
|
||||
{% elif stage.key == 'ueberwiesen' %}
|
||||
<form method="post" action="{% url 'stiftung:unterstuetzung_abschliessen' pk=z.pk %}">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="next" value="{% url 'stiftung:zahlungs_pipeline' %}">
|
||||
<button type="submit" class="btn btn-xs btn-outline-dark" style="font-size:0.7rem;padding:2px 6px;" title="Abschließen">
|
||||
<i class="fas fa-check-double"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<a href="{% url 'stiftung:unterstuetzung_detail' pk=z.pk %}" class="btn btn-xs btn-outline-secondary" style="font-size:0.7rem;padding:2px 6px;" title="Details">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="text-muted text-center small py-4">
|
||||
<i class="fas fa-inbox fa-2x mb-2 d-block opacity-25"></i>
|
||||
Keine Zahlungen
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user