Fix payment system balance integration and add calendar functionality
- Implement automated payment tracking with Django signals - Fix duplicate transaction creation with unique referenz system - Add calendar system with CRUD operations and event management - Reorganize navigation menu (rename sections, move admin functions) - Replace Geschichte editor with EasyMDE markdown editor - Add management commands for balance reconciliation - Create missing transactions for previously paid payments - Ensure account balances accurately reflect all payment activity Features added: - Calendar entries creation and administration via menu - Payment status tracking with automatic balance updates - Duplicate prevention for payment transactions - Markdown editor with live preview for Geschichte pages - Database reconciliation tools for payment/balance sync Bug fixes: - Resolved IntegrityError on payment status changes - Fixed missing account balance updates for paid payments - Prevented duplicate balance deductions on re-saves - Corrected menu structure and admin function placement
This commit is contained in:
@@ -2864,7 +2864,11 @@ class GeschichteSeite(models.Model):
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
titel = models.CharField(max_length=200, verbose_name="Titel")
|
||||
slug = models.SlugField(max_length=200, unique=True, verbose_name="URL-Slug")
|
||||
inhalt = models.TextField(verbose_name="Inhalt")
|
||||
inhalt = models.TextField(
|
||||
verbose_name="Inhalt (Markdown)",
|
||||
blank=True,
|
||||
help_text="Sie können Markdown verwenden: **fett**, *kursiv*, # Überschriften, [Links](URL), Listen, etc."
|
||||
)
|
||||
|
||||
# Metadata
|
||||
erstellt_am = models.DateTimeField(auto_now_add=True, verbose_name="Erstellt am")
|
||||
@@ -2940,3 +2944,124 @@ class GeschichteBild(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.titel} ({self.seite.titel})"
|
||||
|
||||
|
||||
class StiftungsKalenderEintrag(models.Model):
|
||||
"""Custom calendar events for foundation management"""
|
||||
|
||||
KATEGORIE_CHOICES = [
|
||||
('termin', 'Termin/Meeting'),
|
||||
('zahlung', 'Zahlungserinnerung'),
|
||||
('deadline', 'Frist/Deadline'),
|
||||
('geburtstag', 'Geburtstag'),
|
||||
('vertrag', 'Vertrag läuft aus'),
|
||||
('pruefung', 'Prüfung/Nachweis'),
|
||||
('sonstiges', 'Sonstiges'),
|
||||
]
|
||||
|
||||
PRIORITAET_CHOICES = [
|
||||
('niedrig', 'Niedrig'),
|
||||
('normal', 'Normal'),
|
||||
('hoch', 'Hoch'),
|
||||
('kritisch', 'Kritisch'),
|
||||
]
|
||||
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
titel = models.CharField(max_length=200, verbose_name="Titel")
|
||||
beschreibung = models.TextField(blank=True, verbose_name="Beschreibung")
|
||||
|
||||
# Date and time
|
||||
datum = models.DateField(verbose_name="Datum")
|
||||
uhrzeit = models.TimeField(null=True, blank=True, verbose_name="Uhrzeit")
|
||||
ganztags = models.BooleanField(default=True, verbose_name="Ganztägig")
|
||||
|
||||
# Categorization
|
||||
kategorie = models.CharField(
|
||||
max_length=20,
|
||||
choices=KATEGORIE_CHOICES,
|
||||
default='termin',
|
||||
verbose_name="Kategorie"
|
||||
)
|
||||
prioritaet = models.CharField(
|
||||
max_length=20,
|
||||
choices=PRIORITAET_CHOICES,
|
||||
default='normal',
|
||||
verbose_name="Priorität"
|
||||
)
|
||||
|
||||
# Links to related objects
|
||||
destinataer = models.ForeignKey(
|
||||
'Destinataer',
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name="Bezogener Destinatär"
|
||||
)
|
||||
verpachtung = models.ForeignKey(
|
||||
'LandVerpachtung',
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name="Bezogene Verpachtung"
|
||||
)
|
||||
|
||||
# Status and completion
|
||||
erledigt = models.BooleanField(default=False, verbose_name="Erledigt")
|
||||
erledigt_am = models.DateTimeField(null=True, blank=True, verbose_name="Erledigt am")
|
||||
|
||||
# Metadata
|
||||
erstellt_von = models.CharField(
|
||||
max_length=100,
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="Erstellt von"
|
||||
)
|
||||
erstellt_am = models.DateTimeField(auto_now_add=True, verbose_name="Erstellt am")
|
||||
aktualisiert_am = models.DateTimeField(auto_now=True, verbose_name="Aktualisiert am")
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Kalender Eintrag"
|
||||
verbose_name_plural = "Kalender Einträge"
|
||||
ordering = ['datum', 'uhrzeit']
|
||||
indexes = [
|
||||
models.Index(fields=['datum']),
|
||||
models.Index(fields=['kategorie', 'datum']),
|
||||
models.Index(fields=['erledigt', 'datum']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.datum}: {self.titel}"
|
||||
|
||||
def get_kategorie_icon(self):
|
||||
icons = {
|
||||
'termin': 'fas fa-calendar-alt',
|
||||
'zahlung': 'fas fa-euro-sign',
|
||||
'deadline': 'fas fa-exclamation-triangle',
|
||||
'geburtstag': 'fas fa-birthday-cake',
|
||||
'vertrag': 'fas fa-file-contract',
|
||||
'pruefung': 'fas fa-clipboard-check',
|
||||
'sonstiges': 'fas fa-calendar',
|
||||
}
|
||||
return icons.get(self.kategorie, 'fas fa-calendar')
|
||||
|
||||
def get_prioritaet_color(self):
|
||||
colors = {
|
||||
'niedrig': 'success',
|
||||
'normal': 'primary',
|
||||
'hoch': 'warning',
|
||||
'kritisch': 'danger',
|
||||
}
|
||||
return colors.get(self.prioritaet, 'primary')
|
||||
|
||||
def is_overdue(self):
|
||||
"""Check if event is overdue (past due and not completed)"""
|
||||
if self.erledigt:
|
||||
return False
|
||||
return self.datum < timezone.now().date()
|
||||
|
||||
def is_upcoming(self, days=7):
|
||||
"""Check if event is upcoming within specified days"""
|
||||
if self.erledigt:
|
||||
return False
|
||||
today = timezone.now().date()
|
||||
return today <= self.datum <= (today + timezone.timedelta(days=days))
|
||||
|
||||
Reference in New Issue
Block a user