fix: configure CI database connection properly

- Add dotenv loading to Django settings
- Update CI workflow to use correct environment variables
- Set POSTGRES_* variables instead of DATABASE_URL
- Add environment variables to all Django management commands
- Fixes CI test failures due to database connection issues
This commit is contained in:
Stiftung Development
2025-09-06 18:47:23 +02:00
parent dcc91b9f49
commit 35ba089a84
64 changed files with 7040 additions and 1419 deletions

View File

@@ -3,9 +3,11 @@ 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 . import models
from .models import (
Person, Paechter, Destinataer, DokumentLink, Foerderung, Land, Verpachtung, CSVImport,
Rentmeister, StiftungsKonto, Verwaltungskosten, BankTransaction, AuditLog, BackupJob
Person, Paechter, Destinataer, DokumentLink, Foerderung, Land, CSVImport,
Rentmeister, StiftungsKonto, Verwaltungskosten, BankTransaction, AuditLog, BackupJob, AppConfiguration,
DestinataerUnterstuetzung, UnterstuetzungWiederkehrend
)
@admin.register(CSVImport)
@@ -214,66 +216,6 @@ class LandAdmin(admin.ModelAdmin):
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']
@@ -541,6 +483,134 @@ class BackupJobAdmin(admin.ModelAdmin):
return False # Use the web interface for creating backups
@admin.register(AppConfiguration)
class AppConfigurationAdmin(admin.ModelAdmin):
list_display = ['display_name', 'key', 'value_display', 'category', 'setting_type', 'is_active', 'updated_at']
list_filter = ['category', 'setting_type', 'is_active']
search_fields = ['key', 'display_name', 'description']
readonly_fields = ['id', 'created_at', 'updated_at']
ordering = ['category', 'order', 'display_name']
fieldsets = (
('Basic Information', {
'fields': ('key', 'display_name', 'description', 'category', 'setting_type')
}),
('Value Configuration', {
'fields': ('value', 'default_value')
}),
('Options', {
'fields': ('is_active', 'is_system', 'order')
}),
('Metadata', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def value_display(self, obj):
"""Display value with type formatting"""
value = obj.value
if obj.setting_type == 'boolean':
icon = '' if obj.get_typed_value() else ''
return format_html('{} {}', icon, value)
elif obj.setting_type == 'url':
return format_html('<a href="{}" target="_blank">{}</a>', value, value[:50] + '...' if len(value) > 50 else value)
elif len(value) > 100:
return value[:100] + '...'
return value
value_display.short_description = 'Current Value'
def get_readonly_fields(self, request, obj=None):
readonly = list(self.readonly_fields)
if obj and obj.is_system:
readonly.extend(['key', 'setting_type', 'is_system'])
return readonly
@admin.register(DestinataerUnterstuetzung)
class DestinataerUnterstuetzungAdmin(admin.ModelAdmin):
list_display = ['__str__', 'destinataer', 'betrag', 'faellig_am', 'status', 'wiederkehrend_von', 'ausgezahlt_am']
list_filter = ['status', 'faellig_am', 'erstellt_am', 'konto']
search_fields = ['destinataer__vorname', 'destinataer__nachname', 'beschreibung', 'empfaenger_name']
readonly_fields = ['id', 'erstellt_am', 'aktualisiert_am']
fieldsets = (
('Grundinformationen', {
'fields': ('destinataer', 'konto', 'betrag', 'faellig_am', 'status', 'beschreibung')
}),
('Überweisungsdaten', {
'fields': ('empfaenger_iban', 'empfaenger_name', 'verwendungszweck')
}),
('Zahlungsinformationen', {
'fields': ('ausgezahlt_am', 'ausgezahlt_von')
}),
('Wiederkehrend', {
'fields': ('wiederkehrend_von',)
}),
('Metadaten', {
'fields': ('id', 'erstellt_am', 'aktualisiert_am'),
'classes': ('collapse',)
}),
)
@admin.register(UnterstuetzungWiederkehrend)
class UnterstuetzungWiederkehrendAdmin(admin.ModelAdmin):
list_display = ['__str__', 'destinataer', 'betrag', 'intervall', 'aktiv', 'naechste_generierung']
list_filter = ['intervall', 'aktiv', 'erstellt_am']
search_fields = ['destinataer__vorname', 'destinataer__nachname', 'beschreibung', 'empfaenger_name']
readonly_fields = ['id', 'erstellt_am']
fieldsets = (
('Grundinformationen', {
'fields': ('destinataer', 'konto', 'betrag', 'intervall', 'beschreibung', 'aktiv')
}),
('Überweisungsdaten', {
'fields': ('empfaenger_iban', 'empfaenger_name', 'verwendungszweck')
}),
('Zeitplanung', {
'fields': ('erste_zahlung_am', 'letzte_zahlung_am', 'naechste_generierung')
}),
('Metadaten', {
'fields': ('id', 'erstellt_von', 'erstellt_am'),
'classes': ('collapse',)
}),
)
@admin.register(models.HelpBox)
class HelpBoxAdmin(admin.ModelAdmin):
list_display = ['get_page_display', 'title', 'is_active', 'updated_at', 'updated_by']
list_filter = ['page_key', 'is_active', 'updated_at']
search_fields = ['title', 'content']
fieldsets = (
('Grundinformationen', {
'fields': ('page_key', 'title', 'is_active')
}),
('Inhalt', {
'fields': ('content',),
'description': 'Markdown wird unterstützt: **fett**, *kursiv*, `code`, [Link](url), Listen mit - oder 1.'
}),
('Metadaten', {
'fields': ('created_at', 'updated_at', 'created_by', 'updated_by'),
'classes': ('collapse',)
}),
)
readonly_fields = ['created_at', 'updated_at']
def get_page_display(self, obj):
return obj.get_page_key_display()
get_page_display.short_description = "Seite"
def save_model(self, request, obj, form, change):
if not change: # Neues Objekt
obj.created_by = request.user.username
obj.updated_by = request.user.username
super().save_model(request, obj, form, change)
# Customize admin site
admin.site.site_header = "Stiftungsverwaltung Administration"
admin.site.site_title = "Stiftungsverwaltung Admin"