548 lines
20 KiB
Python
548 lines
20 KiB
Python
from django.contrib import admin
|
|
from django.utils.html import format_html
|
|
from django.urls import reverse
|
|
from django.db.models import Sum, Count
|
|
from django.utils.safestring import mark_safe
|
|
from .models import (
|
|
Person, Paechter, Destinataer, DokumentLink, Foerderung, Land, Verpachtung, CSVImport,
|
|
Rentmeister, StiftungsKonto, Verwaltungskosten, BankTransaction, AuditLog, BackupJob
|
|
)
|
|
|
|
@admin.register(CSVImport)
|
|
class CSVImportAdmin(admin.ModelAdmin):
|
|
list_display = ['import_type', 'filename', 'status', 'total_rows', 'imported_rows', 'failed_rows', 'created_by', 'started_at', 'duration_display']
|
|
list_filter = ['import_type', 'status', 'started_at']
|
|
search_fields = ['filename', 'created_by']
|
|
readonly_fields = ['id', 'started_at', 'completed_at', 'get_success_rate']
|
|
ordering = ['-started_at']
|
|
|
|
fieldsets = (
|
|
('Grundinformationen', {
|
|
'fields': ('import_type', 'filename', 'file_size', 'status')
|
|
}),
|
|
('Ergebnisse', {
|
|
'fields': ('total_rows', 'imported_rows', 'failed_rows', 'get_success_rate', 'error_log')
|
|
}),
|
|
('Metadaten', {
|
|
'fields': ('created_by', 'started_at', 'completed_at')
|
|
}),
|
|
)
|
|
|
|
def duration_display(self, obj):
|
|
duration = obj.get_duration()
|
|
if duration:
|
|
return f"{duration.total_seconds():.1f}s"
|
|
return "-"
|
|
duration_display.short_description = "Dauer"
|
|
|
|
def get_success_rate(self, obj):
|
|
rate = obj.get_success_rate()
|
|
if rate >= 90:
|
|
color = "success"
|
|
elif rate >= 70:
|
|
color = "warning"
|
|
else:
|
|
color = "danger"
|
|
return format_html('<span class="badge bg-{}">{:.1f}%</span>', color, rate)
|
|
get_success_rate.short_description = "Erfolgsrate"
|
|
|
|
@admin.register(Person)
|
|
class PersonAdmin(admin.ModelAdmin):
|
|
list_display = ['nachname', 'vorname', 'familienzweig', 'geburtsdatum', 'email', 'iban_display']
|
|
list_filter = ['familienzweig', 'geburtsdatum']
|
|
search_fields = ['nachname', 'vorname', 'email', 'familienzweig']
|
|
ordering = ['nachname', 'vorname']
|
|
readonly_fields = ['id']
|
|
|
|
fieldsets = (
|
|
('Persönliche Daten', {
|
|
'fields': ('vorname', 'nachname', 'geburtsdatum', 'email', 'telefon')
|
|
}),
|
|
('Stiftungsdaten', {
|
|
'fields': ('familienzweig', 'iban', 'adresse')
|
|
}),
|
|
('Zusätzlich', {
|
|
'fields': ('notizen', 'aktiv')
|
|
}),
|
|
('System', {
|
|
'fields': ('id',),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def iban_display(self, obj):
|
|
if obj.iban:
|
|
return format_html('<span style="font-family: monospace;">{}</span>', obj.iban)
|
|
return '-'
|
|
iban_display.short_description = 'IBAN'
|
|
|
|
def get_queryset(self, request):
|
|
return super().get_queryset(request).annotate(
|
|
total_foerderungen=Sum('foerderung__betrag')
|
|
)
|
|
|
|
@admin.register(Paechter)
|
|
class PaechterAdmin(admin.ModelAdmin):
|
|
list_display = ['nachname', 'vorname', 'pachtnummer', 'pachtzins_aktuell', 'landwirtschaftliche_ausbildung', 'aktiv']
|
|
list_filter = ['landwirtschaftliche_ausbildung', 'aktiv']
|
|
search_fields = ['nachname', 'vorname', 'email', 'pachtnummer']
|
|
ordering = ['nachname', 'vorname']
|
|
readonly_fields = ['id']
|
|
|
|
fieldsets = (
|
|
('Persönliche Daten', {
|
|
'fields': ('vorname', 'nachname', 'geburtsdatum', 'email', 'telefon')
|
|
}),
|
|
('Pacht-Informationen', {
|
|
'fields': ('pachtnummer', 'pachtbeginn_erste', 'pachtende_letzte', 'pachtzins_aktuell')
|
|
}),
|
|
('Landwirtschaftliche Qualifikation', {
|
|
'fields': ('landwirtschaftliche_ausbildung', 'berufserfahrung_jahre', 'spezialisierung')
|
|
}),
|
|
('Kontaktdaten', {
|
|
'fields': ('iban', 'strasse', 'plz', 'ort')
|
|
}),
|
|
('Pächter-Typ', {
|
|
'fields': ('personentyp',)
|
|
}),
|
|
('Zusätzlich', {
|
|
'fields': ('notizen', 'aktiv')
|
|
}),
|
|
('System', {
|
|
'fields': ('id',),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def iban_display(self, obj):
|
|
if obj.iban:
|
|
return format_html('<span style="font-family: monospace;">{}</span>', obj.iban)
|
|
return '-'
|
|
iban_display.short_description = 'IBAN'
|
|
|
|
@admin.register(Destinataer)
|
|
class DestinataerAdmin(admin.ModelAdmin):
|
|
list_display = ['nachname', 'vorname', 'familienzweig', 'berufsgruppe', 'institution', 'finanzielle_notlage', 'aktiv']
|
|
list_filter = ['familienzweig', 'berufsgruppe', 'finanzielle_notlage', 'aktiv']
|
|
search_fields = ['nachname', 'vorname', 'email', 'institution', 'familienzweig']
|
|
ordering = ['nachname', 'vorname']
|
|
readonly_fields = ['id']
|
|
|
|
fieldsets = (
|
|
('Persönliche Daten', {
|
|
'fields': ('vorname', 'nachname', 'geburtsdatum', 'email', 'telefon')
|
|
}),
|
|
('Berufliche Informationen', {
|
|
'fields': ('berufsgruppe', 'ausbildungsstand', 'institution')
|
|
}),
|
|
('Projekt & Finanzen', {
|
|
'fields': ('projekt_beschreibung', 'jaehrliches_einkommen', 'finanzielle_notlage')
|
|
}),
|
|
('Stiftungsdaten', {
|
|
'fields': ('familienzweig', 'iban', 'strasse', 'plz', 'ort')
|
|
}),
|
|
('Zusätzlich', {
|
|
'fields': ('notizen', 'aktiv')
|
|
}),
|
|
('System', {
|
|
'fields': ('id',),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def iban_display(self, obj):
|
|
if obj.iban:
|
|
return format_html('<span style="font-family: monospace;">{}</span>', obj.iban)
|
|
return '-'
|
|
iban_display.short_description = 'IBAN'
|
|
|
|
@admin.register(Land)
|
|
class LandAdmin(admin.ModelAdmin):
|
|
list_display = [
|
|
'lfd_nr', 'gemeinde', 'gemarkung', 'flur', 'flurstueck',
|
|
'groesse_qm', 'verp_flaeche_aktuell', 'verpachtungsgrad_display', 'aktiv'
|
|
]
|
|
list_filter = ['gemeinde', 'gemarkung', 'aktiv']
|
|
search_fields = ['lfd_nr', 'gemeinde', 'gemarkung', 'flur', 'flurstueck']
|
|
ordering = ['gemeinde', 'gemarkung', 'flur', 'flurstueck']
|
|
readonly_fields = ['id', 'gesamtflaeche_berechnet', 'verpachtungsgrad_berechnet']
|
|
|
|
fieldsets = (
|
|
('Identifikation', {
|
|
'fields': ('lfd_nr', 'ew_nummer')
|
|
}),
|
|
('Gerichtliche Zuständigkeit', {
|
|
'fields': ('amtsgericht',)
|
|
}),
|
|
('Verwaltungsstruktur', {
|
|
'fields': ('gemeinde', 'gemarkung', 'flur', 'flurstueck')
|
|
}),
|
|
('Flächenangaben', {
|
|
'fields': ('groesse_qm', 'gruenland_qm', 'acker_qm', 'wald_qm', 'sonstiges_qm')
|
|
}),
|
|
('Verpachtung', {
|
|
'fields': ('verpachtete_gesamtflaeche', 'flaeche_alte_liste', 'verp_flaeche_aktuell')
|
|
}),
|
|
('Steuern und Abgaben', {
|
|
'fields': ('anteil_grundsteuer', 'anteil_lwk')
|
|
}),
|
|
('Status', {
|
|
'fields': ('aktiv', 'notizen')
|
|
}),
|
|
('System', {
|
|
'fields': ('id', 'erstellt_am', 'aktualisiert_am'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def verpachtungsgrad_display(self, obj):
|
|
grad = obj.get_verpachtungsgrad()
|
|
if grad > 90:
|
|
color = 'green'
|
|
elif grad > 70:
|
|
color = 'orange'
|
|
else:
|
|
color = 'red'
|
|
return format_html('<span style="color: {};">{:.1f}%</span>', color, grad)
|
|
verpachtungsgrad_display.short_description = 'Verpachtungsgrad'
|
|
|
|
def gesamtflaeche_berechnet(self, obj):
|
|
return f"{obj.get_gesamtflaeche():.2f} qm"
|
|
gesamtflaeche_berechnet.short_description = 'Berechnete Gesamtfläche'
|
|
|
|
def verpachtungsgrad_berechnet(self, obj):
|
|
return f"{obj.get_verpachtungsgrad():.1f}%"
|
|
verpachtungsgrad_berechnet.short_description = 'Verpachtungsgrad'
|
|
|
|
@admin.register(Verpachtung)
|
|
class VerpachtungAdmin(admin.ModelAdmin):
|
|
list_display = [
|
|
'vertragsnummer', 'land', 'paechter', 'pachtbeginn', 'pachtende',
|
|
'pachtzins_jaehrlich', 'verpachtete_flaeche', 'status', 'restlaufzeit'
|
|
]
|
|
list_filter = ['status', 'pachtbeginn', 'pachtende', 'land__gemeinde']
|
|
search_fields = ['vertragsnummer', 'land__gemeinde', 'paechter__nachname', 'paechter__vorname']
|
|
ordering = ['-pachtbeginn']
|
|
readonly_fields = ['id', 'vertragsdauer_tage', 'restlaufzeit_tage', 'is_aktiv_status']
|
|
|
|
fieldsets = (
|
|
('Vertragsdaten', {
|
|
'fields': ('vertragsnummer', 'land', 'paechter', 'pachtbeginn', 'pachtende', 'verlaengerung')
|
|
}),
|
|
('Finanzielle Bedingungen', {
|
|
'fields': ('pachtzins_pro_qm', 'pachtzins_jaehrlich')
|
|
}),
|
|
('Flächenangaben', {
|
|
'fields': ('verpachtete_flaeche',)
|
|
}),
|
|
('Status', {
|
|
'fields': ('status',)
|
|
}),
|
|
('Dokumentation', {
|
|
'fields': ('verwendungsnachweis', 'bemerkungen')
|
|
}),
|
|
('System', {
|
|
'fields': ('id', 'erstellt_am', 'aktualisiert_am'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def restlaufzeit(self, obj):
|
|
tage = obj.get_restlaufzeit_tage()
|
|
if tage > 0:
|
|
if tage < 30:
|
|
color = 'red'
|
|
elif tage < 90:
|
|
color = 'orange'
|
|
else:
|
|
color = 'green'
|
|
return format_html('<span style="color: {};">{} Tage</span>', color, tage)
|
|
return 'Abgelaufen'
|
|
restlaufzeit.short_description = 'Restlaufzeit'
|
|
|
|
def vertragsdauer_tage(self, obj):
|
|
return f"{obj.get_vertragsdauer_tage()} Tage"
|
|
vertragsdauer_tage.short_description = 'Vertragsdauer'
|
|
|
|
def restlaufzeit_tage(self, obj):
|
|
return f"{obj.get_restlaufzeit_tage()} Tage"
|
|
restlaufzeit_tage.short_description = 'Restlaufzeit'
|
|
|
|
def is_aktiv_status(self, obj):
|
|
if obj.is_aktiv():
|
|
return format_html('<span style="color: green;">✓ Aktiv</span>')
|
|
return format_html('<span style="color: red;">✗ Inaktiv</span>')
|
|
is_aktiv_status.short_description = 'Aktueller Status'
|
|
|
|
@admin.register(DokumentLink)
|
|
class DokumentLinkAdmin(admin.ModelAdmin):
|
|
list_display = ['titel', 'kontext', 'paperless_document_id']
|
|
list_filter = ['kontext']
|
|
search_fields = ['titel', 'kontext']
|
|
ordering = ['titel']
|
|
readonly_fields = ['id']
|
|
|
|
fieldsets = (
|
|
('Dokument', {
|
|
'fields': ('titel', 'kontext', 'paperless_document_id', 'beschreibung')
|
|
}),
|
|
('System', {
|
|
'fields': ('id',),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
@admin.register(Foerderung)
|
|
class FoerderungAdmin(admin.ModelAdmin):
|
|
list_display = ['destinataer', 'jahr', 'betrag', 'verwendungsnachweis_link', 'total_for_destinataer']
|
|
list_filter = ['jahr', 'destinataer__familienzweig']
|
|
search_fields = ['destinataer__nachname', 'destinataer__vorname', 'destinataer__familienzweig']
|
|
ordering = ['-jahr', '-betrag']
|
|
readonly_fields = ['id']
|
|
|
|
fieldsets = (
|
|
('Förderung', {
|
|
'fields': ('destinataer', 'person', 'jahr', 'betrag', 'kategorie', 'status')
|
|
}),
|
|
('Dokumentation', {
|
|
'fields': ('verwendungsnachweis', 'bemerkungen')
|
|
}),
|
|
('Daten', {
|
|
'fields': ('antragsdatum', 'entscheidungsdatum')
|
|
}),
|
|
('System', {
|
|
'fields': ('id',),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def verwendungsnachweis_link(self, obj):
|
|
if obj.verwendungsnachweis:
|
|
return format_html(
|
|
'<a href="{}">{}</a>',
|
|
reverse('admin:stiftung_dokumentlink_change', args=[obj.verwendungsnachweis.id]),
|
|
obj.verwendungsnachweis.titel
|
|
)
|
|
return '-'
|
|
verwendungsnachweis_link.short_description = 'Verwendungsnachweis'
|
|
|
|
def total_for_destinataer(self, obj):
|
|
total = Foerderung.objects.filter(destinataer=obj.destinataer).aggregate(Sum('betrag'))['betrag__sum'] or 0
|
|
return f"€{total:,.2f}"
|
|
total_for_destinataer.short_description = 'Gesamt für Destinatär'
|
|
|
|
|
|
@admin.register(Rentmeister)
|
|
class RentmeisterAdmin(admin.ModelAdmin):
|
|
list_display = ['__str__', 'email', 'telefon', 'seit_datum', 'bis_datum', 'aktiv', 'monatliche_verguetung']
|
|
list_filter = ['aktiv', 'seit_datum', 'anrede']
|
|
search_fields = ['vorname', 'nachname', 'email', 'telefon', 'ort']
|
|
ordering = ['nachname', 'vorname']
|
|
readonly_fields = ['id', 'erstellt_am', 'aktualisiert_am']
|
|
|
|
fieldsets = (
|
|
('Persönliche Daten', {
|
|
'fields': ('anrede', 'vorname', 'nachname', 'titel')
|
|
}),
|
|
('Kontaktdaten', {
|
|
'fields': ('email', 'telefon', 'mobil', 'strasse', 'plz', 'ort')
|
|
}),
|
|
('Bankdaten', {
|
|
'fields': ('iban', 'bic', 'bank_name'),
|
|
'classes': ['collapse']
|
|
}),
|
|
('Stiftungsdaten', {
|
|
'fields': ('seit_datum', 'bis_datum', 'aktiv', 'monatliche_verguetung', 'km_pauschale')
|
|
}),
|
|
('Zusätzliche Informationen', {
|
|
'fields': ('notizen',),
|
|
'classes': ['collapse']
|
|
}),
|
|
('System', {
|
|
'fields': ('id', 'erstellt_am', 'aktualisiert_am'),
|
|
'classes': ['collapse']
|
|
})
|
|
)
|
|
|
|
|
|
@admin.register(StiftungsKonto)
|
|
class StiftungsKontoAdmin(admin.ModelAdmin):
|
|
list_display = ['kontoname', 'bank_name', 'konto_typ', 'saldo', 'saldo_datum', 'aktiv']
|
|
list_filter = ['konto_typ', 'aktiv', 'bank_name']
|
|
search_fields = ['kontoname', 'bank_name', 'iban']
|
|
ordering = ['bank_name', 'kontoname']
|
|
readonly_fields = ['id', 'erstellt_am', 'aktualisiert_am']
|
|
|
|
fieldsets = (
|
|
('Kontodaten', {
|
|
'fields': ('kontoname', 'bank_name', 'iban', 'bic', 'konto_typ')
|
|
}),
|
|
('Finanzdaten', {
|
|
'fields': ('saldo', 'saldo_datum', 'zinssatz', 'laufzeit_bis')
|
|
}),
|
|
('Status', {
|
|
'fields': ('aktiv', 'notizen')
|
|
}),
|
|
('System', {
|
|
'fields': ('id', 'erstellt_am', 'aktualisiert_am'),
|
|
'classes': ['collapse']
|
|
})
|
|
)
|
|
|
|
|
|
@admin.register(Verwaltungskosten)
|
|
class VerwaltungskostenAdmin(admin.ModelAdmin):
|
|
list_display = ['bezeichnung', 'kategorie', 'betrag', 'datum', 'status', 'rentmeister', 'konto']
|
|
list_filter = ['kategorie', 'status', 'datum', 'rentmeister', 'konto']
|
|
search_fields = ['bezeichnung', 'lieferant_firma', 'rechnungsnummer', 'beschreibung']
|
|
ordering = ['-datum', '-erstellt_am']
|
|
readonly_fields = ['id', 'erstellt_am', 'aktualisiert_am']
|
|
date_hierarchy = 'datum'
|
|
|
|
fieldsets = (
|
|
('Grunddaten', {
|
|
'fields': ('bezeichnung', 'kategorie', 'betrag', 'datum', 'status')
|
|
}),
|
|
('Zuordnung', {
|
|
'fields': ('rentmeister', 'konto')
|
|
}),
|
|
('Lieferant/Rechnung', {
|
|
'fields': ('lieferant_firma', 'rechnungsnummer'),
|
|
'classes': ['collapse']
|
|
}),
|
|
('Fahrtkosten', {
|
|
'fields': ('km_anzahl', 'km_satz', 'von_ort', 'nach_ort', 'zweck'),
|
|
'classes': ['collapse'],
|
|
'description': 'Nur für Kategorie "Fahrtkosten" relevant'
|
|
}),
|
|
('Zusätzliche Informationen', {
|
|
'fields': ('beschreibung', 'notizen'),
|
|
'classes': ['collapse']
|
|
}),
|
|
('System', {
|
|
'fields': ('id', 'erstellt_am', 'aktualisiert_am'),
|
|
'classes': ['collapse']
|
|
})
|
|
)
|
|
|
|
|
|
@admin.register(BankTransaction)
|
|
class BankTransactionAdmin(admin.ModelAdmin):
|
|
list_display = [
|
|
'datum', 'konto', 'betrag', 'empfaenger_zahlungspflichtiger',
|
|
'transaction_type', 'status', 'verwaltungskosten'
|
|
]
|
|
list_filter = [
|
|
'konto', 'transaction_type', 'status', 'datum', 'importiert_am'
|
|
]
|
|
search_fields = [
|
|
'verwendungszweck', 'empfaenger_zahlungspflichtiger', 'referenz'
|
|
]
|
|
readonly_fields = ['importiert_am', 'import_datei']
|
|
ordering = ['-datum', '-importiert_am']
|
|
|
|
fieldsets = (
|
|
('Basisdaten', {
|
|
'fields': ('konto', 'datum', 'valuta', 'betrag', 'waehrung')
|
|
}),
|
|
('Transaktionsdetails', {
|
|
'fields': ('verwendungszweck', 'empfaenger_zahlungspflichtiger', 'iban_gegenpartei', 'bic_gegenpartei', 'referenz', 'transaction_type')
|
|
}),
|
|
('Verwaltung', {
|
|
'fields': ('status', 'kommentare', 'verwaltungskosten')
|
|
}),
|
|
('Import-Information', {
|
|
'fields': ('import_datei', 'importiert_am', 'saldo_nach_buchung'),
|
|
'classes': ('collapse',)
|
|
}),
|
|
)
|
|
|
|
def get_queryset(self, request):
|
|
return super().get_queryset(request).select_related('konto', 'verwaltungskosten')
|
|
|
|
|
|
@admin.register(AuditLog)
|
|
class AuditLogAdmin(admin.ModelAdmin):
|
|
list_display = ['timestamp', 'username', 'action', 'entity_type', 'entity_name', 'ip_address']
|
|
list_filter = ['action', 'entity_type', 'timestamp', 'username']
|
|
search_fields = ['username', 'entity_name', 'description', 'ip_address']
|
|
readonly_fields = ['id', 'timestamp', 'user', 'username', 'action', 'entity_type', 'entity_id', 'entity_name', 'description', 'changes', 'ip_address', 'user_agent', 'session_key']
|
|
ordering = ['-timestamp']
|
|
date_hierarchy = 'timestamp'
|
|
|
|
fieldsets = (
|
|
('Benutzer und Zeit', {
|
|
'fields': ('timestamp', 'user', 'username', 'session_key')
|
|
}),
|
|
('Aktion', {
|
|
'fields': ('action', 'entity_type', 'entity_id', 'entity_name', 'description')
|
|
}),
|
|
('Änderungsdetails', {
|
|
'fields': ('changes',),
|
|
'classes': ['collapse']
|
|
}),
|
|
('Request-Informationen', {
|
|
'fields': ('ip_address', 'user_agent'),
|
|
'classes': ['collapse']
|
|
}),
|
|
('System', {
|
|
'fields': ('id',),
|
|
'classes': ['collapse']
|
|
})
|
|
)
|
|
|
|
def has_add_permission(self, request):
|
|
return False # Don't allow manual creation
|
|
|
|
def has_change_permission(self, request, obj=None):
|
|
return False # Don't allow editing
|
|
|
|
|
|
@admin.register(BackupJob)
|
|
class BackupJobAdmin(admin.ModelAdmin):
|
|
list_display = ['created_at', 'backup_type', 'status', 'backup_size_display', 'duration_display', 'created_by']
|
|
list_filter = ['backup_type', 'status', 'created_at']
|
|
search_fields = ['backup_filename', 'created_by__username']
|
|
readonly_fields = ['id', 'created_at', 'started_at', 'completed_at', 'backup_size', 'get_duration']
|
|
ordering = ['-created_at']
|
|
|
|
fieldsets = (
|
|
('Job-Details', {
|
|
'fields': ('backup_type', 'status', 'created_by')
|
|
}),
|
|
('Zeitpunkte', {
|
|
'fields': ('created_at', 'started_at', 'completed_at', 'get_duration')
|
|
}),
|
|
('Ergebnis', {
|
|
'fields': ('backup_filename', 'backup_size', 'database_size', 'files_count')
|
|
}),
|
|
('Fehlerbehandlung', {
|
|
'fields': ('error_message',),
|
|
'classes': ['collapse']
|
|
}),
|
|
('System', {
|
|
'fields': ('id',),
|
|
'classes': ['collapse']
|
|
})
|
|
)
|
|
|
|
def backup_size_display(self, obj):
|
|
return obj.get_size_display()
|
|
backup_size_display.short_description = 'Backup-Größe'
|
|
|
|
def duration_display(self, obj):
|
|
duration = obj.get_duration()
|
|
if duration:
|
|
return f"{duration.total_seconds():.1f}s"
|
|
return "-"
|
|
duration_display.short_description = "Dauer"
|
|
|
|
def has_add_permission(self, request):
|
|
return False # Use the web interface for creating backups
|
|
|
|
|
|
# Customize admin site
|
|
admin.site.site_header = "Stiftungsverwaltung Administration"
|
|
admin.site.site_title = "Stiftungsverwaltung Admin"
|
|
admin.site.index_title = "Willkommen zur Stiftungsverwaltung"
|