feat: add comprehensive GitHub workflow and development tools

This commit is contained in:
Stiftung Development
2025-09-06 18:31:54 +02:00
commit ab23d7187e
10224 changed files with 2075210 additions and 0 deletions

View File

@@ -0,0 +1 @@
# Management package

View File

@@ -0,0 +1 @@
# Management commands package

View File

@@ -0,0 +1,92 @@
from django.core.management.base import BaseCommand
from django.db import transaction
from stiftung.models import Land, Verpachtung, Paechter
import logging
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = 'Migriert bestehende Verpachtungen in die neue Land-Struktur'
def add_arguments(self, parser):
parser.add_argument(
'--dry-run',
action='store_true',
help='Zeigt nur an, was gemacht würde, ohne Änderungen zu speichern',
)
def handle(self, *args, **options):
dry_run = options['dry_run']
if dry_run:
self.stdout.write(self.style.WARNING('DRY RUN - Keine Änderungen werden gespeichert!'))
# Alle aktiven Verpachtungen finden
aktive_verpachtungen = Verpachtung.objects.filter(status='aktiv')
self.stdout.write(f'Gefunden: {aktive_verpachtungen.count()} aktive Verpachtungen')
migrated_count = 0
skipped_count = 0
with transaction.atomic():
for verpachtung in aktive_verpachtungen:
land = verpachtung.land
# Prüfen ob bereits migriert
if land.aktueller_paechter is not None:
self.stdout.write(
self.style.WARNING(
f'Übersprungen: {land} hat bereits einen aktuellen Pächter'
)
)
skipped_count += 1
continue
# Migration durchführen
self.stdout.write(f'Migriere: {land} -> {verpachtung.paechter}')
if not dry_run:
# Pächter-Daten ins Land übertragen
land.aktueller_paechter = verpachtung.paechter
land.paechter_name = verpachtung.paechter.get_full_name()
land.paechter_anschrift = self._get_paechter_anschrift(verpachtung.paechter)
land.pachtbeginn = verpachtung.pachtbeginn
land.pachtende = verpachtung.pachtende
land.verlaengerung_klausel = bool(verpachtung.verlaengerung)
# Pachtzins übertragen
land.pachtzins_pauschal = verpachtung.pachtzins_jaehrlich
# Verpachtete Fläche aktualisieren (falls nicht gesetzt)
if land.verp_flaeche_aktuell == 0:
land.verp_flaeche_aktuell = verpachtung.verpachtete_flaeche
land.save()
migrated_count += 1
if dry_run:
self.stdout.write(
self.style.SUCCESS(
f'DRY RUN abgeschlossen: {migrated_count} Verpachtungen würden migriert, {skipped_count} übersprungen'
)
)
else:
self.stdout.write(
self.style.SUCCESS(
f'Migration abgeschlossen: {migrated_count} Verpachtungen migriert, {skipped_count} übersprungen'
)
)
def _get_paechter_anschrift(self, paechter):
"""Erstellt eine Anschrift aus den Pächter-Daten"""
parts = []
if paechter.strasse:
parts.append(paechter.strasse)
if paechter.plz and paechter.ort:
parts.append(f"{paechter.plz} {paechter.ort}")
elif paechter.ort:
parts.append(paechter.ort)
return '\n'.join(parts) if parts else ''

View File

