import re from django import forms from django.core.exceptions import ValidationError from ..models import BankTransaction, Rentmeister, StiftungsKonto, Verwaltungskosten class RentmeisterForm(forms.ModelForm): """Form für das Erstellen und Bearbeiten von Rentmeistern""" class Meta: model = Rentmeister fields = [ "anrede", "vorname", "nachname", "titel", "email", "telefon", "mobil", "strasse", "plz", "ort", "iban", "bic", "bank_name", "seit_datum", "bis_datum", "aktiv", "monatliche_verguetung", "km_pauschale", "notizen", ] widgets = { "anrede": forms.Select(attrs={"class": "form-select"}), "vorname": forms.TextInput(attrs={"class": "form-control"}), "nachname": forms.TextInput(attrs={"class": "form-control"}), "titel": forms.TextInput(attrs={"class": "form-control"}), "email": forms.EmailInput(attrs={"class": "form-control"}), "telefon": forms.TextInput(attrs={"class": "form-control"}), "mobil": forms.TextInput(attrs={"class": "form-control"}), "strasse": forms.TextInput(attrs={"class": "form-control"}), "plz": forms.TextInput(attrs={"class": "form-control"}), "ort": forms.TextInput(attrs={"class": "form-control"}), "iban": forms.TextInput( attrs={"class": "form-control", "placeholder": "DE89370400440532013000"} ), "bic": forms.TextInput( attrs={"class": "form-control", "placeholder": "COBADEFFXXX"} ), "bank_name": forms.TextInput(attrs={"class": "form-control"}), "seit_datum": forms.DateInput( attrs={"class": "form-control", "type": "date"} ), "bis_datum": forms.DateInput( attrs={"class": "form-control", "type": "date"} ), "aktiv": forms.CheckboxInput(attrs={"class": "form-check-input"}), "monatliche_verguetung": forms.NumberInput( attrs={"class": "form-control", "step": "0.01"} ), "km_pauschale": forms.NumberInput( attrs={"class": "form-control", "step": "0.01", "value": "0.30"} ), "notizen": forms.Textarea(attrs={"class": "form-control", "rows": 4}), } labels = { "anrede": "Anrede", "vorname": "Vorname *", "nachname": "Nachname *", "titel": "Titel", "email": "E-Mail", "telefon": "Telefon", "mobil": "Mobil", "strasse": "Straße", "plz": "PLZ", "ort": "Ort", "iban": "IBAN", "bic": "BIC", "bank_name": "Bank", "seit_datum": "Rentmeister seit *", "bis_datum": "Rentmeister bis", "aktiv": "Aktiv", "monatliche_verguetung": "Monatliche Vergütung (€)", "km_pauschale": "Kilometerpauschale (€/km)", "notizen": "Notizen", } help_texts = { "iban": "Internationale Bankkontonummer für Abrechnungen", "km_pauschale": "Standard: 0,30 € pro Kilometer", "seit_datum": "Datum des Amtsantritts als Rentmeister", "bis_datum": "Leer lassen für aktive Rentmeister", } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Markiere Pflichtfelder self.fields["vorname"].required = True self.fields["nachname"].required = True self.fields["seit_datum"].required = True def clean_iban(self): """Validierung der IBAN""" iban = self.cleaned_data.get("iban") if iban: # Entferne Leerzeichen und konvertiere zu Großbuchstaben iban = re.sub(r"\s+", "", iban.upper()) # Einfache IBAN-Längenvalidierung für deutsche IBANs if iban.startswith("DE") and len(iban) != 22: raise ValidationError("Deutsche IBANs müssen 22 Zeichen lang sein.") # Speichere die bereinigte IBAN return iban return iban def clean_plz(self): """Validierung der PLZ""" plz = self.cleaned_data.get("plz") if plz and not re.match(r"^\d{5}$", plz): raise ValidationError("PLZ muss aus 5 Ziffern bestehen.") return plz def clean(self): """Übergreifende Validierung""" from django.utils.dateparse import parse_date cleaned_data = super().clean() seit_datum = cleaned_data.get("seit_datum") bis_datum = cleaned_data.get("bis_datum") # Helper function to ensure we have date objects def ensure_date(date_value): if not date_value: return None if isinstance(date_value, str): return parse_date(date_value) return date_value # Convert to date objects if they're strings seit_datum = ensure_date(seit_datum) bis_datum = ensure_date(bis_datum) # Prüfe Datum-Logik if seit_datum and bis_datum and bis_datum <= seit_datum: raise ValidationError("Das End-Datum muss nach dem Start-Datum liegen.") return cleaned_data class StiftungsKontoForm(forms.ModelForm): """Form für das Erstellen und Bearbeiten von Stiftungskonten""" class Meta: model = StiftungsKonto fields = [ "kontoname", "bank_name", "iban", "bic", "konto_typ", "saldo", "saldo_datum", "zinssatz", "laufzeit_bis", "aktiv", "notizen", ] widgets = { "kontoname": forms.TextInput(attrs={"class": "form-control"}), "bank_name": forms.TextInput(attrs={"class": "form-control"}), "iban": forms.TextInput( attrs={"class": "form-control", "placeholder": "DE89370400440532013000"} ), "bic": forms.TextInput( attrs={"class": "form-control", "placeholder": "COBADEFFXXX"} ), "konto_typ": forms.Select(attrs={"class": "form-select"}), "saldo": forms.NumberInput(attrs={"class": "form-control", "step": "0.01"}), "saldo_datum": forms.DateInput( attrs={"class": "form-control", "type": "date"} ), "zinssatz": forms.NumberInput( attrs={"class": "form-control", "step": "0.01"} ), "laufzeit_bis": forms.DateInput( attrs={"class": "form-control", "type": "date"} ), "aktiv": forms.CheckboxInput(attrs={"class": "form-check-input"}), "notizen": forms.Textarea(attrs={"class": "form-control", "rows": 3}), } class VerwaltungskostenForm(forms.ModelForm): """Form für das Erstellen und Bearbeiten von Verwaltungskosten""" class Meta: model = Verwaltungskosten fields = [ "bezeichnung", "kategorie", "betrag", "datum", "status", "rentmeister", "zahlungskonto", "quellkonto", "lieferant_firma", "rechnungsnummer", "km_anzahl", "km_satz", "von_ort", "nach_ort", "zweck", "beschreibung", "notizen", ] widgets = { "bezeichnung": forms.TextInput(attrs={"class": "form-control"}), "kategorie": forms.Select(attrs={"class": "form-select"}), "betrag": forms.NumberInput( attrs={"class": "form-control", "step": "0.01"} ), "datum": forms.DateInput(attrs={"class": "form-control", "type": "date"}), "status": forms.Select(attrs={"class": "form-select"}), "rentmeister": forms.Select(attrs={"class": "form-select"}), "zahlungskonto": forms.Select(attrs={"class": "form-select"}), "quellkonto": forms.Select(attrs={"class": "form-select"}), "lieferant_firma": forms.TextInput(attrs={"class": "form-control"}), "rechnungsnummer": forms.TextInput(attrs={"class": "form-control"}), "km_anzahl": forms.NumberInput( attrs={"class": "form-control", "step": "0.1"} ), "km_satz": forms.NumberInput( attrs={"class": "form-control", "step": "0.01"} ), "von_ort": forms.TextInput(attrs={"class": "form-control"}), "nach_ort": forms.TextInput(attrs={"class": "form-control"}), "zweck": forms.TextInput(attrs={"class": "form-control"}), "beschreibung": forms.Textarea(attrs={"class": "form-control", "rows": 3}), "notizen": forms.Textarea(attrs={"class": "form-control", "rows": 2}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Filtere nur aktive Rentmeister und Konten self.fields["rentmeister"].queryset = Rentmeister.objects.filter(aktiv=True) self.fields["zahlungskonto"].queryset = StiftungsKonto.objects.filter( aktiv=True ) self.fields["quellkonto"].queryset = StiftungsKonto.objects.filter(aktiv=True) # Standardwerte setzen if not self.instance.pk: # Nur bei neuen Objekten # Standard km_satz auf 0.30 Euro setzen self.fields["km_satz"].initial = 0.30 class BankTransactionForm(forms.ModelForm): """Form für das Bearbeiten von Banktransaktionen""" class Meta: model = BankTransaction fields = [ "konto", "datum", "valuta", "betrag", "waehrung", "verwendungszweck", "empfaenger_zahlungspflichtiger", "iban_gegenpartei", "bic_gegenpartei", "transaction_type", "status", "kommentare", "verwaltungskosten", ] widgets = { "konto": forms.Select(attrs={"class": "form-select"}), "datum": forms.DateInput(attrs={"class": "form-control", "type": "date"}), "valuta": forms.DateInput(attrs={"class": "form-control", "type": "date"}), "betrag": forms.NumberInput( attrs={"class": "form-control", "step": "0.01"} ), "waehrung": forms.TextInput(attrs={"class": "form-control"}), "verwendungszweck": forms.Textarea( attrs={"class": "form-control", "rows": 3} ), "empfaenger_zahlungspflichtiger": forms.TextInput( attrs={"class": "form-control"} ), "iban_gegenpartei": forms.TextInput(attrs={"class": "form-control"}), "bic_gegenpartei": forms.TextInput(attrs={"class": "form-control"}), "transaction_type": forms.Select(attrs={"class": "form-select"}), "status": forms.Select(attrs={"class": "form-select"}), "kommentare": forms.Textarea(attrs={"class": "form-control", "rows": 4}), "verwaltungskosten": forms.Select(attrs={"class": "form-select"}), } class BankImportForm(forms.Form): """Form für den Import von Bankdaten""" konto = forms.ModelChoiceField( queryset=StiftungsKonto.objects.filter(aktiv=True), widget=forms.Select(attrs={"class": "form-select"}), label="Zielkonto", ) datei = forms.FileField( widget=forms.FileInput(attrs={"class": "form-control", "accept": ".csv,.txt"}), label="Bankdatei", help_text="Unterstützte Formate: CSV, TXT (Sparkasse, Volksbank, etc.)", ) encoding = forms.ChoiceField( choices=[ ("utf-8", "UTF-8"), ("latin1", "Latin-1 / ISO-8859-1"), ("cp1252", "Windows-1252"), ], initial="utf-8", widget=forms.Select(attrs={"class": "form-select"}), label="Zeichenkodierung", ) delimiter = forms.ChoiceField( choices=[ (";", "Semikolon (;)"), (",", "Komma (,)"), ("\t", "Tab"), ], initial=";", widget=forms.Select(attrs={"class": "form-select"}), label="Trennzeichen", ) skip_header = forms.BooleanField( initial=True, required=False, widget=forms.CheckboxInput(attrs={"class": "form-check-input"}), label="Erste Zeile überspringen (Spaltenüberschriften)", )