v4.1.0: DMS email documents, category-specific Nachweis linking, version system
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy (push) Has been cancelled
Code Quality / quality (push) Has been cancelled

- Save cover email body as DMS document with new 'email' context type
- Show email body separately from attachments in email detail view
- Add per-category DMS document assignment in quarterly confirmation
  (Studiennachweis, Einkommenssituation, Vermögenssituation)
- Add VERSION file and context processor for automatic version display
- Add MCP server, agent system, import/export, and new migrations
- Update compose files and production environment template

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
SysAdmin Agent
2026-03-15 18:48:52 +00:00
parent faeb7c1073
commit e0b377014c
49 changed files with 5913 additions and 55 deletions

View File

@@ -245,13 +245,35 @@
{% endif %}
</div>
<div class="mb-0">
<div class="mb-3">
<label for="{{ form.studiennachweis_bemerkung.id_for_label }}" class="form-label">{{ form.studiennachweis_bemerkung.label }}</label>
{{ form.studiennachweis_bemerkung }}
{% if form.studiennachweis_bemerkung.help_text %}
<small class="form-text text-muted">{{ form.studiennachweis_bemerkung.help_text }}</small>
{% endif %}
</div>
{% if alle_dms_dokumente or nachweis.studiennachweis_dms_dokument %}
<div class="mb-0">
<label class="form-label"><i class="fas fa-folder-open me-1"></i>Oder: DMS-Dokument zuweisen</label>
{% if nachweis.studiennachweis_dms_dokument %}
<div class="alert alert-success py-2 mb-2">
<i class="fas fa-check-circle me-1"></i>
<strong>{{ nachweis.studiennachweis_dms_dokument.titel }}</strong>
<small class="text-muted">({{ nachweis.studiennachweis_dms_dokument.get_human_size }})</small>
<a href="{% url 'stiftung:dms_download' nachweis.studiennachweis_dms_dokument.pk %}" class="ms-2"><i class="fas fa-download"></i></a>
</div>
{% endif %}
<select class="form-select form-select-sm" name="studiennachweis_dms_id">
<option value="">-- DMS-Dokument waehlen --</option>
{% for dok in alle_dms_dokumente %}
<option value="{{ dok.pk }}" {% if nachweis.studiennachweis_dms_dokument_id == dok.pk %}selected{% endif %}>
{{ dok.titel }} ({{ dok.get_kontext_display }}, {{ dok.get_human_size }})
</option>
{% endfor %}
</select>
</div>
{% endif %}
</div>
</div>
@@ -278,12 +300,12 @@
{% endif %}
</div>
<div class="mb-0">
<div class="mb-3">
<label for="{{ form.einkommenssituation_datei.id_for_label }}" class="form-label">{{ form.einkommenssituation_datei.label }}</label>
{{ form.einkommenssituation_datei }}
{% if nachweis.einkommenssituation_datei %}
<div class="mt-2">
<small class="text-muted">Aktuelle Datei:
<small class="text-muted">Aktuelle Datei:
<a href="{{ nachweis.einkommenssituation_datei.url }}" target="_blank" class="text-decoration-none">
<i class="fas fa-file-pdf text-danger"></i> {{ nachweis.einkommenssituation_datei.name }}
</a>
@@ -294,6 +316,28 @@
<small class="form-text text-muted">{{ form.einkommenssituation_datei.help_text }}</small>
{% endif %}
</div>
{% if alle_dms_dokumente or nachweis.einkommenssituation_dms_dokument %}
<div class="mb-0">
<label class="form-label"><i class="fas fa-folder-open me-1"></i>Oder: DMS-Dokument zuweisen</label>
{% if nachweis.einkommenssituation_dms_dokument %}
<div class="alert alert-success py-2 mb-2">
<i class="fas fa-check-circle me-1"></i>
<strong>{{ nachweis.einkommenssituation_dms_dokument.titel }}</strong>
<small class="text-muted">({{ nachweis.einkommenssituation_dms_dokument.get_human_size }})</small>
<a href="{% url 'stiftung:dms_download' nachweis.einkommenssituation_dms_dokument.pk %}" class="ms-2"><i class="fas fa-download"></i></a>
</div>
{% endif %}
<select class="form-select form-select-sm" name="einkommenssituation_dms_id">
<option value="">-- DMS-Dokument waehlen --</option>
{% for dok in alle_dms_dokumente %}
<option value="{{ dok.pk }}" {% if nachweis.einkommenssituation_dms_dokument_id == dok.pk %}selected{% endif %}>
{{ dok.titel }} ({{ dok.get_kontext_display }}, {{ dok.get_human_size }})
</option>
{% endfor %}
</select>
</div>
{% endif %}
</div>
</div>
@@ -336,6 +380,28 @@
<small class="form-text text-muted">{{ form.vermogenssituation_datei.help_text }}</small>
{% endif %}
</div>
{% if alle_dms_dokumente or nachweis.vermogenssituation_dms_dokument %}
<div class="mb-0">
<label class="form-label"><i class="fas fa-folder-open me-1"></i>Oder: DMS-Dokument zuweisen</label>
{% if nachweis.vermogenssituation_dms_dokument %}
<div class="alert alert-success py-2 mb-2">
<i class="fas fa-check-circle me-1"></i>
<strong>{{ nachweis.vermogenssituation_dms_dokument.titel }}</strong>
<small class="text-muted">({{ nachweis.vermogenssituation_dms_dokument.get_human_size }})</small>
<a href="{% url 'stiftung:dms_download' nachweis.vermogenssituation_dms_dokument.pk %}" class="ms-2"><i class="fas fa-download"></i></a>
</div>
{% endif %}
<select class="form-select form-select-sm" name="vermogenssituation_dms_id">
<option value="">-- DMS-Dokument waehlen --</option>
{% for dok in alle_dms_dokumente %}
<option value="{{ dok.pk }}" {% if nachweis.vermogenssituation_dms_dokument_id == dok.pk %}selected{% endif %}>
{{ dok.titel }} ({{ dok.get_kontext_display }}, {{ dok.get_human_size }})
</option>
{% endfor %}
</select>
</div>
{% endif %}
</div>
</div>
@@ -374,6 +440,66 @@
</div>
</div>
<!-- DMS-Dokumente als Nachweise verknuepfen -->
<div class="card shadow mb-4">
<div class="card-header bg-dark text-white">
<h5 class="card-title mb-0">
<i class="fas fa-folder-open me-2"></i>Dokumente aus dem DMS verknuepfen
</h5>
</div>
<div class="card-body">
<p class="small text-muted mb-3">
Waehlen Sie Dokumente aus dem DMS von {{ destinataer.get_full_name }}, die als Nachweise fuer dieses Quartal dienen sollen.
</p>
{% if verknuepfte_nachweis_dokumente %}
<div class="mb-3">
<label class="form-label"><strong>Bereits verknuepfte Dokumente:</strong></label>
<div class="list-group list-group-flush">
{% for dok in verknuepfte_nachweis_dokumente %}
<div class="list-group-item d-flex justify-content-between align-items-center px-0">
<div>
<i class="fas fa-file me-1 text-muted"></i>
<strong>{{ dok.titel }}</strong>
<span class="badge bg-secondary ms-1">{{ dok.get_kontext_display }}</span>
<br><small class="text-muted">{{ dok.dateiname_original }} ({{ dok.get_human_size }})</small>
</div>
<div class="btn-group btn-group-sm">
<a href="{% url 'stiftung:dms_download' dok.pk %}" class="btn btn-outline-primary" title="Herunterladen"><i class="fas fa-download"></i></a>
<button type="submit" name="entferne_dms_dokument" value="{{ dok.pk }}" class="btn btn-outline-danger" title="Verknuepfung entfernen">
<i class="fas fa-unlink"></i>
</button>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% if verfuegbare_dms_dokumente %}
<div class="mb-0">
<label class="form-label"><strong>Verfuegbare Dokumente hinzufuegen:</strong></label>
<select class="form-select form-select-sm" name="dms_dokument_hinzufuegen" id="dms_dokument_hinzufuegen">
<option value="">-- Dokument aus dem DMS waehlen --</option>
{% for dok in verfuegbare_dms_dokumente %}
<option value="{{ dok.pk }}">
{{ dok.titel }} ({{ dok.get_kontext_display }}, {{ dok.get_human_size }})
</option>
{% endfor %}
</select>
<small class="form-text text-muted mt-1 d-block">
Waehlen Sie ein Dokument und klicken Sie auf "Speichern", um es als Nachweis zu verknuepfen.
</small>
</div>
{% elif not verknuepfte_nachweis_dokumente %}
<div class="text-center text-muted py-2">
<i class="fas fa-info-circle me-1"></i>Keine DMS-Dokumente fuer {{ destinataer.get_full_name }} vorhanden.
<br><small><a href="{% url 'stiftung:dms_upload' %}?destinataer={{ destinataer.pk }}" class="text-decoration-none">Dokument hochladen</a></small>
</div>
{% endif %}
</div>
</div>
<!-- Internal Notes (Staff Only) -->
{% if user.is_staff %}
<div class="card shadow mb-4">