Add production diagnostic and fix commands for semester deadlines
- diagnose_deadlines: Check migration status, deadline correctness, and payment patterns - fix_q4_payment_dates: Fix payments with wrong due dates (31.12 → 15.12) - includes dry-run modes and detailed reporting - helps identify why production dates haven't updated Usage on production: python manage.py diagnose_deadlines python manage.py fix_q4_payment_dates --dry-run python manage.py fix_q4_payment_dates
This commit is contained in:
102
app/stiftung/management/commands/diagnose_deadlines.py
Normal file
102
app/stiftung/management/commands/diagnose_deadlines.py
Normal file
@@ -0,0 +1,102 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import connection
|
||||
from stiftung.models import VierteljahresNachweis, DestinataerUnterstuetzung
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Diagnose deadline and payment due date issues on production'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.stdout.write(self.style.SUCCESS('=== PRODUCTION DEADLINE DIAGNOSTIC ==='))
|
||||
|
||||
# Check if migrations have been applied
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"SELECT name FROM django_migrations WHERE app = 'stiftung' AND name LIKE '%deadline%' ORDER BY id"
|
||||
)
|
||||
migration_results = cursor.fetchall()
|
||||
|
||||
self.stdout.write('\n1. Migration Status:')
|
||||
if migration_results:
|
||||
for migration in migration_results:
|
||||
self.stdout.write(f' ✓ {migration[0]}')
|
||||
else:
|
||||
self.stdout.write(' ❌ No deadline-related migrations found!')
|
||||
|
||||
# Check quarterly confirmation deadlines
|
||||
self.stdout.write('\n2. Quarterly Confirmation Deadlines (2025):')
|
||||
quarterly_records = VierteljahresNachweis.objects.filter(jahr=2025).order_by('quartal', 'destinataer__nachname')
|
||||
|
||||
deadline_summary = {}
|
||||
for record in quarterly_records[:20]: # Limit to first 20 for readability
|
||||
quarter = record.quartal
|
||||
if quarter not in deadline_summary:
|
||||
deadline_summary[quarter] = []
|
||||
deadline_summary[quarter].append(str(record.faelligkeitsdatum))
|
||||
|
||||
for quarter in sorted(deadline_summary.keys()):
|
||||
unique_dates = list(set(deadline_summary[quarter]))
|
||||
expected_dates = {
|
||||
1: '2025-03-15',
|
||||
2: '2025-06-15',
|
||||
3: '2025-09-15',
|
||||
4: '2025-12-15'
|
||||
}
|
||||
expected = expected_dates[quarter]
|
||||
status = '✓' if len(unique_dates) == 1 and unique_dates[0] == expected else '❌'
|
||||
self.stdout.write(f' Q{quarter}: {status} Found: {unique_dates}, Expected: {expected}')
|
||||
|
||||
# Check payment due dates
|
||||
self.stdout.write('\n3. Payment Due Dates (Recent):')
|
||||
recent_payments = DestinataerUnterstuetzung.objects.filter(
|
||||
beschreibung__icontains='Q4/2025'
|
||||
).order_by('-faellig_am')[:5]
|
||||
|
||||
if recent_payments:
|
||||
for payment in recent_payments:
|
||||
self.stdout.write(f' {payment.destinataer.get_full_name()}: {payment.faellig_am} - {payment.beschreibung}')
|
||||
else:
|
||||
self.stdout.write(' No Q4/2025 payments found')
|
||||
|
||||
# Check all 2025 payments pattern
|
||||
self.stdout.write('\n4. All 2025 Payment Due Date Pattern:')
|
||||
all_2025_payments = DestinataerUnterstuetzung.objects.filter(
|
||||
faellig_am__year=2025,
|
||||
beschreibung__icontains='/'
|
||||
).values_list('faellig_am', 'beschreibung').order_by('faellig_am')
|
||||
|
||||
payment_patterns = {}
|
||||
for payment_date, desc in all_2025_payments:
|
||||
if 'Q' in desc and '2025' in desc:
|
||||
quarter_info = desc.split('/')[-1] if '/' in desc else desc
|
||||
if quarter_info not in payment_patterns:
|
||||
payment_patterns[quarter_info] = []
|
||||
payment_patterns[quarter_info].append(str(payment_date))
|
||||
|
||||
for quarter_desc in sorted(payment_patterns.keys()):
|
||||
unique_dates = list(set(payment_patterns[quarter_desc]))
|
||||
self.stdout.write(f' {quarter_desc}: {unique_dates[:3]}{"..." if len(unique_dates) > 3 else ""}')
|
||||
|
||||
# Provide recommendations
|
||||
self.stdout.write('\n5. Recommendations:')
|
||||
|
||||
# Check if deadline migration worked
|
||||
q4_deadlines = VierteljahresNachweis.objects.filter(jahr=2025, quartal=4).values_list('faelligkeitsdatum', flat=True)
|
||||
unique_q4_deadlines = list(set(q4_deadlines))
|
||||
|
||||
if len(unique_q4_deadlines) == 1 and str(unique_q4_deadlines[0]) == '2025-12-15':
|
||||
self.stdout.write(' ✓ Quarterly confirmation deadlines are correct')
|
||||
else:
|
||||
self.stdout.write(' ❌ Run: python manage.py update_semester_deadlines')
|
||||
|
||||
# Check if payments need updating
|
||||
wrong_payments = DestinataerUnterstuetzung.objects.filter(
|
||||
faellig_am='2025-12-31',
|
||||
beschreibung__icontains='Q4/2025'
|
||||
).count()
|
||||
|
||||
if wrong_payments > 0:
|
||||
self.stdout.write(f' ❌ {wrong_payments} payments have wrong due date (31.12 instead of 15.12)')
|
||||
self.stdout.write(' Run: python manage.py fix_q4_payment_dates')
|
||||
else:
|
||||
self.stdout.write(' ✓ Payment due dates appear correct')
|
||||
93
app/stiftung/management/commands/fix_q4_payment_dates.py
Normal file
93
app/stiftung/management/commands/fix_q4_payment_dates.py
Normal file
@@ -0,0 +1,93 @@
|
||||
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 Q4 payment due dates from December 31 to December 15'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--dry-run',
|
||||
action='store_true',
|
||||
help='Show what would be updated without making changes',
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
dry_run = options['dry_run']
|
||||
|
||||
if dry_run:
|
||||
self.stdout.write(self.style.WARNING('DRY RUN MODE - No changes will be made'))
|
||||
|
||||
# Find all Q4/2025 payments with wrong due date (Dec 31 instead of Dec 15)
|
||||
wrong_payments = DestinataerUnterstuetzung.objects.filter(
|
||||
faellig_am='2025-12-31',
|
||||
beschreibung__icontains='Q4/2025'
|
||||
)
|
||||
|
||||
total_count = wrong_payments.count()
|
||||
self.stdout.write(f'Found {total_count} Q4/2025 payments with wrong due date (31.12 instead of 15.12)')
|
||||
|
||||
if total_count == 0:
|
||||
self.stdout.write(self.style.SUCCESS('No payments need fixing!'))
|
||||
return
|
||||
|
||||
updated_count = 0
|
||||
correct_date = date(2025, 12, 15) # December 15, 2025
|
||||
|
||||
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'])
|
||||
|
||||
updated_count += 1
|
||||
|
||||
# Show progress for every 5 updates or if verbose
|
||||
if updated_count % 5 == 0 or options['verbosity'] >= 2:
|
||||
self.stdout.write(
|
||||
f' {payment.destinataer.get_full_name()}: '
|
||||
f'{old_date} → {correct_date} ({payment.betrag}€)'
|
||||
)
|
||||
|
||||
# Summary
|
||||
if dry_run:
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f'DRY RUN: Would update {updated_count} payment due dates from 31.12.2025 to 15.12.2025'
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.stdout.write(
|
||||
self.style.SUCCESS(
|
||||
f'Successfully updated {updated_count} payment due dates to December 15, 2025'
|
||||
)
|
||||
)
|
||||
|
||||
# Also check for other quarters that might have wrong dates
|
||||
self.stdout.write('\nChecking other quarters for potential issues:')
|
||||
|
||||
quarter_checks = {
|
||||
'Q1/2025': ('2025-02-15', '2025-03-15'),
|
||||
'Q2/2025': ('2025-05-15', '2025-06-15'),
|
||||
'Q3/2025': ('2025-08-15', '2025-09-15'),
|
||||
}
|
||||
|
||||
for quarter_desc, (old_expected, new_expected) in quarter_checks.items():
|
||||
old_date_payments = DestinataerUnterstuetzung.objects.filter(
|
||||
faellig_am=old_expected,
|
||||
beschreibung__icontains=quarter_desc
|
||||
).count()
|
||||
|
||||
if old_date_payments > 0:
|
||||
self.stdout.write(f' ⚠️ {quarter_desc}: {old_date_payments} payments still use old date {old_expected}')
|
||||
self.stdout.write(f' Should be: {new_expected}')
|
||||
|
||||
self.stdout.write('\nSemester-based payment schedule:')
|
||||
self.stdout.write(' Q1: March 15 (Spring semester)')
|
||||
self.stdout.write(' Q2: June 15 (Auto-approved with Q1)')
|
||||
self.stdout.write(' Q3: September 15 (Fall semester)')
|
||||
self.stdout.write(' Q4: December 15 (Auto-approved with Q3)')
|
||||
Reference in New Issue
Block a user