Phase 0: forms.py, admin.py und views.py in Domain-Packages aufteilen
- forms.py → forms/ Package (8 Domänen: destinataere, land, finanzen, foerderung, dokumente, veranstaltung, system, geschichte) - admin.py → admin/ Package (7 Domänen, alle 22 @admin.register dekoriert) - views.py (8845 Zeilen) → views/ Package (10 Domänen: dashboard, destinataere, land, paechter, finanzen, foerderung, dokumente, unterstuetzungen, veranstaltung, geschichte, system) - __init__.py in jedem Package re-exportiert alle Symbole für Rückwärtskompatibilität - urls.py bleibt unverändert (funktioniert durch Re-Exports) - Django system check: 0 Fehler, alle URL-Auflösungen funktionieren Keine funktionalen Änderungen – reine Strukturverbesserung für Vision 2026. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
236
app/stiftung/views/foerderung.py
Normal file
236
app/stiftung/views/foerderung.py
Normal file
@@ -0,0 +1,236 @@
|
||||
# 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,
|
||||
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 = DokumentLink.objects.filter(
|
||||
foerderung_id=foerderung.pk
|
||||
).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
|
||||
Reference in New Issue
Block a user