Enhanced quarterly confirmation system with approval workflow and export improvements

Features added:
-  Fixed quarterly confirmation approval system with URL pattern
-  Added re-approval and status reset functionality for quarterly confirmations
-  Synchronized quarterly approval status with support payment system
-  Enhanced Destinataer export with missing fields (anrede, titel, mobil)
-  Added quarterly confirmation data and documents to export system
-  Fixed address field display issues in destinataer template
-  Added quarterly statistics dashboard to support payment lists
-  Implemented duplicate support payment prevention and cleanup
-  Added visual indicators for quarterly-linked support payments

Technical improvements:
- Enhanced create_quarterly_support_payment() with duplicate detection
- Added get_related_support_payment() method to VierteljahresNachweis model
- Improved quarterly confirmation workflow with proper status transitions
- Added computed address property to Destinataer model
- Fixed template field mismatches (anrede, titel, mobil vs strasse, plz, ort)
- Enhanced backup system with operation tracking and cancellation

Workflow enhancements:
- Quarterly confirmations now properly sync with support payments
- Single support payment per destinataer per quarter (no duplicates)
- Approval button works for both eingereicht and geprueft status
- Reset functionality allows workflow restart
- Export includes complete quarterly data with uploaded documents
This commit is contained in:
2025-09-28 19:09:08 +02:00
parent b00cf62d87
commit acac8695fd
73 changed files with 283380 additions and 206 deletions

View File

@@ -327,6 +327,21 @@ class Destinataer(models.Model):
"""Get the most recent funding grant"""
return self.foerderung_set.order_by("-jahr", "-betrag").first()
@property
def adresse(self):
"""Construct full address from separate fields"""
parts = []
if self.strasse:
parts.append(self.strasse)
if self.plz or self.ort:
city_part = []
if self.plz:
city_part.append(self.plz)
if self.ort:
city_part.append(self.ort)
parts.append(" ".join(city_part))
return "\n".join(parts) if parts else ""
def erfuellt_voraussetzungen(self):
"""Prüft die Unterstützungsvoraussetzungen gemäß Angaben.
- Abkömmling muss True sein
@@ -347,6 +362,20 @@ class Destinataer(models.Model):
vermoegen_ok = (self.vermoegen or Decimal("0")) <= Decimal("15500")
return bool(self.ist_abkoemmling and einkommen_ok and vermoegen_ok)
@property
def adresse(self):
"""Computed address property combining strasse, plz, ort"""
parts = []
if self.strasse:
parts.append(self.strasse)
if self.plz and self.ort:
parts.append(f"{self.plz} {self.ort}")
elif self.plz:
parts.append(self.plz)
elif self.ort:
parts.append(self.ort)
return "\n".join(parts) if parts else None
def naechste_studiennachweis_termine(self):
"""Gibt die nächsten beiden Stichtage (15.03, 15.09) zurück."""
import datetime as _dt
@@ -522,6 +551,8 @@ class Land(models.Model):
max_length=20,
choices=ZAHLUNGSWEISE_CHOICES,
default="jaehrlich",
null=True,
blank=True,
verbose_name="Zahlungsweise",
)
pachtzins_pro_ha = models.DecimalField(
@@ -544,7 +575,12 @@ class Land(models.Model):
# Umsatzsteuer
ust_option = models.BooleanField(default=False, verbose_name="USt-Option")
ust_satz = models.DecimalField(
max_digits=4, decimal_places=2, default=19.00, verbose_name="USt-Satz (%)"
max_digits=4,
decimal_places=2,
default=19.00,
null=True,
blank=True,
verbose_name="USt-Satz (%)"
)
# Umlagen (Durchreichungen)
@@ -2234,6 +2270,7 @@ class BackupJob(models.Model):
("running", "Läuft"),
("completed", "Abgeschlossen"),
("failed", "Fehlgeschlagen"),
("cancelled", "Abgebrochen"),
]
TYPE_CHOICES = [
@@ -2242,9 +2279,17 @@ class BackupJob(models.Model):
("files", "Nur Dateien"),
]
OPERATION_CHOICES = [
("backup", "Backup"),
("restore", "Wiederherstellung"),
]
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# Job-Details
operation = models.CharField(
max_length=20, choices=OPERATION_CHOICES, default="backup", verbose_name="Vorgang"
)
backup_type = models.CharField(
max_length=20, choices=TYPE_CHOICES, verbose_name="Backup-Typ"
)
@@ -2725,6 +2770,20 @@ class VierteljahresNachweis(models.Model):
super().save(*args, **kwargs)
def get_related_support_payment(self):
"""Get the related support payment for this quarterly confirmation"""
from datetime import datetime
quarter_start = datetime(self.jahr, (self.quartal - 1) * 3 + 1, 1).date()
quarter_end = datetime(self.jahr, self.quartal * 3, 1).date()
return DestinataerUnterstuetzung.objects.filter(
destinataer=self.destinataer,
faellig_am__gte=quarter_start,
faellig_am__lt=quarter_end,
beschreibung__contains=f"Q{self.quartal}/{self.jahr}"
).first()
@classmethod
def get_or_create_for_period(cls, destinataer, jahr, quartal):
"""Get or create a quarterly confirmation for a specific period"""