Files
stiftung-management-system/app/templates/stiftung/email_eingang/list.html
Stiftung CEO Agent 4b21f553c3
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
feat: Email-Eingangsverarbeitung für Destinatäre implementieren
Neues System zur automatischen Verarbeitung eingehender E-Mails von
Destinatären. IMAP-Polling alle 15 Minuten via Celery Beat, automatische
Zuordnung zu Destinatären anhand der E-Mail-Adresse, Upload von Anhängen
zu Paperless-NGX.

Umfasst:
- DestinataerEmailEingang Model mit Status-Tracking
- Celery Task für IMAP-Polling und Paperless-Integration
- Web-UI (Liste + Detail) mit Such- und Filterfunktion
- Admin-Interface mit Bulk-Actions
- Agent-Dokumentation (SysAdmin, RentmeisterAI)
- Dev-Environment Modernisierung (docker compose v2)

Reviewed by: SysAdmin (STI-15), RentmeisterAI (STI-16)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:11:22 +00:00

230 lines
10 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends 'base.html' %}
{% load humanize %}
{% block title %}E-Mail-Eingang (Destinatäre) - van Hees-Theyssen-Vogel'sche Stiftung{% 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)
</h1>
<div class="d-flex gap-2">
<form method="post" action="{% url 'stiftung:email_eingang_poll_trigger' %}" class="d-inline">
{% csrf_token %}
<button type="submit" class="btn btn-outline-primary btn-sm">
<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 -->
<div class="row mb-4">
<div class="col-md-3">
<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>
</div>
</div>
<div class="col-md-3">
<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>
</div>
</div>
<div class="col-md-3">
<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>
</div>
</div>
</div>
<!-- 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">
<label class="form-label">Suche</label>
<input type="text" class="form-control" name="q" value="{{ search }}"
placeholder="Absender, Betreff, Destinatär...">
</div>
<div class="col-md-3">
<label class="form-label">Status</label>
<select class="form-select" name="status">
<option value="">Alle</option>
{% for value, label in status_choices %}
<option value="{{ value }}" {% if status_filter == value %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-2 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-search me-1"></i>Filtern
</button>
</div>
{% if search or status_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
</a>
</div>
{% endif %}
</form>
</div>
</div>
<!-- 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>
</div>
<div class="card-body p-0">
{% if page_obj %}
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th>Datum</th>
<th>Absender</th>
<th>Destinatär</th>
<th>Betreff</th>
<th>Anhänge</th>
<th>Status</th>
<th></th>
</tr>
</thead>
<tbody>
{% for e in page_obj %}
<tr>
<td class="text-nowrap">
<small>{{ e.eingangsdatum|date:"d.m.Y H:i" }}</small>
</td>
<td>
<div>{{ e.absender_name|default:e.absender_email }}</div>
{% if e.absender_name %}
<small class="text-muted">{{ e.absender_email }}</small>
{% endif %}
</td>
<td>
{% if e.destinataer %}
<a href="{% url 'stiftung:destinataer_detail' e.destinataer.pk %}">
{{ e.destinataer }}
</a>
{% else %}
<span class="text-danger"><i class="fas fa-question-circle me-1"></i>Unbekannt</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>
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td>
{% if e.status == "neu" %}
<span class="badge bg-warning text-dark">Neu</span>
{% elif e.status == "zugewiesen" %}
<span class="badge bg-primary">Zugewiesen</span>
{% elif e.status == "verarbeitet" %}
<span class="badge bg-success">Verarbeitet</span>
{% elif e.status == "unbekannt" %}
<span class="badge bg-danger">Unbekannt</span>
{% elif e.status == "fehler" %}
<span class="badge bg-secondary">Fehler</span>
{% endif %}
</td>
<td>
<a href="{% url 'stiftung:email_eingang_detail' e.pk %}" class="btn btn-sm btn-outline-primary">
<i class="fas fa-eye"></i>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- 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 }}">
&laquo;
</a>
</li>
{% endif %}
<li class="page-item disabled">
<span class="page-link">Seite {{ page_obj.number }} von {{ page_obj.paginator.num_pages }}</span>
</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 }}">
&raquo;
</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}
{% else %}
<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>
</div>
{% endif %}
</div>
</div>
{% endblock %}