From 1a40277d6686eb6427ad729c2f1d8d97c77154ac Mon Sep 17 00:00:00 2001 From: Jan Remmer Siebels Date: Sun, 5 Oct 2025 00:48:21 +0200 Subject: [PATCH] CRITICAL FIX: Remove duplicate signal handler causing double transactions - Fixed signals.py which contained two signal handlers creating duplicate transactions - Removed broken signal handler that created transactions without referenz - Keep only the proper signal handler with PAY- referenz and duplicate prevention - This resolves the issue where payments were deducted twice from account balance - Cleaned up malformed docstring and signal structure in signals.py The issue was that payments were processed by both: 1. Broken signal handler (empty referenz) - creating first transaction 2. Proper signal handler (PAY- referenz) - creating second transaction Now only the proper handler runs, preventing double balance deduction. --- app/stiftung/signals.py | 124 ++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 81 deletions(-) diff --git a/app/stiftung/signals.py b/app/stiftung/signals.py index f6533be..e02711a 100644 --- a/app/stiftung/signals.py +++ b/app/stiftung/signals.py @@ -1,31 +1,6 @@ """ Django signals for the Stiftung app. -Handles automatic # Check if a bank transaction already exists for this specific payment - existing_transaction = BankTransaction.objects.filter( - konto=instance.konto, - betrag=-instance.betrag, # Negative for outgoing payment - kommentare__contains=f'Unterstützung {instance.id}' - ).first() - - if existing_transaction: - print(f"⚠️ Transaction already exists for payment {instance.id} - skipping creation") - return - - # Create a bank transaction for this payment - BankTransaction.objects.create( - konto=instance.konto, - datum=instance.ausgezahlt_am or timezone.now().date(), - valuta=instance.ausgezahlt_am or timezone.now().date(), - betrag=-instance.betrag, # Negative because it's an outgoing payment - waehrung='EUR', - verwendungszweck=f"Unterstützungszahlung: {instance.beschreibung or instance.destinataer.get_full_name()}", - empfaenger_zahlungspflichtiger=instance.empfaenger_name or instance.destinataer.get_full_name(), - iban_gegenpartei=instance.empfaenger_iban or '', - transaction_type='ueberweisung', - status='verified', - kommentare=f'Automatisch erstellt bei Markierung als ausgezahlt für Unterstützung {instance.id}', - referenz=f'PAY-{instance.id}', # Add unique reference to avoid conflicts - )model instances change. +Handles automatic payment tracking and account balance updates when model instances change. """ from django.db.models.signals import post_save, pre_save @@ -64,8 +39,6 @@ def update_account_balance_on_payment(sender, instance, created, **kwargs): # Check if a transaction already exists for this payment to prevent duplicates existing_transaction = BankTransaction.objects.filter( - konto=instance.konto, - betrag=-instance.betrag, # Negative for outgoing payment kommentare__contains=f'Unterstützung {instance.id}' ).first() @@ -81,35 +54,28 @@ def update_account_balance_on_payment(sender, instance, created, **kwargs): ausgezahlt_am=instance.ausgezahlt_am ) - # Check if a transaction already exists for this payment - existing_transaction = BankTransaction.objects.filter( - kommentare__contains=f'Unterstützung {instance.id}' - ).first() + # Create a bank transaction for this payment + transaction = BankTransaction.objects.create( + konto=instance.konto, + datum=instance.ausgezahlt_am or timezone.now().date(), + valuta=instance.ausgezahlt_am or timezone.now().date(), + betrag=-instance.betrag, # Negative because it's an outgoing payment + waehrung='EUR', + verwendungszweck=f"Unterstützungszahlung: {instance.beschreibung or instance.destinataer.get_full_name()}", + empfaenger_zahlungspflichtiger=instance.empfaenger_name or instance.destinataer.get_full_name(), + iban_gegenpartei=instance.empfaenger_iban or '', + transaction_type='ueberweisung', + status='verified', + referenz=f'PAY-{instance.id}', # Unique reference to prevent duplicates + kommentare=f'Automatisch erstellt bei Markierung als ausgezahlt für Unterstützung {instance.id}', + ) - if not existing_transaction: - # Create a bank transaction for this payment - transaction = BankTransaction.objects.create( - konto=instance.konto, - datum=instance.ausgezahlt_am or timezone.now().date(), - valuta=instance.ausgezahlt_am or timezone.now().date(), - betrag=-instance.betrag, # Negative because it's an outgoing payment - waehrung='EUR', - verwendungszweck=f"Unterstützungszahlung: {instance.beschreibung or instance.destinataer.get_full_name()}", - empfaenger_zahlungspflichtiger=instance.empfaenger_name or instance.destinataer.get_full_name(), - iban_gegenpartei=instance.empfaenger_iban or '', - transaction_type='ueberweisung', - status='verified', - referenz=f'PAY-{instance.id}', # Unique reference to prevent duplicates - kommentare=f'Automatisch erstellt bei Markierung als ausgezahlt für Unterstützung {instance.id}', - ) - # Update account balance only for new transactions - instance.konto.saldo -= instance.betrag - instance.konto.saldo_datum = instance.ausgezahlt_am or timezone.now().date() - instance.konto.save() - print(f"✅ Account balance updated: {instance.konto.kontoname} - €{instance.betrag} (Payment to {instance.destinataer.get_full_name()}) - Transaction {transaction.id}") - else: - transaction = existing_transaction - print(f"ℹ️ Transaction already exists for payment {instance.id}, balance not modified") + # Update account balance + instance.konto.saldo -= instance.betrag + instance.konto.saldo_datum = instance.ausgezahlt_am or timezone.now().date() + instance.konto.save() + + print(f"✅ Account balance updated: {instance.konto.kontoname} - €{instance.betrag} (Payment to {instance.destinataer.get_full_name()}) - Transaction {transaction.id}") # Handle reversal if payment is changed from paid back to unpaid elif old_status == 'ausgezahlt' and instance.status != 'ausgezahlt': @@ -132,35 +98,31 @@ def update_account_balance_on_payment(sender, instance, created, **kwargs): instance.konto.saldo_datum = timezone.now().date() instance.konto.save() - print(f"✅ Account balance reversed: {instance.konto.kontoname} + €{instance.betrag} (Payment reversal for {instance.destinataer.get_full_name()})") - else: - print(f"⚠️ No transaction found to reverse for payment {instance.id}") - + # Clear the ausgezahlt_am date + # Update without triggering signals + DestinataerUnterstuetzung.objects.filter(pk=instance.pk).update( + ausgezahlt_am=None + ) + + print(f"🔄 Payment reversal: Account balance restored for {instance.destinataer.get_full_name()} - €{instance.betrag}") except Exception as e: - print(f"⚠️ Error reversing payment transaction: {e}") - - # Clear the ausgezahlt_am date - if instance.ausgezahlt_am: - # Update without triggering signals - DestinataerUnterstuetzung.objects.filter(pk=instance.pk).update( - ausgezahlt_am=None - ) + print(f"⚠️ Error reversing payment for {instance.destinataer.get_full_name()}: {e}") @receiver(post_save, sender=BankTransaction) def update_account_balance_on_transaction(sender, instance, created, **kwargs): """ - Update account balance when a new bank transaction is imported or created. - Only update if the transaction has a saldo_nach_buchung value or if it's manually created. + Update account balance whenever a new bank transaction is created or modified + (excluding transactions created by payment processing to avoid double-counting) """ - if created and instance.status in ['verified', 'imported']: - # If the transaction has a balance after booking, use that - if instance.saldo_nach_buchung is not None: - instance.konto.saldo = instance.saldo_nach_buchung - instance.konto.saldo_datum = instance.datum - instance.konto.save() - else: - # Otherwise, calculate the new balance - instance.konto.saldo += instance.betrag - instance.konto.saldo_datum = instance.datum - instance.konto.save() \ No newline at end of file + # Skip if this is a payment-related transaction (already handled by payment signal) + if instance.kommentare and 'Unterstützung' in instance.kommentare: + return + + if created: + # Update the account balance based on the transaction + instance.konto.saldo += instance.betrag # Add the transaction amount + instance.konto.saldo_datum = instance.valuta or instance.datum + instance.konto.save() + + print(f"💰 Account balance updated from transaction: {instance.konto.kontoname} {'+ ' if instance.betrag >= 0 else '- '}€{abs(instance.betrag)}") \ No newline at end of file