Generalize email system with invoice workflow and Stiftungsgeschichte category

- Rename DestinataerEmailEingang → EmailEingang with category support
  (destinataer, rechnung, land_pacht, stiftungsgeschichte, allgemein)
- Add invoice capture workflow: create Verwaltungskosten from email,
  link DMS documents as invoice attachments, track payment status
- Add Stiftungsgeschichte email category with auto-detection patterns
  (Ahnenforschung, Genealogie, Chronik, etc.) and DMS integration
- Update poll_emails task with category detection and DMS context mapping
- Show available history documents in Geschichte editor sidebar
- Consolidate DMS views, remove legacy dokument templates
- Update all detail/form templates for DMS document linking
- Add deploy.sh script and streamline compose.yml

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
SysAdmin Agent
2026-03-12 10:17:14 +00:00
parent f4fc512ad3
commit e6f4c5ba1b
44 changed files with 1076 additions and 3428 deletions

View File

@@ -260,7 +260,7 @@
</a>
</div>
<div class="col-md-3 mb-3">
<a href="{% url 'stiftung:dokument_management' %}" class="btn btn-outline-secondary w-100">
<a href="{% url 'stiftung:dms_list' %}" class="btn btn-outline-secondary w-100">
<i class="fas fa-folder-open d-block mb-2 fa-2x"></i>
<span>Dokumentenverwaltung</span>
</a>

View File

