from django.core.management.base import BaseCommand from django.db import transaction from datetime import date from stiftung.models import DestinataerUnterstuetzung class Command(BaseCommand): help = 'Fix all quarterly payment dates to use advance payment schedule' def add_arguments(self, parser): parser.add_argument( '--dry-run', action='store_true', help='Show what would be updated without making changes', ) parser.add_argument( '--year', type=int, default=2025, help='Year to fix payments for (default: 2025)', ) def handle(self, *args, **options): dry_run = options['dry_run'] year = options['year'] if dry_run: self.stdout.write(self.style.WARNING('DRY RUN MODE - No changes will be made')) self.stdout.write(f'Checking quarterly payments for {year} using advance payment schedule...') # Define correct quarterly payment schedule quarterly_schedule = { f'Q1/{year}': date(year, 3, 15), # Q1 payment due March 15 f'Q2/{year}': date(year, 6, 15), # Q2 payment due June 15 f'Q3/{year}': date(year, 9, 15), # Q3 payment due September 15 f'Q4/{year}': date(year, 9, 15), # Q4 payment due September 15 (semester alignment) } total_fixed = 0 for quarter_desc, correct_date in quarterly_schedule.items(): # Find payments for this quarter that have wrong dates wrong_payments = DestinataerUnterstuetzung.objects.filter( beschreibung__icontains=quarter_desc ).exclude(faellig_am=correct_date) quarter_count = wrong_payments.count() if quarter_count > 0: self.stdout.write(f'\n{quarter_desc}: Found {quarter_count} payments with wrong due dates') with transaction.atomic(): for payment in wrong_payments: old_date = payment.faellig_am if not dry_run: payment.faellig_am = correct_date payment.save(update_fields=['faellig_am']) total_fixed += 1 # Show some examples if total_fixed <= 10 or options['verbosity'] >= 2: self.stdout.write( f' {payment.destinataer.get_full_name()}: ' f'{old_date} → {correct_date} ({payment.betrag}€)' ) else: self.stdout.write(f'{quarter_desc}: ✓ All payments have correct due date ({correct_date})') # Summary if total_fixed == 0: self.stdout.write(self.style.SUCCESS(f'\n✓ All {year} quarterly payments already have correct advance payment dates!')) elif dry_run: self.stdout.write( self.style.WARNING(f'\nDRY RUN: Would fix {total_fixed} payment due dates') ) else: self.stdout.write( self.style.SUCCESS(f'\n✅ Successfully fixed {total_fixed} payment due dates') ) # Show the quarterly payment schedule self.stdout.write(f'\nQuarterly Payment Schedule for {year}:') for quarter_desc, due_date in quarterly_schedule.items(): self.stdout.write(f' {quarter_desc}: Due {due_date.strftime("%B %d, %Y")}') # Check for other years that might need fixing if year == 2025: other_year_payments = DestinataerUnterstuetzung.objects.filter( beschreibung__regex=r'Q[1-4]/202[6-9]' ).exclude( faellig_am__in=[date(2026, 3, 15), date(2026, 6, 15), date(2026, 9, 15), date(2026, 9, 15)] ).count() if other_year_payments > 0: self.stdout.write(f'\n⚠️ Found {other_year_payments} payments for other years that may need fixing') self.stdout.write(' Run this command with --year 2026, etc. to fix other years')