Files
stiftung-management-system/app/stiftung/management/commands/reconcile_balances.py
Jan Remmer Siebels c289cc3c58 Fix payment system balance integration and add calendar functionality
- Implement automated payment tracking with Django signals
- Fix duplicate transaction creation with unique referenz system
- Add calendar system with CRUD operations and event management
- Reorganize navigation menu (rename sections, move admin functions)
- Replace Geschichte editor with EasyMDE markdown editor
- Add management commands for balance reconciliation
- Create missing transactions for previously paid payments
- Ensure account balances accurately reflect all payment activity

Features added:
- Calendar entries creation and administration via menu
- Payment status tracking with automatic balance updates
- Duplicate prevention for payment transactions
- Markdown editor with live preview for Geschichte pages
- Database reconciliation tools for payment/balance sync

Bug fixes:
- Resolved IntegrityError on payment status changes
- Fixed missing account balance updates for paid payments
- Prevented duplicate balance deductions on re-saves
- Corrected menu structure and admin function placement
2025-10-05 00:38:18 +02:00

145 lines
5.3 KiB
Python

"""
Management command to reconcile account balances with actual transactions.
This command will:
1. Calculate the correct balance based on all bank transactions
2. Update the account balance to match the calculated balance
3. Show discrepancies between stored and calculated balances
Usage:
python manage.py reconcile_balances
"""
from django.core.management.base import BaseCommand
from django.utils import timezone
from decimal import Decimal
from stiftung.models import StiftungsKonto, BankTransaction
class Command(BaseCommand):
help = 'Reconcile account balances with bank transactions'
def add_arguments(self, parser):
parser.add_argument(
'--dry-run',
action='store_true',
help='Show what would be done without making changes',
)
parser.add_argument(
'--account',
type=str,
help='Only process specific account (by kontoname)',
)
def handle(self, *args, **options):
dry_run = options['dry_run']
account_filter = options.get('account')
self.stdout.write(
self.style.SUCCESS('🔍 Reconciling account balances with transactions...')
)
if dry_run:
self.stdout.write(
self.style.WARNING('DRY RUN MODE - No changes will be made')
)
# Get accounts to process
accounts_query = StiftungsKonto.objects.filter(aktiv=True)
if account_filter:
accounts_query = accounts_query.filter(kontoname__icontains=account_filter)
accounts = accounts_query.all()
if not accounts:
self.stdout.write(
self.style.ERROR('No accounts found matching criteria')
)
return
self.stdout.write(f"Processing {accounts.count()} account(s)...")
total_discrepancies = 0
fixed_accounts = 0
for account in accounts:
self.stdout.write(f"\n🏦 {account.bank_name} - {account.kontoname}")
self.stdout.write(f" Stored Balance: €{account.saldo}")
self.stdout.write(f" Last Updated: {account.saldo_datum}")
# Calculate balance from transactions
transactions = BankTransaction.objects.filter(konto=account).order_by('datum')
calculated_balance = Decimal('0.00')
transaction_count = transactions.count()
for transaction in transactions:
calculated_balance += transaction.betrag
self.stdout.write(f" Transactions: {transaction_count}")
self.stdout.write(f" Calculated Balance: €{calculated_balance}")
discrepancy = account.saldo - calculated_balance
if discrepancy != 0:
total_discrepancies += 1
self.stdout.write(
self.style.WARNING(f" ⚠️ Discrepancy: €{discrepancy}")
)
if not dry_run:
# Update the account balance
old_balance = account.saldo
account.saldo = calculated_balance
# Update the balance date to the latest transaction date or today
if transactions.exists():
latest_transaction = transactions.order_by('-datum').first()
account.saldo_datum = latest_transaction.datum
else:
account.saldo_datum = timezone.now().date()
account.save()
fixed_accounts += 1
self.stdout.write(
self.style.SUCCESS(
f" ✅ Updated: €{old_balance} → €{calculated_balance}"
)
)
else:
self.stdout.write(
f" 📝 Would update: €{account.saldo} → €{calculated_balance}"
)
else:
self.stdout.write(
self.style.SUCCESS(" ✅ Balance is correct")
)
# Show recent transactions
if transaction_count > 0:
self.stdout.write(" Recent transactions:")
recent = transactions.order_by('-datum')[:3]
for trans in recent:
self.stdout.write(
f" {trans.datum}: €{trans.betrag} - {trans.verwendungszweck[:50]}"
)
# Summary
self.stdout.write("\n" + "=" * 50)
if dry_run:
self.stdout.write(
f"📊 Found {total_discrepancies} account(s) with balance discrepancies"
)
if total_discrepancies > 0:
self.stdout.write(" Run without --dry-run to fix these discrepancies")
else:
if fixed_accounts > 0:
self.stdout.write(
self.style.SUCCESS(
f"🎉 Fixed {fixed_accounts} account balance(s)"
)
)
else:
self.stdout.write(
self.style.SUCCESS("✅ All account balances were already correct")
)