Phase 2: Destinatär-Timeline, Nachweis-Board, Zahlungs-Pipeline & Pächter-Workflow
2a. Destinatär-Timeline (/destinataere/<pk>/timeline/)
- Chronologische Ansicht aller Events (Zahlungen, Nachweise, E-Mails, Notizen)
- Filter nach Typ via GET-Parameter
2b. Nachweis-Board (/nachweis-board/)
- Quartals-Übersicht aller aktiver Destinatäre (Q1–Q4) in einer Tabellenansicht
- Batch-Erinnerung: erzeugt Audit-Log-Einträge für säumige Destinatäre
- Semester-Logik erhalten (15.03 / 15.09 Fristen)
2c. Zahlungs-Pipeline (/zahlungs-pipeline/)
- 5-Stufen-Kanban: Offen → Nachweis eingereicht → Freigegeben → Überwiesen → Abgeschlossen
- Vier-Augen-Prinzip: can_be_freigegeben() prüft anderen Nutzer als Ersteller
- SEPA pain.001 XML-Export (/sepa-export/) für freigegebene Zahlungen
- Neue Status-Werte: nachweis_eingereicht, freigegeben, abgeschlossen
- Neue Felder: freigegeben_von, freigegeben_am, erstellt_von
2d. Pächter-Workflow (/paechter/workflow/)
- Pipeline nach Restlaufzeit: abgelaufen / <6M / 6–24M / >24M / unbefristet
- Ausstehende Jahresabrechnungen (Vorjahr ohne Abrechnung)
- Pachtanpassungen fällig (Verträge > 5 Jahre laufend)
- Top-Pächter nach Gesamtfläche
Sidebar-Navigation um Pipeline, Nachweis-Board und Pacht-Workflow erweitert.
Migration 0047 erzeugt und angewendet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -346,13 +346,25 @@ class DestinataerUnterstuetzung(models.Model):
|
||||
"""Geplante/ausgeführte Unterstützungszahlungen an Destinatäre"""
|
||||
|
||||
STATUS_CHOICES = [
|
||||
("geplant", "Geplant"),
|
||||
("geplant", "Offen"),
|
||||
("faellig", "Fällig"),
|
||||
("nachweis_eingereicht", "Nachweis eingereicht"),
|
||||
("freigegeben", "Freigegeben (4-Augen)"),
|
||||
("in_bearbeitung", "In Bearbeitung"),
|
||||
("ausgezahlt", "Ausgezahlt"),
|
||||
("ausgezahlt", "Überwiesen"),
|
||||
("abgeschlossen", "Abgeschlossen"),
|
||||
("storniert", "Storniert"),
|
||||
]
|
||||
|
||||
# Pipeline-stage für Zahlungsstatus-Anzeige
|
||||
PIPELINE_STAGES = [
|
||||
("geplant", "Offen"),
|
||||
("nachweis_eingereicht", "Nachweis eingereicht"),
|
||||
("freigegeben", "Freigegeben"),
|
||||
("ausgezahlt", "Überwiesen"),
|
||||
("abgeschlossen", "Abgeschlossen"),
|
||||
]
|
||||
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
destinataer = models.ForeignKey(
|
||||
"Destinataer",
|
||||
@@ -392,9 +404,32 @@ class DestinataerUnterstuetzung(models.Model):
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="ausgezahlte_unterstuetzungen",
|
||||
verbose_name="Ausgezahlt von",
|
||||
)
|
||||
|
||||
# 4-Augen-Prinzip: Freigabe durch zweiten Nutzer
|
||||
freigegeben_am = models.DateField(
|
||||
null=True, blank=True, verbose_name="Freigegeben am"
|
||||
)
|
||||
freigegeben_von = models.ForeignKey(
|
||||
"auth.User",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="freigegebene_unterstuetzungen",
|
||||
verbose_name="Freigegeben von (4-Augen)",
|
||||
help_text="Muss ein anderer Nutzer als der Ersteller sein (Vier-Augen-Prinzip)",
|
||||
)
|
||||
erstellt_von = models.ForeignKey(
|
||||
"auth.User",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="erstellte_unterstuetzungen",
|
||||
verbose_name="Erstellt von",
|
||||
)
|
||||
|
||||
# Link to recurrent payment template if this was auto-generated
|
||||
wiederkehrend_von = models.ForeignKey(
|
||||
"UnterstuetzungWiederkehrend",
|
||||
@@ -431,7 +466,29 @@ class DestinataerUnterstuetzung(models.Model):
|
||||
|
||||
def can_be_marked_paid(self):
|
||||
"""Check if payment can be marked as paid"""
|
||||
return self.status in ["geplant", "faellig", "in_bearbeitung"]
|
||||
return self.status in ["geplant", "faellig", "nachweis_eingereicht", "freigegeben", "in_bearbeitung"]
|
||||
|
||||
def can_be_freigegeben(self, requesting_user):
|
||||
"""4-Augen: Freigabe nur durch anderen Nutzer als Ersteller"""
|
||||
if self.status not in ["nachweis_eingereicht", "faellig", "in_bearbeitung"]:
|
||||
return False
|
||||
if self.erstellt_von and self.erstellt_von == requesting_user:
|
||||
return False # Selber Nutzer darf nicht freigeben
|
||||
return True
|
||||
|
||||
def get_pipeline_stage(self):
|
||||
"""Gibt die Pipeline-Stufe als Integer zurück (1-5)"""
|
||||
stage_map = {
|
||||
"geplant": 1,
|
||||
"faellig": 2,
|
||||
"nachweis_eingereicht": 2,
|
||||
"in_bearbeitung": 3,
|
||||
"freigegeben": 3,
|
||||
"ausgezahlt": 4,
|
||||
"abgeschlossen": 5,
|
||||
"storniert": 0,
|
||||
}
|
||||
return stage_map.get(self.status, 1)
|
||||
|
||||
|
||||
class UnterstuetzungWiederkehrend(models.Model):
|
||||
|
||||
Reference in New Issue
Block a user