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:
@@ -1104,34 +1104,79 @@ class VierteljahresNachweis(models.Model):
|
||||
return None
|
||||
|
||||
|
||||
class DestinataerEmailEingang(models.Model):
|
||||
class EmailEingang(models.Model):
|
||||
"""
|
||||
Erfasst eingehende E-Mails von Destinatären.
|
||||
Erfasst eingehende E-Mails (Destinataere, Rechnungen, Grundstuecke, Allgemein).
|
||||
|
||||
Wird automatisch durch den Celery-Task `poll_destinataer_emails` befüllt,
|
||||
der das IMAP-Postfach der Stiftung (paperless@vhtv-stiftung.de) überwacht.
|
||||
Anhänge werden automatisch in Paperless-NGX hochgeladen und als DokumentLink
|
||||
mit dem jeweiligen Destinatär verknüpft.
|
||||
Wird automatisch durch den Celery-Task `poll_emails` befuellt,
|
||||
der das IMAP-Postfach der Stiftung (paperless@vhtv-stiftung.de) ueberwacht.
|
||||
Anhaenge werden direkt als DokumentDatei im Django-DMS gespeichert.
|
||||
"""
|
||||
|
||||
KATEGORIE_CHOICES = [
|
||||
("destinataer", "Destinataer"),
|
||||
("rechnung", "Rechnung"),
|
||||
("land_pacht", "Grundstueck / Pacht"),
|
||||
("stiftungsgeschichte", "Stiftungsgeschichte"),
|
||||
("allgemein", "Allgemein"),
|
||||
]
|
||||
|
||||
STATUS_CHOICES = [
|
||||
("neu", "Neu / Unbearbeitet"),
|
||||
("zugewiesen", "Destinatär zugewiesen"),
|
||||
("zugewiesen", "Destinataer zugewiesen"),
|
||||
("verarbeitet", "Verarbeitet"),
|
||||
("rechnung_erfasst", "Rechnung erfasst"),
|
||||
("zahlung_gebucht", "Zahlung gebucht"),
|
||||
("unbekannt", "Unbekannter Absender"),
|
||||
("fehler", "Fehler bei Verarbeitung"),
|
||||
]
|
||||
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
|
||||
# Verknüpfung zum Destinatär (None = unbekannter Absender)
|
||||
# Klassifizierung
|
||||
kategorie = models.CharField(
|
||||
max_length=20,
|
||||
choices=KATEGORIE_CHOICES,
|
||||
default="allgemein",
|
||||
verbose_name="Kategorie",
|
||||
)
|
||||
|
||||
# Verknuepfung zum Destinataer (None = kein Destinataer-Bezug)
|
||||
destinataer = models.ForeignKey(
|
||||
Destinataer,
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="email_eingaenge",
|
||||
verbose_name="Destinatär",
|
||||
verbose_name="Destinataer",
|
||||
)
|
||||
|
||||
# Verknuepfung zu Verwaltungskosten (Rechnungsworkflow)
|
||||
verwaltungskosten = models.ForeignKey(
|
||||
"Verwaltungskosten",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="email_eingaenge",
|
||||
verbose_name="Verwaltungskosten / Rechnung",
|
||||
)
|
||||
|
||||
# Verknuepfung zu Land / Verpachtung
|
||||
land = models.ForeignKey(
|
||||
"Land",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="email_eingaenge",
|
||||
verbose_name="Laenderei",
|
||||
)
|
||||
verpachtung = models.ForeignKey(
|
||||
"LandVerpachtung",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="email_eingaenge",
|
||||
verbose_name="Verpachtung",
|
||||
)
|
||||
|
||||
# E-Mail-Metadaten
|
||||
@@ -1143,12 +1188,21 @@ class DestinataerEmailEingang(models.Model):
|
||||
eingangsdatum = models.DateTimeField(verbose_name="Eingangsdatum")
|
||||
email_text = models.TextField(blank=True, verbose_name="E-Mail-Text")
|
||||
|
||||
# Anhänge: Liste der Paperless-Dokument-IDs (JSON-Format)
|
||||
# Anhaenge: DMS-Dokumente (Phase 3 – DokumentDatei)
|
||||
dokument_dateien = models.ManyToManyField(
|
||||
"DokumentDatei",
|
||||
blank=True,
|
||||
related_name="email_eingaenge",
|
||||
verbose_name="DMS-Dokumente (Anhaenge)",
|
||||
help_text="Automatisch befuellte Anhaenge als Django-DMS-Dateien.",
|
||||
)
|
||||
|
||||
# Anhaenge: Liste der Paperless-Dokument-IDs (JSON-Format, deprecated)
|
||||
paperless_dokument_ids = models.JSONField(
|
||||
default=list,
|
||||
blank=True,
|
||||
verbose_name="Paperless Dokument-IDs (Anhänge)",
|
||||
help_text="Automatisch befüllte Liste der hochgeladenen Anhänge in Paperless-NGX",
|
||||
verbose_name="Paperless Dokument-IDs (Anhaenge, veraltet)",
|
||||
help_text="Veraltet – wird nach vollstaendiger Migration entfernt. Neue Anhaenge in dokument_dateien.",
|
||||
)
|
||||
|
||||
# Verarbeitungsstatus
|
||||
@@ -1182,8 +1236,8 @@ class DestinataerEmailEingang(models.Model):
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Erfasst am")
|
||||
|
||||
class Meta:
|
||||
verbose_name = "E-Mail-Eingang (Destinatär)"
|
||||
verbose_name_plural = "E-Mail-Eingänge (Destinatäre)"
|
||||
verbose_name = "E-Mail-Eingang"
|
||||
verbose_name_plural = "E-Mail-Eingaenge"
|
||||
ordering = ["-eingangsdatum"]
|
||||
|
||||
def __str__(self):
|
||||
@@ -1191,10 +1245,18 @@ class DestinataerEmailEingang(models.Model):
|
||||
return f"[{self.eingangsdatum.strftime('%d.%m.%Y')}] {dest}: {self.betreff[:60]}"
|
||||
|
||||
def get_paperless_links(self):
|
||||
"""Gibt Liste der Paperless-Dokument-URLs zurück."""
|
||||
"""Gibt Liste der Paperless-Dokument-URLs zurueck (deprecated)."""
|
||||
from django.conf import settings
|
||||
base = settings.PAPERLESS_API_URL or ""
|
||||
return [
|
||||
f"{base}/documents/{doc_id}/"
|
||||
for doc_id in (self.paperless_dokument_ids or [])
|
||||
]
|
||||
|
||||
def get_dms_dokumente(self):
|
||||
"""Gibt alle verknuepften DokumentDatei-Objekte zurueck."""
|
||||
return self.dokument_dateien.all()
|
||||
|
||||
|
||||
# Backward-compatible alias
|
||||
DestinataerEmailEingang = EmailEingang
|
||||
|
||||
Reference in New Issue
Block a user