@@ -622,8 +622,8 @@
{# ════════ TAB: Dokumente ════════ #}
<div class="tab-pane fade" id="pane-dokumente" role="tabpanel">
<div class="d-flex justify-content-end mb-3">
<a href="{% url 'stiftung:dokument_management' %}" class="btn btn-success btn-sm">
<i class="fas fa-plus me-1"></i>Dokument verknuepfen
<a href="{% url 'stiftung:dms_upload' %}?destinataer={{ destinataer.pk }}" class="btn btn-success btn-sm">
<i class="fas fa-upload me-1"></i>Dokument hochladen
</a>
</div>
{% if verknuepfte_dokumente %}
@@ -635,14 +635,17 @@
<tbody>
{% for d in verknuepfte_dokumente %}
<tr>
<td><strong>{{ d.titel }}</strong><br><small class="text-muted">ID: {{ d.paperless_document_id }}</small></td>
<td>
<strong>{{ d.titel }}</strong>
{% if d.dateiname_original %}<br><small class="text-muted">{{ d.dateiname_original }} ({{ d.get_human_size }})</small>{% endif %}
</td>
<td><span class="badge bg-secondary">{{ d.get_kontext_display }}</span></td>
<td>{{ d.beschreibung|default:"-"|truncatewords:10 }}</td>
<td>
<div class="btn-group btn-group-sm">
<a href="{{ d.get_paperless_url }}" target="_blank" class="btn btn-outline-primary" title="In Paperless oeffnen"><i class="fas fa-external-link-alt"></i></a>
<a href="{% url 'stiftung:dokument_update' d.pk %}" class="btn btn-outline-warning" title="Bearbeiten"><i class="fas fa-edit"></i></a>
<a href="{% url 'stiftung:dokument_delete' d.pk %}" class="btn btn-outline-danger" title="Loeschen"><i class="fas fa-unlink"></i></a>
<a href="{% url 'stiftung:dms_download' d.pk %}" class="btn btn-outline-primary" title="Herunterladen"><i class="fas fa-download"></i></a>
<a href="{% url 'stiftung:dms_edit' d.pk %}" class="btn btn-outline-warning" title="Bearbeiten"><i class="fas fa-edit"></i></a>
<a href="{% url 'stiftung:dms_delete' d.pk %}" class="btn btn-outline-danger" title="Loeschen"><i class="fas fa-trash"></i></a>
</div>
</td>
</tr>
@@ -653,7 +656,7 @@
{% else %}
<div class="text-center py-4 text-muted">
<i class="fas fa-file-alt fa-2x mb-2"></i>
<p>Keine Dokumente verknuepft.</p>
<p>Keine Dokumente vorhanden.</p>
</div>
{% endif %}
</div>

View File

@@ -18,6 +18,8 @@
<form method="post" enctype="multipart/form-data" id="upload-form">
{% csrf_token %}
{% if initial.foerderung_id %}<input type="hidden" name="foerderung_id" value="{{ initial.foerderung_id }}">{% endif %}
{% if initial.verpachtung_id %}<input type="hidden" name="verpachtung_id" value="{{ initial.verpachtung_id }}">{% endif %}
<!-- Drag & Drop Zone -->
<div class="card shadow mb-4">

View File

@@ -1,65 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}Dokument löschen - Stiftungsverwaltung{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-6 mx-auto">
<div class="card shadow">
<div class="card-header bg-danger text-white">
<h4 class="mb-0">
<i class="fas fa-exclamation-triangle me-2"></i>Dokument löschen
</h4>
</div>
<div class="card-body">
<div class="alert alert-warning">
<h5 class="alert-heading">
<i class="fas fa-exclamation-triangle me-2"></i>Warnung!
</h5>
<p class="mb-0">
Sind Sie sicher, dass Sie das Dokument <strong>{{ dokument.titel }}</strong> löschen möchten?
</p>
</div>
<div class="card mb-3">
<div class="card-body">
<h6 class="card-title">Dokumentdetails:</h6>
<p class="card-text">
<strong>Titel:</strong> {{ dokument.titel }}<br>
<strong>Kontext:</strong> {{ dokument.get_kontext_display }}<br>
<strong>Paperless ID:</strong> {{ dokument.paperless_document_id }}<br>
{% if dokument.beschreibung %}
<strong>Beschreibung:</strong> {{ dokument.beschreibung }}
{% endif %}
</p>
</div>
</div>
<div class="alert alert-info">
<h6 class="alert-heading">
<i class="fas fa-info-circle me-2"></i>Wichtiger Hinweis
</h6>
<p class="mb-0">
Diese Aktion kann nicht rückgängig gemacht werden. Alle zugehörigen Verknüpfungen werden ebenfalls gelöscht.
</p>
</div>
<form method="post">
{% csrf_token %}
<div class="d-flex justify-content-between">
<a href="{% url 'stiftung:dokument_detail' dokument.pk %}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-2"></i>Abbrechen
</a>
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash me-2"></i>Endgültig löschen
</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,168 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}{{ title }} - 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-file-alt text-primary me-2"></i>{{ title }}
</h1>
<div class="btn-group" role="group">
<a href="{% url 'stiftung:dokument_update' dokument.pk %}" class="btn btn-warning">
<i class="fas fa-edit me-2"></i>Bearbeiten
</a>
<a href="{% url 'stiftung:dokument_delete' dokument.pk %}" class="btn btn-danger">
<i class="fas fa-trash me-2"></i>Löschen
</a>
</div>
</div>
<div class="row">
<!-- Main Information -->
<div class="col-lg-8">
<!-- Dokument Details -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-info-circle me-2"></i>Dokumentdetails
</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h6 class="text-primary">Titel</h6>
<p class="mb-3">{{ dokument.titel }}</p>
<h6 class="text-primary">Kontext</h6>
<p class="mb-3">
<span class="badge bg-secondary">{{ dokument.get_kontext_display }}</span>
</p>
</div>
<div class="col-md-6">
<h6 class="text-primary">Paperless ID</h6>
<p class="mb-3">
<code>{{ dokument.paperless_document_id }}</code>
</p>
<h6 class="text-primary">Erstellt</h6>
<p class="mb-3">{{ dokument.id }}</p>
</div>
</div>
{% if dokument.beschreibung %}
<hr class="my-3">
<h6 class="text-primary">Beschreibung</h6>
<p class="mb-0">{{ dokument.beschreibung }}</p>
{% endif %}
</div>
</div>
<!-- Verknüpfungen -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-link me-2"></i>Verknüpfungen
</h6>
</div>
<div class="card-body">
{% if dokument.foerderung_set.exists or dokument.verpachtung_set.exists %}
{% if dokument.foerderung_set.exists %}
<h6 class="text-primary">Förderungen</h6>
<div class="list-group list-group-flush mb-3">
{% for foerderung in dokument.foerderung_set.all %}
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<strong>{{ foerderung.person.get_full_name }}</strong> - {{ foerderung.jahr }}
<br>
<small class="text-muted">€{{ foerderung.betrag|floatformat:2 }}</small>
</div>
<a href="{% url 'stiftung:foerderung_detail' foerderung.pk %}" class="btn btn-sm btn-outline-primary">
<i class="fas fa-eye"></i>
</a>
</div>
{% endfor %}
</div>
{% endif %}
{% if dokument.verpachtung_set.exists %}
<h6 class="text-primary">Verpachtungen</h6>
<div class="list-group list-group-flush">
{% for verpachtung in dokument.verpachtung_set.all %}
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<strong>{{ verpachtung.vertragsnummer }}</strong> - {{ verpachtung.land.gemeinde }}
<br>
<small class="text-muted">{{ verpachtung.paechter.get_full_name }}</small>
</div>
<a href="{% url 'stiftung:verpachtung_detail' verpachtung.pk %}" class="btn btn-sm btn-outline-primary">
<i class="fas fa-eye"></i>
</a>
</div>
{% endfor %}
</div>
{% endif %}
{% else %}
<div class="text-center py-4">
<i class="fas fa-link fa-3x text-muted mb-3"></i>
<h5 class="text-muted">Keine Verknüpfungen</h5>
<p class="text-muted">Dieses Dokument ist noch nicht mit Förderungen oder Verpachtungen verknüpft.</p>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<!-- Quick Stats -->
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-chart-pie me-2"></i>Übersicht
</h6>
</div>
<div class="card-body">
<div class="row text-center">
<div class="col-6">
<div class="border-end">
<h4 class="text-primary">{{ dokument.foerderung_set.count }}</h4>
<small class="text-muted">Förderungen</small>
</div>
</div>
<div class="col-6">
<h4 class="text-success">{{ dokument.verpachtung_set.count }}</h4>
<small class="text-muted">Verpachtungen</small>
</div>
</div>
</div>
</div>
<!-- Actions -->
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-bolt me-2"></i>Schnellzugriff
</h6>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a href="{% url 'stiftung:dokument_update' dokument.pk %}" class="btn btn-warning">
<i class="fas fa-edit me-2"></i>Dokument bearbeiten
</a>
<a href="{% url 'stiftung:dokument_list' %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Zurück zur Liste
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,139 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}{{ title }} - Stiftung Management{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-8 mx-auto">
<div class="card shadow">
<div class="card-header">
<h4 class="mb-0">
<i class="fas fa-file-alt text-primary me-2"></i>{{ title }}
</h4>
</div>
<div class="card-body">
<form method="post">
{% csrf_token %}
<!-- Verknüpfungsanzeigen -->
{% if form.land_verpachtung_id.value %}
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
<strong>Verknüpfung:</strong> Dieses Dokument wird mit einer Verpachtung verknüpft.
</div>
{% elif form.verpachtung_id.value %}
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
<strong>Verknüpfung:</strong> Dieses Dokument wird mit einer Verpachtung (Legacy) verknüpft.
</div>
{% elif form.land_id.value %}
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
<strong>Verknüpfung:</strong> Dieses Dokument wird mit einer Länderei verknüpft.
</div>
{% elif form.paechter_id.value %}
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
<strong>Verknüpfung:</strong> Dieses Dokument wird mit einem Pächter verknüpft.
</div>
{% elif form.destinataer_id.value %}
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
<strong>Verknüpfung:</strong> Dieses Dokument wird mit einem Destinatär verknüpft.
</div>
{% elif form.foerderung_id.value %}
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
<strong>Verknüpfung:</strong> Dieses Dokument wird mit einer Förderung verknüpft.
</div>
{% endif %}
<div class="row">
<div class="col-md-6 mb-3">
<label for="{{ form.paperless_document_id.id_for_label }}" class="form-label">
{{ form.paperless_document_id.label }} *
</label>
{{ form.paperless_document_id }}
{% if form.paperless_document_id.errors %}
<div class="invalid-feedback d-block">
{{ form.paperless_document_id.errors.0 }}
</div>
{% endif %}
<small class="form-text text-muted">
Die Dokument-ID aus Paperless (z.B. aus der URL: /documents/12345/)
</small>
</div>
<div class="col-md-6 mb-3">
<label for="{{ form.kontext.id_for_label }}" class="form-label">
{{ form.kontext.label }} *
</label>
{{ form.kontext }}
{% if form.kontext.errors %}
<div class="invalid-feedback d-block">
{{ form.kontext.errors.0 }}
</div>
{% endif %}
</div>
</div>
<div class="mb-3">
<label for="{{ form.titel.id_for_label }}" class="form-label">
{{ form.titel.label }} *
</label>
{{ form.titel }}
{% if form.titel.errors %}
<div class="invalid-feedback d-block">
{{ form.titel.errors.0 }}
</div>
{% endif %}
</div>
<div class="mb-3">
<label for="{{ form.beschreibung.id_for_label }}" class="form-label">
{{ form.beschreibung.label }}
</label>
{{ form.beschreibung }}
{% if form.beschreibung.errors %}
<div class="invalid-feedback d-block">
{{ form.beschreibung.errors.0 }}
</div>
{% endif %}
</div>
<!-- Versteckte Verknüpfungsfelder -->
{{ form.land_verpachtung_id }}
{{ form.verpachtung_id }}
{{ form.land_id }}
{{ form.paechter_id }}
{{ form.destinataer_id }}
{{ form.foerderung_id }}
<div class="d-flex justify-content-between">
<a href="{% url 'stiftung:dokument_list' %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-1"></i>Zurück zur Liste
</a>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-1"></i>
{% if dokument %}Aktualisieren{% else %}Verknüpfen{% endif %}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_css %}
<style>
.form-control, .form-select {
border-radius: 0.375rem;
}
.form-control:focus, .form-select:focus {
border-color: #86b7fe;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
</style>
{% endblock %}

View File

@@ -1,146 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}Alle Dokumente - Stiftungsverwaltung{% endblock %}
{% block content %}
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">
<i class="fas fa-file-alt text-primary me-2"></i>
Alle Dokumente
</h1>
<div>
<a href="{% url 'stiftung:dokument_management' %}" class="btn btn-info me-2">
<i class="fas fa-external-link-alt me-1"></i>Dokumentenverwaltung
</a>
</div>
</div>
</div>
</div>
<!-- Verknüpfte Dokumente -->
<div class="row mb-4">
<div class="col-12">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-success">
<i class="fas fa-link me-2"></i>Verknüpfte Dokumente ({{ dokumente|length }})
</h6>
</div>
<div class="card-body">
{% if dokumente %}
<div class="table-responsive">
<table class="table table-hover">
<thead class="table-light">
<tr>
<th>Dokument</th>
<th>Kontext</th>
<th>Verknüpft mit</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for dokument in dokumente %}
<tr>
<td>
<strong>{{ dokument.titel }}</strong>
<br>
<small class="text-muted">ID: {{ dokument.paperless_document_id }}</small>
</td>
<td>
<span class="badge bg-secondary">{{ dokument.get_kontext_display }}</span>
</td>
<td>
{% if dokument.verpachtung_id %}
<span class="badge bg-info">Verpachtung</span>
{% elif dokument.land_id %}
<span class="badge bg-success">Länderei</span>
{% elif dokument.paechter_id %}
<span class="badge bg-primary">Pächter</span>
{% elif dokument.destinataer_id %}
<span class="badge bg-warning">Destinatär</span>
{% elif dokument.foerderung_id %}
<span class="badge bg-secondary">Förderung</span>
{% else %}
<span class="text-muted">Keine Verknüpfung</span>
{% endif %}
</td>
<td>
<div class="btn-group" role="group">
<a href="{{ dokument.get_paperless_url }}" target="_blank" class="btn btn-sm btn-outline-primary" title="In Paperless öffnen">
<i class="fas fa-external-link-alt"></i>
</a>
<a href="{% url 'stiftung:dokument_detail' dokument.pk %}" class="btn btn-sm btn-outline-info" title="Details">
<i class="fas fa-eye"></i>
</a>
<a href="{% url 'stiftung:dokument_update' dokument.pk %}" class="btn btn-sm btn-outline-warning" title="Bearbeiten">
<i class="fas fa-edit"></i>
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-4">
<i class="fas fa-link fa-3x text-muted mb-3"></i>
<h5 class="text-muted">Keine Dokumente verknüpft</h5>
<p class="text-muted">Verknüpfen Sie Dokumente aus Paperless mit Ihren Entitäten.</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Verfügbare Paperless-Dokumente -->
{% if available_dokumente %}
<div class="row mb-4">
<div class="col-12">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-info">
<i class="fas fa-plus-circle me-2"></i>Verfügbare Paperless-Dokumente ({{ available_dokumente|length }})
</h6>
</div>
<div class="card-body">
<div class="row">
{% for doc in available_dokumente %}
<div class="col-md-6 col-lg-4 mb-3">
<div class="card h-100 border-info">
<div class="card-body">
<h6 class="card-title">{{ doc.title }}</h6>
<div class="mb-2">
{% for tag in doc.tags %}
{% if tag == 'Stiftung_Destinatäre' or tag == 'Stiftung_Land_und_Pächter' or tag == 'Stiftung_Administration' %}
<span class="badge bg-primary me-1">{{ tag }}</span>
{% else %}
<span class="badge bg-light text-dark me-1">{{ tag }}</span>
{% endif %}
{% endfor %}
</div>
<div class="d-flex justify-content-between align-items-center">
<a href="{{ doc.document_url }}" target="_blank" class="btn btn-sm btn-outline-info">
<i class="fas fa-external-link-alt me-1"></i>In Paperless öffnen
</a>
<a href="{% url 'stiftung:dokument_management' %}" class="btn btn-sm btn-success">
<i class="fas fa-link me-1"></i>Verknüpfen
</a>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -1,557 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}Dokumentenverwaltung - Stiftung{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h1><i class="fas fa-folder-open me-2"></i>Dokumentenverwaltung</h1>
<div>
<a href="{% url 'stiftung:dokument_list' %}" class="btn btn-outline-primary">
<i class="fas fa-list me-1"></i>Alle Dokumente anzeigen
</a>
</div>
</div>
<div id="statusMessages"></div>
<!-- Filter -->
<div class="card mb-3">
<div class="card-header">
<i class="fas fa-filter me-2"></i>Filter
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-3">
<label class="form-label">Kategorie</label>
<select id="filterCategory" class="form-select">
<option value="all">Alle</option>
<option value="destinaere">Destinatäre</option>
<option value="land">Ländereien</option>
<option value="admin">Administration</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Suche im Titel</label>
<input id="filterQuery" class="form-control" placeholder="Titel enthält..." />
</div>
<div class="col-md-3 d-flex align-items-end">
<button id="refreshDocuments" class="btn btn-primary w-100">
<i class="fas fa-sync me-1"></i>Aktualisieren
</button>
</div>
</div>
</div>
</div>
<!-- Dokumente-Liste -->
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<div><i class="fas fa-file-alt me-2"></i>Dokumente</div>
<small class="text-muted" id="counts"></small>
</div>
<div class="card-body" id="documentsContainer">
<div class="text-center py-5 text-muted" id="loadingState">
<div class="spinner-border" role="status"></div>
<p class="mt-2">Lade Dokumente...</p>
</div>
</div>
</div>
<!-- Re-Link Modal -->
<div class="modal fade" id="relinkModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Dokument neu verknüpfen</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3"><strong id="relinkDocTitle"></strong></div>
<div id="currentLinks" class="mb-3"></div>
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">Kategorie</label>
<select id="relinkCategory" class="form-select">
<option value="destinataer">Destinatäre</option>
<option value="land">Ländereien</option>
<option value="paechter">Pächter</option>
<option value="verpachtung">Verpachtungen</option>
<option value="foerderung">Förderungen</option>
<option value="abrechnung">Abrechnungen</option>
<option value="rentmeister">Rentmeister</option>
</select>
</div>
<div class="col-md-8">
<label class="form-label">Suche</label>
<div class="input-group">
<input id="relinkQuery" class="form-control" placeholder="Name, Ort, E-Mail, Telefon, Adresse..." />
<button id="relinkSearch" class="btn btn-outline-secondary"><i class="fas fa-search"></i></button>
</div>
<small class="form-text text-muted">Durchsucht Name, Adresse, E-Mail, Telefon und weitere Felder</small>
</div>
</div>
<div class="mt-3" id="relinkResults" style="max-height: 400px; overflow-y: auto; border: 1px solid #dee2e6; border-radius: 0.375rem; padding: 0.5rem;"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_css %}
<style>
.search-result-item:hover {
background-color: #f8f9fa !important;
border-color: #0d6efd !important;
}
#relinkResults::-webkit-scrollbar {
width: 8px;
}
#relinkResults::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
#relinkResults::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 4px;
}
#relinkResults::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
</style>
{% endblock %}
{% block javascript %}
<script>
let allDocuments = [];
let linksByPaperlessId = new Map();
let currentRelink = { linkId: null, paperlessId: null };
function showMessage(message, type) {
const statusDiv = document.getElementById('statusMessages');
const alert = document.createElement('div');
alert.className = `alert alert-${type} alert-dismissible fade show`;
alert.innerHTML = `${message}<button type="button" class="btn-close" data-bs-dismiss="alert"></button>`;
statusDiv.appendChild(alert);
setTimeout(() => alert.remove(), 5000);
}
function fetchData() {
console.log('Fetching updated document data...'); // Debug log
const loadingState = document.getElementById('loadingState');
if (loadingState) {
loadingState.style.display = 'block';
}
console.log('Making API calls to:', [
'/api/paperless/documents/?poll=1',
'/api/link-document/list/'
]);
Promise.all([
fetch('/api/paperless/documents/?poll=1').then(r => {
console.log('Paperless API response status:', r.status, r.ok);
if (!r.ok) {
throw new Error(`Paperless API failed: ${r.status} ${r.statusText}`);
}
return r.json();
}),
fetch('/api/link-document/list/').then(r => {
console.log('Link API response status:', r.status, r.ok);
if (!r.ok) {
throw new Error(`Link API failed: ${r.status} ${r.statusText}`);
}
return r.json();
})
]).then(([docs, linksResp]) => {
console.log('Data fetched successfully:', { docs: docs.documents?.length, links: linksResp.links?.length }); // Debug log
console.log('Full docs response:', docs); // Debug the full response
console.log('Full links response:', linksResp); // Debug the full response
allDocuments = docs.documents || [];
linksByPaperlessId = new Map();
// Handle new grouped links format
(linksResp.links || []).forEach(docLinks => {
console.log(`Setting linksByPaperlessId for document ${docLinks.paperless_id}:`, docLinks);
linksByPaperlessId.set(docLinks.paperless_id, docLinks);
});
console.log('Final linksByPaperlessId Map:', linksByPaperlessId);
renderDocuments();
const countsElement = document.getElementById('counts');
if (countsElement) {
countsElement.textContent = `Gesamt: ${docs.total_all} | Destinatäre: ${docs.total_destinaere} | Land: ${docs.total_land} | Admin: ${docs.total_admin}`;
}
}).catch(err => {
console.error('Error fetching data:', err);
console.error('Error details:', {
message: err.message,
stack: err.stack,
name: err.name
});
showMessage('Fehler beim Laden der Daten: ' + err.message, 'danger');
// Show error in the container
const container = document.getElementById('documentsContainer');
if (container) {
container.innerHTML = `
<div class="alert alert-danger">
<h6><i class="fas fa-exclamation-triangle me-2"></i>Fehler beim Laden der Dokumente</h6>
<p class="mb-0">${err.message}</p>
<button class="btn btn-primary mt-2" onclick="fetchData()">
<i class="fas fa-sync me-1"></i>Erneut versuchen
</button>
</div>
`;
}
}).finally(() => {
const loadingState = document.getElementById('loadingState');
if (loadingState) {
loadingState.style.display = 'none';
}
});
}
function renderDocuments() {
const container = document.getElementById('documentsContainer');
const category = document.getElementById('filterCategory').value;
const query = document.getElementById('filterQuery').value.toLowerCase();
let filtered = allDocuments.slice();
if (category !== 'all') {
filtered = filtered.filter(d => d.tag_category === category);
}
if (query) {
filtered = filtered.filter(d => (d.title || '').toLowerCase().includes(query));
}
if (!filtered.length) {
container.innerHTML = '<p class="text-muted">Keine Dokumente gefunden.</p>';
return;
}
let html = '<div class="table-responsive"><table class="table table-striped align-middle"><thead><tr><th>Titel</th><th>Kategorie</th><th>Verknüpft mit</th><th>Aktionen</th></tr></thead><tbody>';
filtered.forEach(doc => {
const linkData = linksByPaperlessId.get(doc.id);
let linkedTo = '<span class="text-muted">nicht verknüpft</span>';
let hasLinks = false;
if (linkData && linkData.links && linkData.links.length > 0) {
console.log(`Document ${doc.id} (${doc.title}) has ${linkData.links.length} links:`, linkData.links);
hasLinks = true;
const linkItems = linkData.links.map(link => {
const obj = link.linked_object;
if (!obj) {
console.warn('Link missing linked_object:', link);
return `<div class="mb-1"><span class="badge bg-warning me-1">Fehler</span> Fehlerhafter Link</div>`;
}
// Generate the appropriate detail URL based on link type
let detailUrl = '#';
if (link.link_type === 'destinataer') {
detailUrl = `/destinataere/${obj.id}/`;
} else if (link.link_type === 'land') {
detailUrl = `/laendereien/${obj.id}/`;
} else if (link.link_type === 'paechter') {
detailUrl = `/paechter/${obj.id}/`;
} else if (link.link_type === 'verpachtung') {
detailUrl = `/laendereien/verpachtungen/${obj.id}/`;
} else if (link.link_type === 'foerderung') {
detailUrl = `/foerderungen/${obj.id}/`;
} else if (link.link_type === 'abrechnung') {
detailUrl = `/laendereien/abrechnungen/${obj.id}/`;
} else if (link.link_type === 'rentmeister') {
detailUrl = `/geschaeftsfuehrung/rentmeister/${obj.id}/`;
}
return `<div class="mb-1 d-flex align-items-center justify-content-between">
<div class="flex-grow-1">
<span class="badge bg-info me-1">${obj?.type || 'Unbekannt'}</span>
<a href="${detailUrl}" class="text-decoration-none small text-primary" title="Zu ${obj?.type || 'Entität'} navigieren">
${obj?.name || 'Unbekannt'}
<i class="fas fa-external-link-alt ms-1" style="font-size: 0.7em;"></i>
</a>
</div>
<button class="btn btn-sm btn-outline-danger ms-2" onclick="onDeleteLink('${link.id}')" title="Diese Verknüpfung löschen">
<i class="fas fa-times" style="font-size: 0.7em;"></i>
</button>
</div>`;
}).join('');
linkedTo = `<div class="small">${linkItems}</div>`;
console.log(`Final linkedTo HTML for doc ${doc.id}:`, linkedTo);
}
const openUrl = `/api/paperless/documents/${doc.id}/`;
html += `
<tr>
<td><strong>${doc.title || 'Ohne Titel'}</strong><br><small class="text-muted">Paperless-ID: ${doc.id}</small></td>
<td><span class="badge bg-secondary">${doc.tag_category}</span></td>
<td>${linkedTo}</td>
<td>
<a class="btn btn-sm btn-outline-primary" href="${openUrl}" target="_blank" title="In Paperless öffnen"><i class="fas fa-external-link-alt"></i></a>
${hasLinks ? `<button class="btn btn-sm btn-outline-danger" onclick="onDeleteAllLinks(${doc.id})" title="Alle Verknüpfungen löschen"><i class="fas fa-trash"></i></button>` : ''}
<button class="btn btn-sm btn-outline-success" onclick="onRelink('', ${doc.id}, '${(doc.title||'').replace(/'/g, "&#39;")}')" title="Neu verknüpfen"><i class="fas fa-link"></i></button>
</td>
</tr>`;
});
html += '</tbody></table></div>';
container.innerHTML = html;
}
function onRelink(linkId, paperlessId, title) {
currentRelink = { linkId, paperlessId };
document.getElementById('relinkDocTitle').textContent = title;
// Show current links in modal
const linkData = linksByPaperlessId.get(paperlessId);
const currentLinksDiv = document.getElementById('currentLinks');
if (linkData && linkData.links && linkData.links.length > 0) {
let currentLinksHtml = '<div class="alert alert-info"><strong>Aktuell verknüpft mit:</strong><ul class="mb-0 mt-2">';
linkData.links.forEach(link => {
const obj = link.linked_object;
currentLinksHtml += `<li><span class="badge bg-secondary me-1">${obj?.type || 'Unbekannt'}</span> ${obj?.name || 'Unbekannt'}</li>`;
});
currentLinksHtml += '</ul></div>';
currentLinksDiv.innerHTML = currentLinksHtml;
} else {
currentLinksDiv.innerHTML = '<div class="alert alert-warning">Dieses Dokument ist noch nicht verknüpft.</div>';
}
document.getElementById('relinkResults').innerHTML = '';
const modal = new bootstrap.Modal(document.getElementById('relinkModal'));
modal.show();
}
document.getElementById('relinkSearch').addEventListener('click', function() {
const category = document.getElementById('relinkCategory').value;
const q = document.getElementById('relinkQuery').value || 'all';
const target = document.getElementById('relinkResults');
target.innerHTML = '<div class="d-flex align-items-center"><div class="spinner-border spinner-border-sm me-2" role="status"></div>Suche...</div>';
fetch(`/api/link-document/search/?q=${encodeURIComponent(q)}&category=${category}`)
.then(r => r.json())
.then(data => {
let items = data[category] || [];
if (!items.length) {
target.innerHTML = '<div class="text-center py-3 text-muted"><i class="fas fa-search me-2"></i>Keine Treffer gefunden.</div>';
return;
}
let html = `<div class="mb-2"><small class="text-muted">${items.length} Treffer gefunden</small></div>`;
// Get current links for this document to mark already linked entities
const linkData = linksByPaperlessId.get(currentRelink.paperlessId);
const currentlyLinkedIds = new Set();
if (linkData && linkData.links) {
linkData.links.forEach(link => {
if (link.linked_object && link.linked_object.id) {
currentlyLinkedIds.add(link.linked_object.id);
}
});
}
items.forEach(it => {
const isLinked = currentlyLinkedIds.has(it.id);
const linkClass = isLinked ? 'border-success bg-light' : 'border';
const buttonClass = isLinked ? 'btn-success' : 'btn-outline-primary';
const buttonIcon = isLinked ? 'fas fa-check-circle' : 'fas fa-plus';
const buttonText = isLinked ? 'Bereits verknüpft' : 'Verknüpfen';
html += `<div class="d-flex justify-content-between align-items-start ${linkClass} rounded p-3 mb-2 search-result-item" style="transition: all 0.2s;">
<div class="flex-grow-1">
<div class="fw-bold mb-1">${it.name}</div>
<div class="text-muted small mb-1">${it.details || ''}</div>
${isLinked ? '<span class="badge bg-success mt-1"><i class="fas fa-check-circle me-1"></i>Aktuell verknüpft</span>' : ''}
</div>
<button class="btn btn-sm ${buttonClass} ms-3" onclick="confirmRelink('${it.id}', '${category}')" title="${buttonText}" ${isLinked ? 'disabled' : ''}>
<i class="${buttonIcon} me-1"></i>${isLinked ? 'Verknüpft' : 'Auswählen'}
</button>
</div>`;
});
target.innerHTML = html;
})
.catch(() => { target.innerHTML = '<div class="text-center py-3 text-danger"><i class="fas fa-exclamation-triangle me-2"></i>Fehler bei der Suche</div>'; });
});
function onDeleteAllLinks(paperlessId) {
const linkData = linksByPaperlessId.get(paperlessId);
if (!linkData || !linkData.links || linkData.links.length === 0) {
showMessage('Keine Verknüpfungen zum Löschen gefunden', 'warning');
return;
}
if (!confirm(`Möchten Sie wirklich alle ${linkData.links.length} Verknüpfung(en) für dieses Dokument löschen?`)) {
return;
}
console.log(`Deleting all ${linkData.links.length} links for document ${paperlessId}:`, linkData.links);
// Delete all links for this document with proper CSRF token
const deletePromises = linkData.links.map(link => {
console.log(`Deleting link ${link.id}`);
return fetch(`/api/link-document/delete/${link.id}/`, {
method: 'DELETE',
headers: { 'X-CSRFToken': getCookie('csrftoken') }
});
});
Promise.all(deletePromises).then(responses => {
console.log('All delete responses:', responses.map(r => ({ status: r.status, ok: r.ok })));
const allSuccessful = responses.every(r => r.ok);
const failedCount = responses.filter(r => !r.ok).length;
if (allSuccessful) {
showMessage('Alle Verknüpfungen erfolgreich gelöscht', 'success');
setTimeout(() => {
fetchData();
}, 300);
} else {
console.error(`${failedCount} of ${responses.length} delete requests failed`);
showMessage(`Fehler beim Löschen von ${failedCount} Verknüpfung(en)`, 'danger');
}
}).catch(err => {
console.error('Error during bulk delete:', err);
showMessage('Fehler beim Löschen der Verknüpfungen', 'danger');
});
}
// Add Enter key support for search
document.getElementById('relinkQuery').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
document.getElementById('relinkSearch').click();
}
});
function confirmRelink(targetId, category) {
console.log('confirmRelink called:', { targetId, category, currentRelink });
const isUpdate = !!currentRelink.linkId;
const url = isUpdate ? '/api/link-document/update/' : '/api/link-document/create/';
const payload = isUpdate ? {
link_id: currentRelink.linkId,
link_type: category,
link_id_target: targetId
} : {
paperless_id: currentRelink.paperlessId,
paperless_title: document.getElementById('relinkDocTitle').textContent,
paperless_url: `/api/paperless/documents/${currentRelink.paperlessId}/`,
link_type: category,
link_id: targetId
};
console.log('Sending request:', { url, payload });
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCookie('csrftoken') },
body: JSON.stringify(payload)
}).then(async r => {
console.log('Response status:', r.status, r.ok);
let resp = {};
try {
resp = await r.json();
console.log('Response data:', resp);
} catch (e) {
console.log('No JSON response, treating as success if status OK');
if (r.ok) {
resp = { success: true };
} else {
throw new Error('Network response was not ok');
}
}
if (r.ok && (resp.success || resp.message)) {
console.log('Success! Showing message and refreshing...');
showMessage(resp.message || 'Verknüpfung gespeichert', 'success');
// Close modal first, then refresh data
const modal = bootstrap.Modal.getInstance(document.getElementById('relinkModal'));
if (modal) {
console.log('Closing modal...');
modal.hide();
}
// Clear search results to prevent confusion
document.getElementById('relinkResults').innerHTML = '';
document.getElementById('relinkQuery').value = '';
// Try immediate refresh first
console.log('Calling fetchData() immediately...');
fetchData();
// Also schedule a backup refresh to be sure
console.log('Scheduling backup refresh in 500ms...');
setTimeout(() => {
console.log('Backup fetchData() call...');
fetchData();
}, 500);
} else {
console.log('Error response:', resp);
showMessage(resp.error || 'Fehler beim Speichern', 'danger');
}
}).catch(err => {
console.error('Relink error:', err);
showMessage('Fehler beim Speichern', 'danger');
});
}
function onDeleteLink(linkId) {
if (!confirm('Diese Verknüpfung wirklich löschen?')) return;
console.log('Deleting individual link:', linkId);
fetch(`/api/link-document/delete/${linkId}/`, {
method: 'DELETE',
headers: { 'X-CSRFToken': getCookie('csrftoken') }
})
.then(async r => {
console.log('Delete response status:', r.status, r.ok);
let data = {};
try {
data = await r.json();
console.log('Delete response data:', data);
} catch (_) {
console.log('No JSON response, treating as success if status OK');
}
if (r.ok && (data.success === undefined || data.success === true)) {
showMessage('Verknüpfung gelöscht', 'success');
setTimeout(() => {
fetchData();
}, 300);
} else {
showMessage((data && data.error) || 'Fehler beim Löschen', 'danger');
}
})
.catch(err => {
console.error('Delete error:', err);
showMessage('Fehler beim Löschen', 'danger');
});
}
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
document.getElementById('refreshDocuments').addEventListener('click', fetchData);
document.getElementById('filterCategory').addEventListener('change', renderDocuments);
document.getElementById('filterQuery').addEventListener('input', () => { renderDocuments(); });
document.addEventListener('DOMContentLoaded', function() {
console.log('Dokumentenverwaltung page loaded, calling fetchData()...');
fetchData();
});
</script>
{% endblock %}

