# views/foerderung.py # Phase 0: Vision 2026 – Code-Refactoring import csv import io import json import os import time from datetime import datetime, timedelta, date from decimal import Decimal import qrcode import qrcode.image.svg import requests from django.conf import settings from django.contrib import messages from django.contrib.auth.decorators import login_required from django.core.paginator import Paginator from django.db.models import (Avg, Count, DecimalField, F, IntegerField, Q, Sum, Value) from django.db.models.functions import Cast, Coalesce, NullIf, Replace from django.http import HttpResponse, JsonResponse from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils import timezone from django.views.decorators.csrf import csrf_exempt from django_otp.decorators import otp_required from django_otp.plugins.otp_totp.models import TOTPDevice from django_otp.plugins.otp_static.models import StaticDevice, StaticToken from django_otp.util import random_hex from rest_framework.decorators import api_view from rest_framework.response import Response from stiftung.models import (AppConfiguration, AuditLog, BackupJob, BankTransaction, BriefVorlage, CSVImport, Destinataer, DestinataerEmailEingang, DestinataerNotiz, DestinataerUnterstuetzung, DokumentDatei, DokumentLink, Foerderung, GeschichteBild, GeschichteSeite, Land, LandAbrechnung, LandVerpachtung, Paechter, Person, Rentmeister, StiftungsKalenderEintrag, StiftungsKonto, UnterstuetzungWiederkehrend, Veranstaltung, Veranstaltungsteilnehmer, Verwaltungskosten, VierteljahresNachweis) from stiftung.forms import ( DestinataerForm, DestinataerUnterstuetzungForm, DestinataerNotizForm, FoerderungForm, GeschichteBildForm, GeschichteSeiteForm, LandForm, LandVerpachtungForm, LandAbrechnungForm, PaechterForm, DokumentLinkForm, RentmeisterForm, StiftungsKontoForm, VerwaltungskostenForm, BankTransactionForm, BankImportForm, UnterstuetzungForm, UnterstuetzungWiederkehrendForm, UnterstuetzungMarkAsPaidForm, VierteljahresNachweisForm, UserCreationForm, UserUpdateForm, PasswordChangeForm, UserPermissionForm, TwoFactorSetupForm, TwoFactorVerifyForm, TwoFactorDisableForm, BackupTokenRegenerateForm, PersonForm, VeranstaltungForm, VeranstaltungsteilnehmerForm, ) @login_required def foerderung_list(request): """List all funding grants with filtering and pagination""" foerderungen = Foerderung.objects.select_related( "destinataer", "verwendungsnachweis" ).all() # Check for export request - handle both GET and POST export_format = ( request.POST.get("format") if request.method == "POST" else request.GET.get("format", "") ) selected_ids_param = ( request.POST.get("selected_entries", "") if request.method == "POST" else request.GET.get("selected_entries", "") ) selected_ids = ( [id for id in selected_ids_param.split(",") if id] if selected_ids_param else [] ) # Filtering jahr = request.GET.get("jahr") kategorie = request.GET.get("kategorie") status = request.GET.get("status") destinataer = request.GET.get("destinataer") if jahr: foerderungen = foerderungen.filter(jahr=int(jahr)) if kategorie: foerderungen = foerderungen.filter(kategorie=kategorie) if status: foerderungen = foerderungen.filter(status=status) if destinataer: foerderungen = foerderungen.filter(destinataer__nachname__icontains=destinataer) # Handle exports if export_format == "csv": return export_foerderungen_csv(request, foerderungen, selected_ids) elif export_format == "pdf": return export_foerderungen_pdf(request, foerderungen, selected_ids) # Pagination paginator = Paginator(foerderungen, 25) page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) # Statistics total_betrag = foerderungen.aggregate(total=Sum("betrag"))["total"] or 0 avg_betrag = foerderungen.aggregate(avg=Avg("betrag"))["avg"] or 0 # Year choices for filters jahre = sorted( set(list(Foerderung.objects.values_list("jahr", flat=True))), reverse=True ) context = { "page_obj": page_obj, "foerderungen": foerderungen, # Add for counting "total_betrag": total_betrag, "avg_betrag": avg_betrag, "kategorien": Foerderung.KATEGORIE_CHOICES, "status_choices": Foerderung.STATUS_CHOICES, "filter_jahr": jahr, "filter_kategorie": kategorie, "filter_status": status, "filter_person": destinataer, "jahre": jahre, } return render(request, "stiftung/foerderung_list.html", context) @login_required def foerderung_detail(request, pk): """Show details of a specific funding grant""" foerderung = get_object_or_404( Foerderung.objects.select_related("person", "verwendungsnachweis"), pk=pk ) # Alle mit dieser Förderung verknüpften Dokumente laden verknuepfte_dokumente = DokumentDatei.objects.filter( foerderung=foerderung ).order_by("kontext", "titel") context = { "foerderung": foerderung, "verknuepfte_dokumente": verknuepfte_dokumente, "title": f"Förderung: {foerderung}", } return render(request, "stiftung/foerderung_detail.html", context) @login_required def foerderung_create(request): """Create a new funding grant""" # Get destinataer from URL parameter if provided destinataer_id = request.GET.get("destinataer") initial = {} if destinataer_id: initial["destinataer"] = destinataer_id if request.method == "POST": form = FoerderungForm(request.POST) if form.is_valid(): foerderung = form.save() messages.success( request, f"Förderung für {foerderung.destinataer} wurde erfolgreich erstellt.", ) return redirect("stiftung:foerderung_detail", pk=foerderung.pk) else: form = FoerderungForm(initial=initial) context = { "form": form, "title": "Neue Förderung erstellen", } return render(request, "stiftung/foerderung_form.html", context) @login_required def foerderung_update(request, pk): """Update an existing funding grant""" foerderung = get_object_or_404(Foerderung, pk=pk) if request.method == "POST": form = FoerderungForm(request.POST, instance=foerderung) if form.is_valid(): form.save() messages.success( request, f"Förderung für {foerderung.person} wurde erfolgreich aktualisiert.", ) return redirect("stiftung:foerderung_detail", pk=foerderung.pk) else: form = FoerderungForm(instance=foerderung) context = { "form": form, "foerderung": foerderung, "title": f"Förderung bearbeiten: {foerderung}", } return render(request, "stiftung/foerderung_form.html", context) @login_required def foerderung_delete(request, pk): """Delete a funding grant""" foerderung = get_object_or_404(Foerderung, pk=pk) if request.method == "POST": # Get the recipient name before deletion recipient_name = ( foerderung.destinataer.get_full_name() if foerderung.destinataer else ( foerderung.person.get_full_name() if foerderung.person else "Unbekannter Empfänger" ) ) foerderung.delete() messages.success( request, f"Förderung für {recipient_name} wurde erfolgreich gelöscht." ) return redirect("stiftung:foerderung_list") context = { "foerderung": foerderung, "title": f"Förderung löschen: {foerderung}", } return render(request, "stiftung/foerderung_confirm_delete.html", context) # DokumentLink Views