@@ -0,0 +1,119 @@
from django.core.management.base import BaseCommand
from django.db import transaction
from stiftung.models import Land, Verpachtung, Paechter, LandAbrechnung
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = 'Vereinheitlicht Verpachtungen, Land und Abrechnungen zu einem konsistenten System'
def add_arguments(self, parser):
parser.add_argument(
'--dry-run',
action='store_true',
help='Zeigt nur an, was gemacht würde, ohne Änderungen zu speichern',
)
parser.add_argument(
'--create-abrechnungen',
action='store_true',
help='Erstellt automatisch Abrechnungen aus Verpachtungsdaten',
)
def handle(self, *args, **options):
dry_run = options['dry_run']
create_abrechnungen = options['create_abrechnungen']
if dry_run:
self.stdout.write(self.style.WARNING('DRY RUN - Keine Änderungen werden gespeichert!'))
# Schritt 1: Alle Verpachtungen analysieren
alle_verpachtungen = Verpachtung.objects.all().order_by('land', '-pachtbeginn')
self.stdout.write(f'Gefunden: {alle_verpachtungen.count()} Verpachtungen insgesamt')
land_updates = 0
abrechnungen_created = 0
with transaction.atomic():
current_land = None
for verpachtung in alle_verpachtungen:
land = verpachtung.land
# Für jedes Land nur die neueste aktive Verpachtung als "aktuell" setzen
if current_land != land:
current_land = land
# Prüfen ob dies die neueste aktive Verpachtung ist
if verpachtung.status == 'aktiv' and not land.aktueller_paechter:
self.stdout.write(f'Setze aktuelle Verpachtung: {land} -> {verpachtung.paechter}')
if not dry_run:
# Land-Felder aktualisieren
land.aktueller_paechter = verpachtung.paechter
land.paechter_name = verpachtung.paechter.get_full_name()
land.paechter_anschrift = self._get_paechter_anschrift(verpachtung.paechter)
land.pachtbeginn = verpachtung.pachtbeginn
land.pachtende = verpachtung.pachtende
land.verlaengerung_klausel = bool(verpachtung.verlaengerung)
land.pachtzins_pauschal = verpachtung.pachtzins_jaehrlich
# Verpachtete Fläche synchronisieren
land.verp_flaeche_aktuell = verpachtung.verpachtete_flaeche
land.save()
land_updates += 1
# Schritt 2: Abrechnungen aus Verpachtungen erstellen (optional)
if create_abrechnungen and verpachtung.status == 'aktiv':
# Erstelle Abrechnungen für die letzten 3 Jahre
current_year = datetime.now().year
for jahr in range(current_year - 2, current_year + 1):
# Prüfen ob Abrechnung bereits existiert
existing = LandAbrechnung.objects.filter(
land=land,
abrechnungsjahr=jahr
).first()
if not existing:
self.stdout.write(f'Erstelle Abrechnung: {land} - {jahr}')
if not dry_run:
abrechnung = LandAbrechnung.objects.create(
land=land,
abrechnungsjahr=jahr,
pacht_vereinnahmt=verpachtung.pachtzins_jaehrlich,
bemerkungen=f'Automatisch erstellt aus Verpachtung {verpachtung.vertragsnummer}'
)
abrechnungen_created += 1
# Zusammenfassung
self.stdout.write(self.style.SUCCESS('\n=== MIGRATION ABGESCHLOSSEN ==='))
if dry_run:
self.stdout.write(f'DRY RUN: {land_updates} Länder würden aktualisiert')
if create_abrechnungen:
self.stdout.write(f'DRY RUN: {abrechnungen_created} Abrechnungen würden erstellt')
else:
self.stdout.write(f'{land_updates} Länder aktualisiert')
if create_abrechnungen:
self.stdout.write(f'{abrechnungen_created} Abrechnungen erstellt')
# Empfehlungen
self.stdout.write(self.style.WARNING('\n=== NÄCHSTE SCHRITTE ==='))
self.stdout.write('1. Prüfen Sie die migrierten Daten in der Weboberfläche')
self.stdout.write('2. Alte Verpachtungs-Views können als "Legacy" markiert werden')
self.stdout.write('3. Neue Verpachtungen sollten direkt im Land-Model erstellt werden')
def _get_paechter_anschrift(self, paechter):
"""Erstellt eine Anschrift aus den Pächter-Daten"""
parts = []
if paechter.strasse:
parts.append(paechter.strasse)
if paechter.plz and paechter.ort:
parts.append(f"{paechter.plz} {paechter.ort}")
elif paechter.ort:
parts.append(paechter.ort)
return '\n'.join(parts) if parts else ''