View File

@@ -1,7 +1,7 @@
{% extends 'base.html' %}
{% load humanize %}
{% block title %}E-Mail-Eingang Detail - van Hees-Theyssen-Vogel'sche Stiftung{% endblock %}
{% block title %}E-Mail-Eingang Detail - Stiftungsverwaltung{% endblock %}
{% block content %}
<div class="row">
@@ -11,22 +11,31 @@
<i class="fas fa-envelope me-2"></i>E-Mail-Eingang
</h1>
<a href="{% url 'stiftung:email_eingang_list' %}" class="btn btn-outline-secondary btn-sm">
<i class="fas fa-arrow-left me-1"></i>Zurück zur Übersicht
<i class="fas fa-arrow-left me-1"></i>Zurueck zur Uebersicht
</a>
</div>
</div>
</div>
<div class="row">
<!-- Linke Spalte: E-Mail-Details -->
{# Linke Spalte: E-Mail-Details #}
<div class="col-lg-8">
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<span><i class="fas fa-envelope-open me-2"></i>E-Mail-Details</span>
<span>
{# Kategorie-Badge #}
{% if eingang.kategorie == "rechnung" %}<span class="badge bg-warning text-dark me-1"><i class="fas fa-file-invoice me-1"></i>Rechnung</span>
{% elif eingang.kategorie == "destinataer" %}<span class="badge bg-info me-1"><i class="fas fa-user me-1"></i>Destinataer</span>
{% elif eingang.kategorie == "land_pacht" %}<span class="badge bg-success me-1"><i class="fas fa-map me-1"></i>Land/Pacht</span>
{% elif eingang.kategorie == "stiftungsgeschichte" %}<span class="badge bg-dark me-1"><i class="fas fa-landmark me-1"></i>Geschichte</span>
{% endif %}
{# Status-Badge #}
{% if eingang.status == "neu" %}<span class="badge bg-warning text-dark">Neu</span>
{% elif eingang.status == "zugewiesen" %}<span class="badge bg-primary">Zugewiesen</span>
{% elif eingang.status == "verarbeitet" %}<span class="badge bg-success">Verarbeitet</span>
{% elif eingang.status == "rechnung_erfasst" %}<span class="badge bg-info">Rechnung erfasst</span>
{% elif eingang.status == "zahlung_gebucht" %}<span class="badge bg-success">Zahlung gebucht</span>
{% elif eingang.status == "unbekannt" %}<span class="badge bg-danger">Unbekannter Absender</span>
{% elif eingang.status == "fehler" %}<span class="badge bg-secondary">Fehler</span>
{% endif %}
@@ -47,17 +56,27 @@
<dt class="col-sm-3">Betreff</dt>
<dd class="col-sm-9">{{ eingang.betreff|default:"(kein Betreff)" }}</dd>
<dt class="col-sm-3">Destinatär</dt>
<dt class="col-sm-3">Destinataer</dt>
<dd class="col-sm-9">
{% if eingang.destinataer %}
<a href="{% url 'stiftung:destinataer_detail' eingang.destinataer.pk %}">
{{ eingang.destinataer }}
</a>
{% else %}
<span class="text-danger"><i class="fas fa-exclamation-circle me-1"></i>Nicht zugeordnet</span>
<span class="text-muted">Nicht zugeordnet</span>
{% endif %}
</dd>
{% if eingang.verwaltungskosten %}
<dt class="col-sm-3">Rechnung</dt>
<dd class="col-sm-9">
<a href="{% url 'stiftung:verwaltungskosten_edit' 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>
</dd>
{% endif %}
{% if eingang.quartalsnachweis %}
<dt class="col-sm-3">Quartalsnachweis</dt>
<dd class="col-sm-9">
@@ -82,32 +101,34 @@
</div>
</div>
<!-- Anhänge / Paperless-Dokumente -->
{% if dokument_links %}
{# Anhaenge / DMS-Dokumente #}
{% if dms_dokumente %}
<div class="card mb-4">
<div class="card-header">
<i class="fas fa-paperclip me-2"></i>Anhänge in Paperless-NGX
<i class="fas fa-paperclip me-2"></i>Anhaenge ({{ dms_dokumente|length }})
</div>
<div class="card-body p-0">
<table class="table mb-0">
<thead class="table-light">
<tr>
<th>Titel</th>
<th>Kontext</th>
<th>Paperless-ID</th>
<th>Dateiname</th>
<th>Typ</th>
<th>Groesse</th>
<th></th>
</tr>
</thead>
<tbody>
{% for link in dokument_links %}
{% for dok in dms_dokumente %}
<tr>
<td>{{ link.titel }}</td>
<td>{{ link.get_kontext_display }}</td>
<td><code>{{ link.paperless_document_id }}</code></td>
<td>{{ dok.dateiname_original|default:dok.titel }}</td>
<td><span class="text-muted small">{{ dok.dateityp|default:"" }}</span></td>
<td><span class="text-muted small">{{ dok.get_human_size }}</span></td>
<td>
<a href="{{ link.get_paperless_url }}" target="_blank" class="btn btn-sm btn-outline-info">
<i class="fas fa-external-link-alt me-1"></i>Öffnen
{% if dok.datei %}
<a href="{% url 'stiftung:dms_download' dok.pk %}" class="btn btn-sm btn-outline-primary">
<i class="fas fa-download me-1"></i>Herunterladen
</a>
{% endif %}
</td>
</tr>
{% endfor %}
@@ -115,43 +136,109 @@
</table>
</div>
</div>
{% elif eingang.paperless_dokument_ids %}
<div class="alert alert-info">
<i class="fas fa-info-circle me-1"></i>
{{ eingang.paperless_dokument_ids|length }} Anhang/-hänge in Paperless hochgeladen
(IDs: {{ eingang.paperless_dokument_ids|join:", " }}), aber noch kein DokumentLink erstellt.
</div>
{% else %}
<div class="card mb-4">
<div class="card-body text-muted text-center py-3">
<i class="fas fa-paperclip me-1"></i>Keine Anhänge in dieser E-Mail.
<i class="fas fa-paperclip me-1"></i>Keine Anhaenge in dieser E-Mail.
</div>
</div>
{% endif %}
</div>
<!-- Rechte Spalte: Aktionen -->
{# Rechte Spalte: Aktionen #}
<div class="col-lg-4">
<!-- Manuelle Destinatär-Zuordnung -->
{% if not eingang.destinataer or eingang.status == "unbekannt" %}
{# Kategorie aendern #}
<div class="card mb-4">
<div class="card-header">
<i class="fas fa-tag me-2"></i>Kategorie
</div>
<div class="card-body">
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="set_kategorie">
<div class="mb-2">
<select class="form-select form-select-sm" name="kategorie">
<option value="allgemein" {% if eingang.kategorie == "allgemein" %}selected{% endif %}>Allgemein</option>
<option value="destinataer" {% if eingang.kategorie == "destinataer" %}selected{% endif %}>Destinataer</option>
<option value="rechnung" {% if eingang.kategorie == "rechnung" %}selected{% endif %}>Rechnung</option>
<option value="land_pacht" {% if eingang.kategorie == "land_pacht" %}selected{% endif %}>Grundstueck / Pacht</option>
<option value="stiftungsgeschichte" {% if eingang.kategorie == "stiftungsgeschichte" %}selected{% endif %}>Stiftungsgeschichte</option>
</select>
</div>
<button type="submit" class="btn btn-outline-primary btn-sm w-100">
<i class="fas fa-save me-1"></i>Kategorie setzen
</button>
</form>
</div>
</div>
{# Rechnung erfassen (nur wenn noch keine zugeordnet) #}
{% if not eingang.verwaltungskosten and eingang.status != "zahlung_gebucht" %}
<div class="card mb-4 border-warning">
<div class="card-header bg-warning text-dark">
<i class="fas fa-user-plus me-2"></i>Destinatär manuell zuordnen
<i class="fas fa-file-invoice-dollar me-2"></i>Als Rechnung erfassen
</div>
<div class="card-body">
<p class="small text-muted">
Die E-Mail-Adresse <strong>{{ eingang.absender_email }}</strong>
konnte keinem Destinatär automatisch zugeordnet werden.
Bitte wählen Sie den passenden Destinatär aus.
Erstellt einen Verwaltungskosten-Eintrag und verknuepft die Anhaenge als Rechnungsdokumente.
</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="erfasse_rechnung">
<div class="mb-2">
<label class="form-label small">Bezeichnung</label>
<input type="text" class="form-control form-control-sm" name="bezeichnung"
value="{{ eingang.betreff }}" required>
</div>
<div class="mb-2">
<label class="form-label small">Betrag (EUR)</label>
<input type="number" step="0.01" class="form-control form-control-sm" name="betrag"
placeholder="0.00" required>
</div>
<div class="mb-2">
<label class="form-label small">Lieferant / Firma</label>
<input type="text" class="form-control form-control-sm" name="lieferant"
value="{{ eingang.absender_name|default:eingang.absender_email }}">
</div>
<div class="mb-2">
<label class="form-label small">Rechnungsnummer</label>
<input type="text" class="form-control form-control-sm" name="rechnungsnummer"
placeholder="z.B. RE-2026001">
</div>
<div class="mb-2">
<label class="form-label small">Kategorie</label>
<select class="form-select form-select-sm" name="vk_kategorie">
{% for key, label in vk_kategorie_choices %}
<option value="{{ key }}" {% if key == "rechnung_intern" %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-warning w-100">
<i class="fas fa-file-invoice me-1"></i>Rechnung erfassen
</button>
</form>
</div>
</div>
{% endif %}
{# Manuelle Destinataer-Zuordnung #}
{% if not eingang.destinataer or eingang.status == "unbekannt" %}
<div class="card mb-4 border-info">
<div class="card-header bg-info text-white">
<i class="fas fa-user-plus me-2"></i>Destinataer zuordnen
</div>
<div class="card-body">
<p class="small text-muted">
Absender <strong>{{ eingang.absender_email }}</strong>
konnte nicht automatisch zugeordnet werden.
</p>
<form method="post">
{% csrf_token %}
<input type="hidden" name="action" value="assign_destinataer">
<div class="mb-3">
<label class="form-label">Destinatär</label>
<select class="form-select" name="destinataer_id" required>
<option value=""> Bitte wählen </option>
<select class="form-select form-select-sm" name="destinataer_id" required>
<option value=""> Bitte waehlen </option>
{% for d in alle_destinataere %}
<option value="{{ d.pk }}">{{ d.nachname }}, {{ d.vorname }}
{% if d.email %} ({{ d.email }}){% endif %}
@@ -159,16 +246,16 @@
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-warning w-100">
<i class="fas fa-link me-1"></i>Zuordnen & Speichern
<button type="submit" class="btn btn-info w-100">
<i class="fas fa-link me-1"></i>Zuordnen
</button>
</form>
</div>
</div>
{% endif %}
<!-- Als verarbeitet markieren -->
{% if eingang.status != "verarbeitet" %}
{# Als verarbeitet markieren #}
{% if eingang.status != "verarbeitet" and eingang.status != "zahlung_gebucht" %}
<div class="card mb-4">
<div class="card-header">
<i class="fas fa-check-circle me-2"></i>Als verarbeitet markieren
@@ -178,9 +265,8 @@
{% csrf_token %}
<input type="hidden" name="action" value="mark_verarbeitet">
<div class="mb-3">
<label class="form-label">Interne Notiz (optional)</label>
<textarea class="form-control" name="notizen" rows="3"
placeholder="Z. B. 'Studiennachweis für WS 2025/26 eingegangen und geprüft.'">{{ eingang.notizen }}</textarea>
<textarea class="form-control form-control-sm" name="notizen" rows="3"
placeholder="Optionale Notiz...">{{ eingang.notizen }}</textarea>
</div>
<button type="submit" class="btn btn-success w-100">
<i class="fas fa-check me-1"></i>Verarbeitet
@@ -190,7 +276,7 @@
</div>
{% endif %}
<!-- Notizen bearbeiten -->
{# Notizen #}
<div class="card mb-4">
<div class="card-header">
<i class="fas fa-sticky-note me-2"></i>Interne Notizen
@@ -200,41 +286,42 @@
{% csrf_token %}
<input type="hidden" name="action" value="save_notizen">
<div class="mb-3">
<textarea class="form-control" name="notizen" rows="5"
placeholder="Interne Notizen zur E-Mail...">{{ eingang.notizen }}</textarea>
<textarea class="form-control form-control-sm" name="notizen" rows="4"
placeholder="Interne Notizen...">{{ eingang.notizen }}</textarea>
</div>
<button type="submit" class="btn btn-outline-secondary w-100">
<button type="submit" class="btn btn-outline-secondary btn-sm w-100">
<i class="fas fa-save me-1"></i>Notizen speichern
</button>
</form>
</div>
</div>
<!-- Metadaten -->
{# Metadaten #}
<div class="card mb-4">
<div class="card-header"><i class="fas fa-info-circle me-2"></i>Metadaten</div>
<div class="card-body">
<dl class="row mb-0 small">
<dt class="col-6">Erfasst am</dt>
<dd class="col-6">{{ eingang.created_at|date:"d.m.Y H:i" }}</dd>
<dt class="col-6">Kategorie</dt>
<dd class="col-6">{{ eingang.get_kategorie_display }}</dd>
<dt class="col-6">Datensatz-ID</dt>
<dd class="col-6 text-muted"><code>{{ eingang.pk|stringformat:"s"|slice:":8" }}</code></dd>
<dd class="col-6 text-muted"><code>{{ eingang.pk|stringformat:"s"|slice:":8" }}...</code></dd>
</dl>
</div>
</div>
<!-- Löschen -->
{# Loeschen #}
<div class="card border-danger">
<div class="card-header text-danger">
<i class="fas fa-trash-alt me-2"></i>E-Mail löschen
<i class="fas fa-trash-alt me-2"></i>E-Mail loeschen
</div>
<div class="card-body">
<p class="small text-muted mb-2">Diese E-Mail unwiderruflich aus dem System entfernen.</p>
<form method="post" action="{% url 'stiftung:email_eingang_delete' eingang.pk %}"
onsubmit="return confirm('E-Mail wirklich löschen?');">
onsubmit="return confirm('E-Mail wirklich loeschen?');">
{% csrf_token %}
<button type="submit" class="btn btn-outline-danger w-100">
<i class="fas fa-trash-alt me-1"></i>Löschen
<button type="submit" class="btn btn-outline-danger btn-sm w-100">
<i class="fas fa-trash-alt me-1"></i>Loeschen
</button>
</form>
</div>

View File

@@ -1,14 +1,14 @@
{% extends 'base.html' %}
{% load humanize %}
{% block title %}E-Mail-Eingang (Destinatäre) - van Hees-Theyssen-Vogel'sche Stiftung{% endblock %}
{% block title %}E-Mail-Eingang - Stiftungsverwaltung{% endblock %}
{% block content %}
<div class="row">
<div class="col-12">
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">
<i class="fas fa-envelope-open-text me-2"></i>E-Mail-Eingang (Destinatäre)
<i class="fas fa-envelope-open-text me-2"></i>E-Mail-Eingang
</h1>
<div class="d-flex gap-2">
<form method="post" action="{% url 'stiftung:email_eingang_poll_trigger' %}" class="d-inline">
@@ -17,79 +17,65 @@
<i class="fas fa-sync-alt me-1"></i>Jetzt abrufen
</button>
</form>
<a href="{% url 'stiftung:destinataer_list' %}" class="btn btn-outline-secondary btn-sm">
<i class="fas fa-arrow-left me-1"></i>Destinatäre
</a>
</div>
</div>
</div>
</div>
<!-- Statuskarten -->
{# Statuskarten #}
<div class="row mb-4">
<div class="col-md-3">
<div class="col-md-3 col-6 mb-2">
<div class="card border-left-primary h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Gesamt</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ counts.gesamt }}</div>
</div>
<div class="col-auto"><i class="fas fa-envelope fa-2x text-gray-300"></i></div>
</div>
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">Gesamt</div>
<div class="h5 mb-0 font-weight-bold">{{ counts.gesamt }}</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="col-md-3 col-6 mb-2">
<div class="card border-left-warning h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Neu / Unbearbeitet</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ counts.neu }}</div>
</div>
<div class="col-auto"><i class="fas fa-exclamation-circle fa-2x text-gray-300"></i></div>
</div>
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">Neu</div>
<div class="h5 mb-0 font-weight-bold">{{ counts.neu }}</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="col-md-3 col-6 mb-2">
<div class="card border-left-info h-100 py-2">
<div class="card-body">
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">Rechnungen</div>
<div class="h5 mb-0 font-weight-bold">{{ counts.rechnung }}</div>
</div>
</div>
</div>
<div class="col-md-3 col-6 mb-2">
<div class="card border-left-danger h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-danger text-uppercase mb-1">Unbekannter Absender</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ counts.unbekannt }}</div>
</div>
<div class="col-auto"><i class="fas fa-user-times fa-2x text-gray-300"></i></div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card border-left-secondary h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-secondary text-uppercase mb-1">Fehler</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ counts.fehler }}</div>
</div>
<div class="col-auto"><i class="fas fa-exclamation-triangle fa-2x text-gray-300"></i></div>
</div>
<div class="text-xs font-weight-bold text-danger text-uppercase mb-1">Unbekannt</div>
<div class="h5 mb-0 font-weight-bold">{{ counts.unbekannt }}</div>
</div>
</div>
</div>
</div>
<!-- Filter -->
{# Filter #}
<div class="card mb-4">
<div class="card-header"><i class="fas fa-filter me-2"></i>Filter</div>
<div class="card-body">
<form method="get" class="row g-3">
<div class="col-md-4">
<div class="col-md-3">
<label class="form-label">Suche</label>
<input type="text" class="form-control" name="q" value="{{ search }}"
placeholder="Absender, Betreff, Destinatär...">
placeholder="Absender, Betreff...">
</div>
<div class="col-md-3">
<label class="form-label">Kategorie</label>
<select class="form-select" name="kategorie">
<option value="">Alle</option>
{% for value, label in kategorie_choices %}
<option value="{{ value }}" {% if kategorie_filter == value %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-3">
<label class="form-label">Status</label>
@@ -100,15 +86,15 @@
{% endfor %}
</select>
</div>
<div class="col-md-2 d-flex align-items-end">
<div class="col-md-1 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-search me-1"></i>Filtern
<i class="fas fa-search"></i>
</button>
</div>
{% if search or status_filter %}
{% if search or status_filter or kategorie_filter %}
<div class="col-md-2 d-flex align-items-end">
<a href="{% url 'stiftung:email_eingang_list' %}" class="btn btn-outline-secondary w-100">
<i class="fas fa-times me-1"></i>Zurücksetzen
<i class="fas fa-times me-1"></i>Reset
</a>
</div>
{% endif %}
@@ -116,11 +102,11 @@
</div>
</div>
<!-- Tabelle -->
{# Tabelle #}
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<span><i class="fas fa-inbox me-2"></i>Eingegangene E-Mails</span>
<span class="text-muted small">{{ page_obj.paginator.count }} Einträge</span>
<span class="text-muted small">{{ page_obj.paginator.count }} Eintraege</span>
</div>
<div class="card-body p-0">
{% if page_obj %}
@@ -130,9 +116,9 @@
<tr>
<th>Datum</th>
<th>Absender</th>
<th>Destinatär</th>
<th>Betreff</th>
<th>Anhänge</th>
<th>Kategorie</th>
<th>Zuordnung</th>
<th>Status</th>
<th></th>
</tr>
@@ -149,19 +135,27 @@
<small class="text-muted">{{ e.absender_email }}</small>
{% endif %}
</td>
<td>{{ e.betreff|truncatechars:50 }}</td>
<td>
{% if e.destinataer %}
<a href="{% url 'stiftung:destinataer_detail' e.destinataer.pk %}">
{{ e.destinataer }}
</a>
{% if e.kategorie == "rechnung" %}
<span class="badge bg-warning text-dark"><i class="fas fa-file-invoice me-1"></i>Rechnung</span>
{% elif e.kategorie == "destinataer" %}
<span class="badge bg-info"><i class="fas fa-user me-1"></i>Destinataer</span>
{% elif e.kategorie == "land_pacht" %}
<span class="badge bg-success"><i class="fas fa-map me-1"></i>Land/Pacht</span>
{% elif e.kategorie == "stiftungsgeschichte" %}
<span class="badge bg-dark"><i class="fas fa-landmark me-1"></i>Geschichte</span>
{% else %}
<span class="text-danger"><i class="fas fa-question-circle me-1"></i>Unbekannt</span>
<span class="badge bg-secondary">Allgemein</span>
{% endif %}
</td>
<td>{{ e.betreff|truncatechars:60 }}</td>
<td class="text-center">
{% if e.paperless_dokument_ids %}
<span class="badge bg-info">{{ e.paperless_dokument_ids|length }}</span>
<td>
{% if e.destinataer %}
<a href="{% url 'stiftung:destinataer_detail' e.destinataer.pk %}" class="text-decoration-none">
{{ e.destinataer }}
</a>
{% elif e.verwaltungskosten %}
<span class="text-info"><i class="fas fa-file-invoice-dollar me-1"></i>{{ e.verwaltungskosten.bezeichnung|truncatechars:30 }}</span>
{% else %}
<span class="text-muted"></span>
{% endif %}
@@ -173,6 +167,10 @@
<span class="badge bg-primary">Zugewiesen</span>
{% elif e.status == "verarbeitet" %}
<span class="badge bg-success">Verarbeitet</span>
{% elif e.status == "rechnung_erfasst" %}
<span class="badge bg-info">Rechnung erfasst</span>
{% elif e.status == "zahlung_gebucht" %}
<span class="badge bg-success">Bezahlt</span>
{% elif e.status == "unbekannt" %}
<span class="badge bg-danger">Unbekannt</span>
{% elif e.status == "fehler" %}
@@ -180,18 +178,9 @@
{% endif %}
</td>
<td>
<div class="btn-group btn-group-sm">
<a href="{% url 'stiftung:email_eingang_detail' e.pk %}" class="btn btn-outline-primary" title="Details">
<i class="fas fa-eye"></i>
</a>
<form method="post" action="{% url 'stiftung:email_eingang_delete' e.pk %}" class="d-inline"
onsubmit="return confirm('E-Mail wirklich löschen?');">
{% csrf_token %}
<button type="submit" class="btn btn-outline-danger" title="Löschen">
<i class="fas fa-trash-alt"></i>
</button>
</form>
</div>
<a href="{% url 'stiftung:email_eingang_detail' e.pk %}" class="btn btn-sm btn-outline-primary" title="Details">
<i class="fas fa-eye"></i>
</a>
</td>
</tr>
{% endfor %}
@@ -199,14 +188,14 @@
</table>
</div>
<!-- Pagination -->
{# Pagination #}
{% if page_obj.has_other_pages %}
<div class="d-flex justify-content-center py-3">
<nav>
<ul class="pagination mb-0">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}&q={{ search }}&status={{ status_filter }}">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}&q={{ search }}&status={{ status_filter }}&kategorie={{ kategorie_filter }}">
&laquo;
</a>
</li>
@@ -216,7 +205,7 @@
</li>
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}&q={{ search }}&status={{ status_filter }}">
<a class="page-link" href="?page={{ page_obj.next_page_number }}&q={{ search }}&status={{ status_filter }}&kategorie={{ kategorie_filter }}">
&raquo;
</a>
</li>
@@ -230,7 +219,7 @@
<div class="text-center py-5 text-muted">
<i class="fas fa-inbox fa-3x mb-3"></i>
<p>Keine E-Mails gefunden.</p>
<small>Der automatische Abruf erfolgt alle 15 Minuten. Über den Button "Jetzt abrufen" kann der Vorgang manuell ausgelöst werden.</small>
<small>Der automatische Abruf erfolgt alle 15 Minuten.</small>
</div>
{% endif %}
</div>

View File

@@ -72,18 +72,21 @@
<div class="col-12">
<h6 class="text-primary">Verwendungsnachweis</h6>
<p class="mb-3">
<a href="{% url 'stiftung:dokument_detail' foerderung.verwendungsnachweis.pk %}">
{{ foerderung.verwendungsnachweis.titel }}
</a>
{{ foerderung.verwendungsnachweis.titel }}
</p>
</div>
</div>
{% endif %}
<!-- Verknüpfte Dokumente -->
<!-- Dokumente (DMS) -->
<div class="row">
<div class="col-12">
<h6 class="text-primary">Verknüpfte Dokumente</h6>
<div class="d-flex justify-content-between align-items-center mb-2">
<h6 class="text-primary mb-0">Dokumente</h6>
<a href="{% url 'stiftung:dms_upload' %}?foerderung={{ foerderung.pk }}" class="btn btn-sm btn-success">
<i class="fas fa-upload me-1"></i>Dokument hochladen
</a>
</div>
{% if verknuepfte_dokumente %}
<div class="table-responsive">
<table class="table table-sm table-hover">
@@ -100,8 +103,7 @@
<tr>
<td>
<strong>{{ dokument.titel }}</strong>
<br>
<small class="text-muted">ID: {{ dokument.paperless_document_id }}</small>
{% if dokument.dateiname_original %}<br><small class="text-muted">{{ dokument.dateiname_original }} ({{ dokument.get_human_size }})</small>{% endif %}
</td>
<td>
<span class="badge bg-secondary">{{ dokument.get_kontext_display }}</span>
@@ -115,12 +117,15 @@
</td>
<td>
<div class="btn-group" role="group">
<a href="{{ dokument.get_paperless_url }}" target="_blank" class="btn btn-sm btn-outline-primary" title="In Paperless öffnen">
<i class="fas fa-external-link-alt"></i>
<a href="{% url 'stiftung:dms_download' dokument.pk %}" class="btn btn-sm btn-outline-primary" title="Herunterladen">
<i class="fas fa-download"></i>
</a>
<a href="{% url 'stiftung:dokument_update' dokument.pk %}" class="btn btn-sm btn-outline-warning" title="Bearbeiten">
<a href="{% url 'stiftung:dms_edit' dokument.pk %}" class="btn btn-sm btn-outline-warning" title="Bearbeiten">
<i class="fas fa-edit"></i>
</a>
<a href="{% url 'stiftung:dms_delete' dokument.pk %}" class="btn btn-sm btn-outline-danger" title="Löschen">
<i class="fas fa-trash"></i>
</a>
</div>
</td>
</tr>
@@ -128,17 +133,12 @@
</tbody>
</table>
</div>
<div class="mt-2">
<a href="{% url 'stiftung:dokument_management' %}" class="btn btn-sm btn-success">
<i class="fas fa-plus me-1"></i>Weiteres Dokument verknüpfen
</a>
</div>
{% else %}
<div class="text-center py-3">
<i class="fas fa-file-alt fa-2x text-muted mb-2"></i>
<p class="text-muted mb-2">Keine Dokumente verknüpft</p>
<a href="{% url 'stiftung:dokument_management' %}" class="btn btn-success btn-sm">
<i class="fas fa-plus me-1"></i>Erstes Dokument verknüpfen
<p class="text-muted mb-2">Keine Dokumente vorhanden</p>
<a href="{% url 'stiftung:dms_upload' %}?foerderung={{ foerderung.pk }}" class="btn btn-success btn-sm">
<i class="fas fa-upload me-1"></i>Erstes Dokument hochladen
</a>
</div>
{% endif %}

View File

@@ -123,7 +123,7 @@
</div>
{% endif %}
<div class="form-text">
Optionale Verknüpfung zu einem Dokument aus dem Paperless-System
Optionale Verknüpfung zu einem Verwendungsnachweis (Legacy)
</div>
</div>
</div>

View File

@@ -177,6 +177,35 @@
</p>
</div>
</div>
{% if geschichte_dokumente %}
<div class="card mt-3">
<div class="card-header bg-dark text-white">
<h6 class="mb-0"><i class="fas fa-landmark me-2"></i>Verfuegbare Geschichtsdokumente</h6>
</div>
<div class="card-body p-0">
<div class="list-group list-group-flush">
{% for dok in geschichte_dokumente %}
<div class="list-group-item">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="small fw-bold">{{ dok.titel|truncatechars:40 }}</div>
<small class="text-muted">{{ dok.dateiname_original }} ({{ dok.get_human_size }})</small>
<br><small class="text-muted">{{ dok.erstellt_am|date:"d.m.Y" }}</small>
</div>
<a href="{% url 'stiftung:dms_download' dok.pk %}" class="btn btn-sm btn-outline-primary" title="Herunterladen">
<i class="fas fa-download"></i>
</a>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="card-footer small text-muted">
Dokumente aus dem DMS mit Kontext "Stiftungsgeschichte". Eingegangen per E-Mail oder manuell hochgeladen.
</div>
</div>
{% endif %}
</div>
</div>
{% endblock %}

View File

@@ -303,11 +303,8 @@
{% endif %}
<div class="d-grid gap-2">
<a href="{% url 'stiftung:dokument_management' %}" class="btn btn-outline-primary btn-sm">
<i class="fas fa-link me-2"></i>Dokumente verknüpfen
</a>
<a href="mailto:paperless@vhtv-stiftung.de?subject=Dokumente für {{ land }}" class="btn btn-outline-info btn-sm">
<i class="fas fa-envelope me-2"></i>E-Mail an Paperless
<a href="{% url 'stiftung:dms_list' %}" class="btn btn-outline-primary btn-sm">
<i class="fas fa-folder-open me-2"></i>Zum DMS
</a>
</div>
</div>

View File

@@ -234,14 +234,11 @@
</div>
<div class="card-body">
<p class="text-muted mb-3">
Dokumente werden über Paperless verwaltet und verknüpft.
Dokumente werden im DMS verwaltet.
</p>
<div class="d-grid gap-2">
<a href="{% url 'stiftung:dokument_management' %}" class="btn btn-outline-primary btn-sm">
<i class="fas fa-link me-2"></i>Dokumente verknüpfen
</a>
<a href="mailto:paperless@vhtv-stiftung.de" class="btn btn-outline-info btn-sm">
<i class="fas fa-envelope me-2"></i>E-Mail an Paperless
<a href="{% url 'stiftung:dms_list' %}" class="btn btn-outline-primary btn-sm">
<i class="fas fa-folder-open me-2"></i>Zum DMS
</a>
</div>

View File

@@ -479,14 +479,14 @@
</div>
{% endif %}
<!-- Verknüpfte Dokumente -->
<!-- Dokumente (DMS) -->
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex justify-content-between align-items-center">
<h6 class="m-0 font-weight-bold text-success">
<i class="fas fa-file-alt me-2"></i>Verknüpfte Dokumente
<i class="fas fa-file-alt me-2"></i>Dokumente
</h6>
<a href="{% url 'stiftung:dokument_management' %}" class="btn btn-sm btn-success">
<i class="fas fa-plus me-2"></i>Dokument verknüpfen
<a href="{% url 'stiftung:dms_upload' %}?land={{ land.pk }}" class="btn btn-sm btn-success">
<i class="fas fa-upload me-2"></i>Dokument hochladen
</a>
</div>
<div class="card-body">
@@ -506,8 +506,7 @@
<tr>
<td>
<strong>{{ dokument.titel }}</strong>
<br>
<small class="text-muted">ID: {{ dokument.paperless_document_id }}</small>
{% if dokument.dateiname_original %}<br><small class="text-muted">{{ dokument.dateiname_original }} ({{ dokument.get_human_size }})</small>{% endif %}
</td>
<td>
<span class="badge bg-secondary">{{ dokument.get_kontext_display }}</span>
@@ -521,17 +520,14 @@
</td>
<td>
<div class="btn-group" role="group">
<a href="{{ dokument.get_paperless_url }}" target="_blank" class="btn btn-sm btn-outline-primary" title="In Paperless öffnen">
<i class="fas fa-external-link-alt"></i>
<a href="{% url 'stiftung:dms_download' dokument.pk %}" class="btn btn-sm btn-outline-primary" title="Herunterladen">
<i class="fas fa-download"></i>
</a>
<a href="{{ dokument.get_paperless_thumbnail_url }}" target="_blank" class="btn btn-sm btn-outline-info" title="Thumbnail anzeigen">
<i class="fas fa-image"></i>
</a>
<a href="{% url 'stiftung:dokument_update' dokument.pk %}" class="btn btn-sm btn-outline-warning" title="Bearbeiten">
<a href="{% url 'stiftung:dms_edit' dokument.pk %}" class="btn btn-sm btn-outline-warning" title="Bearbeiten">
<i class="fas fa-edit"></i>
</a>
<a href="{% url 'stiftung:dokument_delete' dokument.pk %}" class="btn btn-sm btn-outline-danger" title="Verknüpfung löschen">
<i class="fas fa-unlink"></i>
<a href="{% url 'stiftung:dms_delete' dokument.pk %}" class="btn btn-sm btn-outline-danger" title="Löschen">
<i class="fas fa-trash"></i>
</a>
</div>
</td>
@@ -543,10 +539,10 @@
{% else %}
<div class="text-center py-4">
<i class="fas fa-file-alt fa-3x text-muted mb-3"></i>
<h5 class="text-muted">Keine Dokumente verknüpft</h5>
<p class="text-muted">Verknüpfen Sie Dokumente aus Paperless mit dieser Länderei.</p>
<a href="{% url 'stiftung:dokument_management' %}" class="btn btn-success">
<i class="fas fa-plus me-2"></i>Erstes Dokument verknüpfen
<h5 class="text-muted">Keine Dokumente vorhanden</h5>
<p class="text-muted">Laden Sie Dokumente direkt hoch und verknüpfen Sie sie mit dieser Länderei.</p>
<a href="{% url 'stiftung:dms_upload' %}?land={{ land.pk }}" class="btn btn-success">
<i class="fas fa-upload me-2"></i>Erstes Dokument hochladen
</a>
</div>
{% endif %}

View File

@@ -188,12 +188,12 @@
</div>
</div>
<!-- Verknüpfte Dokumente -->
<!-- Dokumente (DMS) -->
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0"><i class="fas fa-folder-open me-2"></i>Verknüpfte Dokumente</h5>
<a href="/dokumente/verwaltung/" class="btn btn-sm btn-outline-primary">
<i class="fas fa-link me-1"></i>Dokument verknüpfen
<h5 class="mb-0"><i class="fas fa-folder-open me-2"></i>Dokumente</h5>
<a href="{% url 'stiftung:dms_upload' %}?verpachtung={{ landverpachtung.pk }}" class="btn btn-sm btn-outline-primary">
<i class="fas fa-upload me-1"></i>Dokument hochladen
</a>
</div>
<div class="card-body">
@@ -212,14 +212,21 @@
<tr>
<td>
<strong>{{ doc.titel|default:"Ohne Titel" }}</strong>
<br>
<small class="text-muted">Paperless-ID: {{ doc.paperless_document_id }}</small>
{% if doc.dateiname_original %}<br><small class="text-muted">{{ doc.dateiname_original }} ({{ doc.get_human_size }})</small>{% endif %}
</td>
<td>{{ doc.get_kontext_display }}</td>
<td>
<a href="{{ doc.get_paperless_url }}" target="_blank" class="btn btn-sm btn-outline-primary" title="In Paperless öffnen">
<i class="fas fa-external-link-alt"></i>
</a>
<div class="btn-group" role="group">
<a href="{% url 'stiftung:dms_download' doc.pk %}" class="btn btn-sm btn-outline-primary" title="Herunterladen">
<i class="fas fa-download"></i>
</a>
<a href="{% url 'stiftung:dms_edit' doc.pk %}" class="btn btn-sm btn-outline-warning" title="Bearbeiten">
<i class="fas fa-edit"></i>
</a>
<a href="{% url 'stiftung:dms_delete' doc.pk %}" class="btn btn-sm btn-outline-danger" title="Löschen">
<i class="fas fa-trash"></i>
</a>
</div>
</td>
</tr>
{% endfor %}
@@ -227,7 +234,7 @@
</table>
</div>
{% else %}
<p class="text-muted">Keine Dokumente verknüpft.</p>
<p class="text-muted">Keine Dokumente vorhanden.</p>
{% endif %}
</div>
</div>

View File

@@ -279,14 +279,14 @@
<!-- Legacy Verpachtungen entfernt für saubere UI -->
<!-- Verknüpfte Dokumente -->
<!-- Dokumente (DMS) -->
<div class="card shadow mb-4">
<div class="card-header bg-success text-white d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="fas fa-file-alt me-2"></i>Verknüpfte Dokumente
<i class="fas fa-file-alt me-2"></i>Dokumente
</h5>
<a href="{% url 'stiftung:dokument_management' %}" class="btn btn-light btn-sm">
<i class="fas fa-plus me-1"></i>Dokument verknüpfen
<a href="{% url 'stiftung:dms_upload' %}?paechter={{ paechter.pk }}" class="btn btn-light btn-sm">
<i class="fas fa-upload me-1"></i>Dokument hochladen
</a>
</div>
<div class="card-body">
@@ -306,8 +306,7 @@
<tr>
<td>
<strong>{{ dokument.titel }}</strong>
<br>
<small class="text-muted">ID: {{ dokument.paperless_document_id }}</small>
{% if dokument.dateiname_original %}<br><small class="text-muted">{{ dokument.dateiname_original }} ({{ dokument.get_human_size }})</small>{% endif %}
</td>
<td>
<span class="badge bg-secondary">{{ dokument.get_kontext_display }}</span>
@@ -321,17 +320,14 @@
</td>
<td>
<div class="btn-group" role="group">
<a href="{{ dokument.get_paperless_url }}" target="_blank" class="btn btn-sm btn-outline-primary" title="In Paperless öffnen">
<i class="fas fa-external-link-alt"></i>
<a href="{% url 'stiftung:dms_download' dokument.pk %}" class="btn btn-sm btn-outline-primary" title="Herunterladen">
<i class="fas fa-download"></i>
</a>
<a href="{{ dokument.get_paperless_thumbnail_url }}" target="_blank" class="btn btn-sm btn-outline-info" title="Thumbnail anzeigen">
<i class="fas fa-image"></i>
</a>
<a href="{% url 'stiftung:dokument_update' dokument.pk %}" class="btn btn-sm btn-outline-warning" title="Bearbeiten">
<a href="{% url 'stiftung:dms_edit' dokument.pk %}" class="btn btn-sm btn-outline-warning" title="Bearbeiten">
<i class="fas fa-edit"></i>
</a>
<a href="{% url 'stiftung:dokument_delete' dokument.pk %}" class="btn btn-sm btn-outline-danger" title="Verknüpfung löschen">
<i class="fas fa-unlink"></i>
<a href="{% url 'stiftung:dms_delete' dokument.pk %}" class="btn btn-sm btn-outline-danger" title="Löschen">
<i class="fas fa-trash"></i>
</a>
</div>
</td>
@@ -343,10 +339,10 @@
{% else %}
<div class="text-center py-4">
<i class="fas fa-file-alt fa-3x text-muted mb-3"></i>
<h5 class="text-muted">Keine Dokumente verknüpft</h5>
<p class="text-muted">Verknüpfen Sie Dokumente aus Paperless mit diesem Pächter.</p>
<a href="{% url 'stiftung:dokument_management' %}" class="btn btn-success">
<i class="fas fa-plus me-2"></i>Erstes Dokument verknüpfen
<h5 class="text-muted">Keine Dokumente vorhanden</h5>
<p class="text-muted">Laden Sie Dokumente direkt hoch und verknüpfen Sie sie mit diesem Pächter.</p>
<a href="{% url 'stiftung:dms_upload' %}?paechter={{ paechter.pk }}" class="btn btn-success">
<i class="fas fa-upload me-2"></i>Erstes Dokument hochladen
</a>
</div>
{% endif %}

View File

@@ -296,8 +296,8 @@
<span class="badge bg-primary ms-2">{{ verknuepfte_dokumente.count }}</span>
{% endif %}
</h6>
<a href="/dokumente/verwaltung/" class="btn btn-outline-primary btn-sm">
<i class="fas fa-link me-1"></i>Dokumentenverwaltung
<a href="{% url 'stiftung:dms_list' %}" class="btn btn-outline-primary btn-sm">
<i class="fas fa-folder-open me-1"></i>Dokumentenverwaltung
</a>
</div>
<div class="card-body">
@@ -318,10 +318,7 @@
</small>
</div>
<div class="d-flex gap-2">
<a href="/api/paperless/documents/{{ dokument.paperless_document_id }}/"
class="btn btn-outline-primary btn-sm" target="_blank" title="In Paperless öffnen">
<i class="fas fa-external-link-alt"></i>
</a>
<span class="badge bg-secondary">Legacy</span>
</div>
</div>
{% endfor %}
@@ -332,7 +329,7 @@
<small class="text-muted">
<i class="fas fa-info-circle me-1"></i>
Es werden nur die neuesten 10 Dokumente angezeigt.
<a href="/dokumente/verwaltung/" class="text-decoration-none">Alle Dokumente anzeigen</a>
<a href="{% url 'stiftung:dms_list' %}" class="text-decoration-none">Alle Dokumente anzeigen</a>
</small>
</div>
{% endif %}
@@ -342,7 +339,7 @@
<h5 class="text-muted">Keine Dokumente verknüpft</h5>
<p class="text-muted">
Verknüpfen Sie Dokumente über die
<a href="/dokumente/verwaltung/" class="text-decoration-none">Dokumentenverwaltung</a>.
<a href="{% url 'stiftung:dms_list' %}" class="text-decoration-none">Dokumentenverwaltung</a>.
</p>
</div>
{% endif %}

View File

@@ -292,66 +292,60 @@
</div>
{% endif %}
<!-- Verknüpfte Dokumente -->
{% if verknuepfte_dokumente %}
<div class="row">
<div class="col-12">
<div class="card shadow mb-4">
<div class="card-header bg-success text-white">
<h5 class="card-title mb-0">
<i class="fas fa-file-alt me-2"></i>Verknüpfte Dokumente ({{ verknuepfte_dokumente.count }})
</h5>
</div>
<div class="card-body">
<div class="row">
{% for dokument in verknuepfte_dokumente %}
<div class="col-md-6 mb-3">
<div class="card border-left-success">
<div class="card-body py-2">
<div class="d-flex justify-content-between align-items-center">
<div>
<h6 class="mb-1">{{ dokument.titel }}</h6>
<small class="text-muted">{{ dokument.kontext }}</small>
</div>
<div>
<a href="{{ dokument.paperless_url }}" target="_blank"
class="btn btn-sm btn-outline-success">
<i class="fas fa-external-link-alt"></i>
</a>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endif %}
<!-- Dokument Management Section -->
<!-- Dokumente (DMS) -->
<div class="row mb-4">
<div class="col-12">
<div class="card shadow">
<div class="card-header py-3 d-flex justify-content-between align-items-center">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-paperclip me-2"></i>Dokumente verwalten
<i class="fas fa-file-alt me-2"></i>Dokumente{% if verknuepfte_dokumente %} ({{ verknuepfte_dokumente.count }}){% endif %}
</h6>
<a href="{% url 'stiftung:dokument_create' %}?land_verpachtung_id={{ verpachtung.pk }}"
<a href="{% url 'stiftung:dms_upload' %}?verpachtung={{ verpachtung.pk }}"
class="btn btn-sm btn-success">
<i class="fas fa-plus me-1"></i>Dokument verknüpfen
<i class="fas fa-upload me-1"></i>Dokument hochladen
</a>
</div>
<div class="card-body">
{% if verknuepfte_dokumente %}
<p class="text-muted mb-3">{{ verknuepfte_dokumente.count }} Dokument{{ verknuepfte_dokumente.count|pluralize:"e" }} verknüpft</p>
<div class="table-responsive">
<table class="table table-hover table-sm align-middle">
<thead class="table-light">
<tr>
<th>Titel</th>
<th>Kontext</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for dokument in verknuepfte_dokumente %}
<tr>
<td>
<strong>{{ dokument.titel }}</strong>
{% if dokument.dateiname_original %}<br><small class="text-muted">{{ dokument.dateiname_original }} ({{ dokument.get_human_size }})</small>{% endif %}
</td>
<td><span class="badge bg-secondary">{{ dokument.get_kontext_display }}</span></td>
<td>
<div class="btn-group" role="group">
<a href="{% url 'stiftung:dms_download' dokument.pk %}" class="btn btn-sm btn-outline-primary" title="Herunterladen">
<i class="fas fa-download"></i>
</a>
<a href="{% url 'stiftung:dms_edit' dokument.pk %}" class="btn btn-sm btn-outline-warning" title="Bearbeiten">
<i class="fas fa-edit"></i>
</a>
<a href="{% url 'stiftung:dms_delete' dokument.pk %}" class="btn btn-sm btn-outline-danger" title="Löschen">
<i class="fas fa-trash"></i>
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="text-muted mb-0">
<i class="fas fa-info-circle me-2"></i>
Noch keine Dokumente mit dieser Verpachtung verknüpft.
Klicken Sie auf "Dokument verknüpfen", um Dokumente aus dem Paperless-System zu verknüpfen.
Noch keine Dokumente vorhanden.
</p>
{% endif %}
</div>

View File

@@ -341,7 +341,7 @@
</div>
<div class="card-body">
<p class="text-muted small">Dokumente können nach dem Speichern der Verpachtung verknüpft werden.</p>
<a href="{% url 'stiftung:dokument_create' %}?land_verpachtung_id={{ form.instance.pk }}"
<a href="{% url 'stiftung:dms_upload' %}?land_verpachtung_id={{ form.instance.pk }}"
class="btn btn-outline-primary btn-sm">
<i class="fas fa-plus me-1"></i>Neues Dokument
</a>