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:
@@ -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"""
|
||||
|
||||
Reference in New Issue
Block a user