Format code with Black and isort for CI/CD compliance

- Apply Black formatting to all Python files in core and stiftung modules
- Fix import statement ordering with isort
- Ensure all code meets automated quality standards
- Resolve CI/CD pipeline formatting failures
- Maintain consistent code style across the entire codebase
This commit is contained in:
Stiftung Development
2025-09-06 21:04:07 +02:00
parent c7c790ee09
commit e0c7d0e351
54 changed files with 11004 additions and 6423 deletions

View File

@@ -12,110 +12,116 @@ Usage:
python manage.py sync_abrechnungen [--dry-run] [--year YEAR]
"""
from datetime import date
from decimal import Decimal
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from decimal import Decimal
from datetime import date
from stiftung.models import Verpachtung, LandVerpachtung, LandAbrechnung
from stiftung.models import LandAbrechnung, LandVerpachtung, Verpachtung
class Command(BaseCommand):
help = 'Synchronize existing Verpachtungen with LandAbrechnungen'
help = "Synchronize existing Verpachtungen with LandAbrechnungen"
def add_arguments(self, parser):
parser.add_argument(
'--dry-run',
action='store_true',
help='Show what would be done without making changes',
"--dry-run",
action="store_true",
help="Show what would be done without making changes",
)
parser.add_argument(
'--year',
"--year",
type=int,
help='Only sync data for specific year',
help="Only sync data for specific year",
)
parser.add_argument(
'--force',
action='store_true',
help='Force update even if Abrechnungen already exist',
"--force",
action="store_true",
help="Force update even if Abrechnungen already exist",
)
def handle(self, *args, **options):
dry_run = options['dry_run']
target_year = options['year']
force = options['force']
dry_run = options["dry_run"]
target_year = options["year"]
force = options["force"]
self.stdout.write(
self.style.SUCCESS('🔄 Starting Abrechnung synchronization...')
self.style.SUCCESS("🔄 Starting Abrechnung synchronization...")
)
if dry_run:
self.stdout.write(self.style.WARNING('📋 DRY RUN MODE - No changes will be made'))
self.stdout.write(
self.style.WARNING("📋 DRY RUN MODE - No changes will be made")
)
# Statistics
stats = {
'legacy_contracts': 0,
'new_contracts': 0,
'abrechnungen_created': 0,
'abrechnungen_updated': 0,
'total_rent_amount': Decimal('0.00'),
'years_processed': set(),
"legacy_contracts": 0,
"new_contracts": 0,
"abrechnungen_created": 0,
"abrechnungen_updated": 0,
"total_rent_amount": Decimal("0.00"),
"years_processed": set(),
}
try:
with transaction.atomic():
# Process Legacy Verpachtungen
self.stdout.write('\n📄 Processing Legacy Verpachtungen...')
self.stdout.write("\n📄 Processing Legacy Verpachtungen...")
legacy_verpachtungen = Verpachtung.objects.all()
for verpachtung in legacy_verpachtungen:
stats['legacy_contracts'] += 1
stats["legacy_contracts"] += 1
years_affected = self._get_affected_years(
verpachtung.pachtbeginn,
verpachtung.verlaengerung or verpachtung.pachtende,
target_year
target_year,
)
for year in years_affected:
stats['years_processed'].add(year)
rent_amount = self._calculate_legacy_rent_for_year(verpachtung, year)
stats["years_processed"].add(year)
rent_amount = self._calculate_legacy_rent_for_year(
verpachtung, year
)
if not dry_run:
created, updated = self._update_abrechnung(
verpachtung.land,
year,
rent_amount,
Decimal('0.00'), # No umlage for legacy
Decimal("0.00"), # No umlage for legacy
f"Legacy-Verpachtung {verpachtung.vertragsnummer}",
force
force,
)
if created:
stats['abrechnungen_created'] += 1
stats["abrechnungen_created"] += 1
if updated:
stats['abrechnungen_updated'] += 1
stats['total_rent_amount'] += rent_amount
stats["abrechnungen_updated"] += 1
stats["total_rent_amount"] += rent_amount
self.stdout.write(
f" 📊 {verpachtung.vertragsnummer} ({year}): {rent_amount:.2f}"
)
# Process New LandVerpachtungen
self.stdout.write('\n🆕 Processing New LandVerpachtungen...')
# Process New LandVerpachtungen
self.stdout.write("\n🆕 Processing New LandVerpachtungen...")
land_verpachtungen = LandVerpachtung.objects.all()
for verpachtung in land_verpachtungen:
stats['new_contracts'] += 1
stats["new_contracts"] += 1
years_affected = self._get_affected_years(
verpachtung.pachtbeginn,
verpachtung.pachtende,
target_year
verpachtung.pachtbeginn, verpachtung.pachtende, target_year
)
for year in years_affected:
stats['years_processed'].add(year)
rent_amount = self._calculate_new_rent_for_year(verpachtung, year)
umlage_amount = Decimal('0.00') # To be calculated later
stats["years_processed"].add(year)
rent_amount = self._calculate_new_rent_for_year(
verpachtung, year
)
umlage_amount = Decimal("0.00") # To be calculated later
if not dry_run:
created, updated = self._update_abrechnung(
verpachtung.land,
@@ -123,131 +129,143 @@ class Command(BaseCommand):
rent_amount,
umlage_amount,
f"LandVerpachtung {verpachtung.vertragsnummer}",
force
force,
)
if created:
stats['abrechnungen_created'] += 1
stats["abrechnungen_created"] += 1
if updated:
stats['abrechnungen_updated'] += 1
stats['total_rent_amount'] += rent_amount
stats["abrechnungen_updated"] += 1
stats["total_rent_amount"] += rent_amount
self.stdout.write(
f" 📊 {verpachtung.vertragsnummer} ({year}): {rent_amount:.2f}"
)
if dry_run:
# Rollback transaction in dry run
transaction.set_rollback(True)
except Exception as e:
self.stdout.write(
self.style.ERROR(f'❌ Error during synchronization: {str(e)}')
self.style.ERROR(f"❌ Error during synchronization: {str(e)}")
)
raise CommandError(f'Synchronization failed: {str(e)}')
raise CommandError(f"Synchronization failed: {str(e)}")
# Print summary
self.stdout.write('\n' + '='*50)
self.stdout.write(self.style.SUCCESS('📈 SYNCHRONIZATION SUMMARY'))
self.stdout.write('='*50)
self.stdout.write("\n" + "=" * 50)
self.stdout.write(self.style.SUCCESS("📈 SYNCHRONIZATION SUMMARY"))
self.stdout.write("=" * 50)
self.stdout.write(f"Legacy contracts processed: {stats['legacy_contracts']}")
self.stdout.write(f"New contracts processed: {stats['new_contracts']}")
self.stdout.write(f"Years affected: {', '.join(map(str, sorted(stats['years_processed'])))}")
self.stdout.write(
f"Years affected: {', '.join(map(str, sorted(stats['years_processed'])))}"
)
self.stdout.write(f"Abrechnungen created: {stats['abrechnungen_created']}")
self.stdout.write(f"Abrechnungen updated: {stats['abrechnungen_updated']}")
self.stdout.write(f"Total rent amount: {stats['total_rent_amount']:.2f}")
if dry_run:
self.stdout.write(self.style.WARNING('\n📋 This was a DRY RUN - no changes were saved'))
self.stdout.write(
self.style.WARNING("\n📋 This was a DRY RUN - no changes were saved")
)
else:
self.stdout.write(self.style.SUCCESS('\n✅ Synchronization completed successfully!'))
self.stdout.write(
self.style.SUCCESS("\n✅ Synchronization completed successfully!")
)
def _get_affected_years(self, start_date, end_date, target_year=None):
"""Get all years affected by a contract"""
if not start_date:
return []
years = []
start_year = start_date.year
end_year = end_date.year if end_date else date.today().year
if target_year:
if start_year <= target_year <= end_year:
return [target_year]
else:
return []
for year in range(start_year, end_year + 1):
years.append(year)
return years
def _calculate_legacy_rent_for_year(self, verpachtung, year):
"""Calculate rent for legacy Verpachtung for specific year"""
if not verpachtung.pachtzins_jaehrlich or not verpachtung.pachtbeginn:
return Decimal('0.00')
return Decimal("0.00")
year_start = date(year, 1, 1)
year_end = date(year, 12, 31)
contract_end_date = verpachtung.verlaengerung if verpachtung.verlaengerung else verpachtung.pachtende
contract_end_date = (
verpachtung.verlaengerung
if verpachtung.verlaengerung
else verpachtung.pachtende
)
contract_start = max(verpachtung.pachtbeginn, year_start)
contract_end = min(contract_end_date or year_end, year_end)
if contract_start > contract_end:
return Decimal('0.00')
return Decimal("0.00")
days_in_year = (year_end - year_start).days + 1
days_active = (contract_end - contract_start).days + 1
proportion = Decimal(str(days_active)) / Decimal(str(days_in_year))
return Decimal(str(verpachtung.pachtzins_jaehrlich)) * proportion
def _calculate_new_rent_for_year(self, verpachtung, year):
"""Calculate rent for new LandVerpachtung for specific year"""
if not verpachtung.pachtzins_pauschal or not verpachtung.pachtbeginn:
return Decimal('0.00')
return Decimal("0.00")
year_start = date(year, 1, 1)
year_end = date(year, 12, 31)
contract_start = max(verpachtung.pachtbeginn, year_start)
contract_end = min(verpachtung.pachtende or year_end, year_end)
if contract_start > contract_end:
return Decimal('0.00')
return Decimal("0.00")
days_in_year = (year_end - year_start).days + 1
days_active = (contract_end - contract_start).days + 1
proportion = Decimal(str(days_active)) / Decimal(str(days_in_year))
return Decimal(str(verpachtung.pachtzins_pauschal)) * proportion
def _update_abrechnung(self, land, year, rent_amount, umlage_amount, source_note, force):
def _update_abrechnung(
self, land, year, rent_amount, umlage_amount, source_note, force
):
"""Update or create Abrechnung for specific land and year"""
abrechnung, created = LandAbrechnung.objects.get_or_create(
land=land,
abrechnungsjahr=year,
defaults={
'pacht_vereinnahmt': rent_amount,
'umlagen_vereinnahmt': umlage_amount,
'bemerkungen': f'[{date.today().strftime("%d.%m.%Y")}] Automatisch synchronisiert von {source_note}'
}
"pacht_vereinnahmt": rent_amount,
"umlagen_vereinnahmt": umlage_amount,
"bemerkungen": f'[{date.today().strftime("%d.%m.%Y")}] Automatisch synchronisiert von {source_note}',
},
)
updated = False
if not created and force:
# Update existing
abrechnung.pacht_vereinnahmt += rent_amount
abrechnung.umlagen_vereinnahmt += umlage_amount
sync_note = f'[{date.today().strftime("%d.%m.%Y")}] Resync: +{rent_amount:.2f}€ von {source_note}'
if abrechnung.bemerkungen:
abrechnung.bemerkungen += f'\n{sync_note}'
abrechnung.bemerkungen += f"\n{sync_note}"
else:
abrechnung.bemerkungen = sync_note
abrechnung.save()
updated = True
return created, updated