feat: Email-Eingangsverarbeitung für Destinatäre implementieren
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

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>
This commit is contained in:
Stiftung CEO Agent
2026-03-09 21:11:22 +00:00
parent 6c8ddbb4f0
commit 4b21f553c3
16 changed files with 1554 additions and 49 deletions

View File

@@ -7,7 +7,8 @@ from django.utils.safestring import mark_safe
from . import models
from .models import (AppConfiguration, AuditLog, BackupJob, BankTransaction,
CSVImport, Destinataer, DestinataerUnterstuetzung,
CSVImport, Destinataer, DestinataerEmailEingang,
DestinataerUnterstuetzung,
DokumentLink, Foerderung, Land, LandVerpachtung, Paechter, Person,
Rentmeister, StiftungsKonto, UnterstuetzungWiederkehrend,
Verwaltungskosten, VierteljahresNachweis)
@@ -1157,6 +1158,76 @@ class VierteljahresNachweisAdmin(admin.ModelAdmin):
mark_as_needs_revision.short_description = "Nachbesserung erforderlich markieren"
@admin.register(DestinataerEmailEingang)
class DestinataerEmailEingangAdmin(admin.ModelAdmin):
list_display = [
"eingangsdatum",
"absender_email",
"absender_name",
"destinataer_link",
"betreff_kurz",
"anzahl_anhaenge",
"status",
"created_at",
]
list_filter = ["status", "eingangsdatum"]
search_fields = [
"absender_email",
"absender_name",
"betreff",
"destinataer__vorname",
"destinataer__nachname",
]
readonly_fields = ["created_at", "absender_email", "absender_name", "eingangsdatum",
"email_text", "paperless_dokument_ids", "fehler_details"]
raw_id_fields = ["destinataer", "quartalsnachweis"]
date_hierarchy = "eingangsdatum"
ordering = ["-eingangsdatum"]
fieldsets = [
("E-Mail-Metadaten", {
"fields": ["eingangsdatum", "absender_name", "absender_email", "betreff"],
}),
("Zuordnung", {
"fields": ["destinataer", "status", "quartalsnachweis"],
}),
("Inhalt & Anhänge", {
"fields": ["email_text", "paperless_dokument_ids"],
}),
("Notizen & Fehler", {
"fields": ["notizen", "fehler_details"],
"classes": ["collapse"],
}),
("System", {
"fields": ["created_at"],
"classes": ["collapse"],
}),
]
def destinataer_link(self, obj):
if obj.destinataer:
url = reverse("admin:stiftung_destinataer_change", args=[obj.destinataer.pk])
return format_html('<a href="{}">{}</a>', url, obj.destinataer)
return format_html('<span style="color:red;"></span>')
destinataer_link.short_description = "Destinatär"
def betreff_kurz(self, obj):
return (obj.betreff or "")[:60]
betreff_kurz.short_description = "Betreff"
def anzahl_anhaenge(self, obj):
n = len(obj.paperless_dokument_ids or [])
return n if n else ""
anzahl_anhaenge.short_description = "Anhänge"
actions = ["mark_verarbeitet"]
def mark_verarbeitet(self, request, queryset):
updated = queryset.filter(status__in=["neu", "zugewiesen"]).update(status="verarbeitet")
self.message_user(request, f"{updated} E-Mail(s) als verarbeitet markiert.")
mark_verarbeitet.short_description = "Als verarbeitet markieren"
# Customize admin site
admin.site.site_header = "Stiftungsverwaltung Administration"
admin.site.site_title = "Stiftungsverwaltung Admin"