Add Verwaltungskosten detail view with linked documents and emails
- New detail view at /geschaeftsfuehrung/verwaltungskosten/<pk>/ showing invoice data, status, linked DMS documents, and emails - Status change form in sidebar for quick workflow updates - Link Verwaltungskosten list items to detail page - Update email detail to link to VK detail instead of edit Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -180,6 +180,11 @@ urlpatterns = [
|
||||
views.verwaltungskosten_create,
|
||||
name="verwaltungskosten_create",
|
||||
),
|
||||
path(
|
||||
"geschaeftsfuehrung/verwaltungskosten/<uuid:pk>/",
|
||||
views.verwaltungskosten_detail,
|
||||
name="verwaltungskosten_detail",
|
||||
),
|
||||
path(
|
||||
"geschaeftsfuehrung/verwaltungskosten/<uuid:pk>/bearbeiten/",
|
||||
views.verwaltungskosten_edit,
|
||||
|
||||
@@ -32,6 +32,7 @@ from .finanzen import ( # noqa: F401
|
||||
geschaeftsfuehrung,
|
||||
konto_list,
|
||||
verwaltungskosten_list,
|
||||
verwaltungskosten_detail,
|
||||
rentmeister_list,
|
||||
rentmeister_detail,
|
||||
rentmeister_ausgaben,
|
||||
|
||||
@@ -312,6 +312,38 @@ def verwaltungskosten_list(request):
|
||||
return render(request, "stiftung/verwaltungskosten_list.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
def verwaltungskosten_detail(request, pk):
|
||||
"""Detailansicht einer Verwaltungskosten-Position mit verknüpften Dokumenten und E-Mails."""
|
||||
from stiftung.models import DokumentDatei, EmailEingang, Verwaltungskosten
|
||||
|
||||
vk = get_object_or_404(Verwaltungskosten, pk=pk)
|
||||
|
||||
if request.method == "POST":
|
||||
action = request.POST.get("action")
|
||||
if action == "set_status":
|
||||
new_status = request.POST.get("status", "")
|
||||
if new_status in dict(Verwaltungskosten.STATUS_CHOICES):
|
||||
vk.status = new_status
|
||||
vk.save()
|
||||
messages.success(request, f"Status auf '{vk.get_status_display()}' gesetzt.")
|
||||
return redirect("stiftung:verwaltungskosten_detail", pk=pk)
|
||||
|
||||
# Verknüpfte DMS-Dokumente
|
||||
dms_dokumente = DokumentDatei.objects.filter(verwaltungskosten=vk).order_by("erstellt_am")
|
||||
|
||||
# Verknüpfte E-Mails
|
||||
email_eingaenge = EmailEingang.objects.filter(verwaltungskosten=vk).order_by("-eingangsdatum")
|
||||
|
||||
context = {
|
||||
"vk": vk,
|
||||
"dms_dokumente": dms_dokumente,
|
||||
"email_eingaenge": email_eingaenge,
|
||||
"status_choices": Verwaltungskosten.STATUS_CHOICES,
|
||||
}
|
||||
return render(request, "stiftung/verwaltungskosten_detail.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
def rentmeister_list(request):
|
||||
"""Liste aller Rentmeister"""
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
{% if eingang.verwaltungskosten %}
|
||||
<dt class="col-sm-3">Rechnung</dt>
|
||||
<dd class="col-sm-9">
|
||||
<a href="{% url 'stiftung:verwaltungskosten_edit' eingang.verwaltungskosten.pk %}">
|
||||
<a href="{% url 'stiftung:verwaltungskosten_detail' eingang.verwaltungskosten.pk %}">
|
||||
{{ eingang.verwaltungskosten.bezeichnung }} ({{ eingang.verwaltungskosten.betrag }} EUR)
|
||||
</a>
|
||||
<span class="badge bg-{{ eingang.verwaltungskosten.get_status_color }}">{{ eingang.verwaltungskosten.get_status_display }}</span>
|
||||
|
||||
254
app/templates/stiftung/verwaltungskosten_detail.html
Normal file
254
app/templates/stiftung/verwaltungskosten_detail.html
Normal file
@@ -0,0 +1,254 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load humanize %}
|
||||
|
||||
{% block title %}{{ vk.bezeichnung }} – Verwaltungskosten{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3">
|
||||
<i class="fas fa-file-invoice-dollar text-primary me-2"></i>
|
||||
{{ vk.bezeichnung }}
|
||||
</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{% url 'stiftung:verwaltungskosten_edit' vk.pk %}" class="btn btn-outline-primary">
|
||||
<i class="fas fa-edit me-1"></i>Bearbeiten
|
||||
</a>
|
||||
<a href="{% url 'stiftung:verwaltungskosten_list' %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left me-1"></i>Zurück
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status + Kategorie Header -->
|
||||
<div class="d-flex gap-2 mb-3">
|
||||
<span class="badge bg-{{ vk.get_status_color }} fs-6">{{ vk.get_status_display }}</span>
|
||||
<span class="badge bg-info fs-6">{{ vk.get_kategorie_display }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Grunddaten -->
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header bg-dark text-white py-2">
|
||||
<span class="small fw-bold"><i class="fas fa-info-circle me-2"></i>Rechnungsdaten</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-sm-4 text-muted small">Bezeichnung</dt>
|
||||
<dd class="col-sm-8 fw-bold">{{ vk.bezeichnung }}</dd>
|
||||
|
||||
<dt class="col-sm-4 text-muted small">Betrag</dt>
|
||||
<dd class="col-sm-8"><span class="fs-5 fw-bold text-success">€{{ vk.betrag|floatformat:2 }}</span></dd>
|
||||
|
||||
<dt class="col-sm-4 text-muted small">Datum</dt>
|
||||
<dd class="col-sm-8">{{ vk.datum|date:"d.m.Y" }}</dd>
|
||||
|
||||
{% if vk.lieferant_firma %}
|
||||
<dt class="col-sm-4 text-muted small">Lieferant / Firma</dt>
|
||||
<dd class="col-sm-8">{{ vk.lieferant_firma }}</dd>
|
||||
{% endif %}
|
||||
|
||||
{% if vk.rechnungsnummer %}
|
||||
<dt class="col-sm-4 text-muted small">Rechnungsnummer</dt>
|
||||
<dd class="col-sm-8"><code>{{ vk.rechnungsnummer }}</code></dd>
|
||||
{% endif %}
|
||||
|
||||
{% if vk.rentmeister %}
|
||||
<dt class="col-sm-4 text-muted small">Rentmeister</dt>
|
||||
<dd class="col-sm-8">
|
||||
<a href="{% url 'stiftung:rentmeister_detail' vk.rentmeister.pk %}">
|
||||
{{ vk.rentmeister.get_full_name }}
|
||||
</a>
|
||||
</dd>
|
||||
{% endif %}
|
||||
|
||||
{% if vk.get_effective_zahlungskonto %}
|
||||
<dt class="col-sm-4 text-muted small">Zahlungskonto</dt>
|
||||
<dd class="col-sm-8">{{ vk.get_effective_zahlungskonto.bank_name }} – {{ vk.get_effective_zahlungskonto.kontoname }}</dd>
|
||||
{% endif %}
|
||||
|
||||
{% if vk.quellkonto %}
|
||||
<dt class="col-sm-4 text-muted small">Quellkonto</dt>
|
||||
<dd class="col-sm-8">{{ vk.quellkonto.bank_name }} – {{ vk.quellkonto.kontoname }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if vk.is_fahrtkosten %}
|
||||
<!-- Fahrtkosten Details -->
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header bg-dark text-white py-2">
|
||||
<span class="small fw-bold"><i class="fas fa-route me-2"></i>Fahrtkosten</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0">
|
||||
{% if vk.km_anzahl %}
|
||||
<dt class="col-sm-4 text-muted small">Kilometer</dt>
|
||||
<dd class="col-sm-8">{{ vk.km_anzahl }} km</dd>
|
||||
{% endif %}
|
||||
{% if vk.km_satz %}
|
||||
<dt class="col-sm-4 text-muted small">km-Satz</dt>
|
||||
<dd class="col-sm-8">€{{ vk.km_satz|floatformat:2 }}/km</dd>
|
||||
{% endif %}
|
||||
{% if vk.von_ort %}
|
||||
<dt class="col-sm-4 text-muted small">Von</dt>
|
||||
<dd class="col-sm-8">{{ vk.von_ort }}</dd>
|
||||
{% endif %}
|
||||
{% if vk.nach_ort %}
|
||||
<dt class="col-sm-4 text-muted small">Nach</dt>
|
||||
<dd class="col-sm-8">{{ vk.nach_ort }}</dd>
|
||||
{% endif %}
|
||||
{% if vk.zweck %}
|
||||
<dt class="col-sm-4 text-muted small">Zweck</dt>
|
||||
<dd class="col-sm-8">{{ vk.zweck }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if vk.beschreibung %}
|
||||
<!-- Beschreibung -->
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header bg-dark text-white py-2">
|
||||
<span class="small fw-bold"><i class="fas fa-align-left me-2"></i>Beschreibung</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="mb-0" style="white-space: pre-line;">{{ vk.beschreibung }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if vk.notizen %}
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header bg-dark text-white py-2">
|
||||
<span class="small fw-bold"><i class="fas fa-sticky-note me-2"></i>Notizen</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="mb-0" style="white-space: pre-line;">{{ vk.notizen }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- DMS-Dokumente -->
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header bg-dark text-white py-2 d-flex justify-content-between align-items-center">
|
||||
<span class="small fw-bold"><i class="fas fa-file-alt me-2"></i>Verknüpfte Dokumente ({{ dms_dokumente.count }})</span>
|
||||
<a href="{% url 'stiftung:dms_upload' %}?kontext=rechnung" class="btn btn-sm btn-outline-light">
|
||||
<i class="fas fa-upload me-1"></i>Hochladen
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body {% if dms_dokumente %}p-0{% endif %}">
|
||||
{% if dms_dokumente %}
|
||||
<div class="list-group list-group-flush">
|
||||
{% for dok in dms_dokumente %}
|
||||
<a href="{% url 'stiftung:dms_detail' pk=dok.pk %}" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="small fw-bold">
|
||||
{% if dok.is_pdf %}<i class="fas fa-file-pdf text-danger me-1"></i>{% else %}<i class="fas fa-file text-primary me-1"></i>{% endif %}
|
||||
{{ dok.titel|truncatechars:50 }}
|
||||
</div>
|
||||
<small class="text-muted">{{ dok.dateiname_original }} · {{ dok.get_human_size }} · {{ dok.erstellt_am|date:"d.m.Y" }}</small>
|
||||
</div>
|
||||
<span class="btn btn-sm btn-outline-success" title="Herunterladen" onclick="event.preventDefault(); window.location='{% url 'stiftung:dms_download' dok.pk %}';">
|
||||
<i class="fas fa-download"></i>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-muted small mb-0">Keine Dokumente verknüpft.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Verknüpfte E-Mails -->
|
||||
{% if email_eingaenge %}
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header bg-dark text-white py-2">
|
||||
<span class="small fw-bold"><i class="fas fa-envelope me-2"></i>Verknüpfte E-Mails ({{ email_eingaenge.count }})</span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="list-group list-group-flush">
|
||||
{% for email in email_eingaenge %}
|
||||
<a href="{% url 'stiftung:email_eingang_detail' pk=email.pk %}" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="small fw-bold">{{ email.betreff|truncatechars:60 }}</div>
|
||||
<small class="text-muted">{{ email.absender_email }} · {{ email.eingangsdatum|date:"d.m.Y H:i" }}</small>
|
||||
</div>
|
||||
<span class="badge bg-{{ email.get_status_color|default:'secondary' }}">{{ email.get_status_display }}</span>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="col-lg-4">
|
||||
<!-- Status ändern -->
|
||||
<div class="card shadow mb-3">
|
||||
<div class="card-header bg-dark text-white py-2">
|
||||
<span class="small fw-bold"><i class="fas fa-tasks me-2"></i>Status ändern</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="set_status">
|
||||
<div class="d-flex gap-2">
|
||||
<select name="status" class="form-select form-select-sm">
|
||||
{% for code, label in status_choices %}
|
||||
<option value="{{ code }}" {% if vk.status == code %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="submit" class="btn btn-sm btn-primary">
|
||||
<i class="fas fa-check"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Audit Info -->
|
||||
<div class="card shadow mb-3">
|
||||
<div class="card-header bg-dark text-white py-2">
|
||||
<span class="small fw-bold"><i class="fas fa-clock me-2"></i>Zeitstempel</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row mb-0 small">
|
||||
<dt class="col-sm-5 text-muted">Erstellt</dt>
|
||||
<dd class="col-sm-7">{{ vk.erstellt_am|date:"d.m.Y H:i" }}</dd>
|
||||
<dt class="col-sm-5 text-muted">Aktualisiert</dt>
|
||||
<dd class="col-sm-7">{{ vk.aktualisiert_am|date:"d.m.Y H:i" }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Aktionen -->
|
||||
<div class="card shadow mb-3">
|
||||
<div class="card-header bg-dark text-white py-2">
|
||||
<span class="small fw-bold"><i class="fas fa-cog me-2"></i>Aktionen</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="{% url 'stiftung:verwaltungskosten_edit' vk.pk %}" class="btn btn-outline-primary btn-sm">
|
||||
<i class="fas fa-edit me-1"></i>Bearbeiten
|
||||
</a>
|
||||
<a href="{% url 'stiftung:dms_upload' %}?kontext=rechnung" class="btn btn-outline-success btn-sm">
|
||||
<i class="fas fa-upload me-1"></i>Dokument hochladen
|
||||
</a>
|
||||
<a href="{% url 'stiftung:verwaltungskosten_delete' vk.pk %}" class="btn btn-outline-danger btn-sm">
|
||||
<i class="fas fa-trash me-1"></i>Löschen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -101,7 +101,7 @@
|
||||
<tr>
|
||||
<td>{{ kosten.datum|date:"d.m.Y" }}</td>
|
||||
<td>
|
||||
<strong>{{ kosten.bezeichnung }}</strong>
|
||||
<a href="{% url 'stiftung:verwaltungskosten_detail' kosten.pk %}" class="text-decoration-none"><strong>{{ kosten.bezeichnung }}</strong></a>
|
||||
{% if kosten.rechnungsnummer %}
|
||||
<br><small class="text-muted">RG: {{ kosten.rechnungsnummer }}</small>
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user