""" 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. """ from django.db.models.signals import post_save, pre_save from django.dispatch import receiver from django.utils import timezone from decimal import Decimal from .models import DestinataerUnterstuetzung, BankTransaction @receiver(pre_save, sender=DestinataerUnterstuetzung) def unterstuetzung_pre_save(sender, instance, **kwargs): """Store the old status before saving to detect status changes""" if instance.pk: try: old_instance = DestinataerUnterstuetzung.objects.get(pk=instance.pk) instance._old_status = old_instance.status except DestinataerUnterstuetzung.DoesNotExist: instance._old_status = None else: instance._old_status = None @receiver(post_save, sender=DestinataerUnterstuetzung) def update_account_balance_on_payment(sender, instance, created, **kwargs): """ Update account balance when a payment is marked as paid (ausgezahlt). Creates a corresponding bank transaction and updates the account balance. Prevents duplicate transactions by checking if one already exists. """ # Only process if payment was just marked as paid old_status = getattr(instance, '_old_status', None) if instance.status == 'ausgezahlt' and old_status != 'ausgezahlt': # Payment was just marked as paid # 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() if existing_transaction: print(f"⚠️ Transaction already exists for payment {instance.id} to {instance.destinataer.get_full_name()}, skipping duplicate") return # Set the ausgezahlt_am date if not already set if not instance.ausgezahlt_am: instance.ausgezahlt_am = timezone.now().date() # Avoid infinite recursion by updating without triggering signals DestinataerUnterstuetzung.objects.filter(pk=instance.pk).update( 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() 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") # Handle reversal if payment is changed from paid back to unpaid elif old_status == 'ausgezahlt' and instance.status != 'ausgezahlt': # Payment was unmarked as paid - reverse the transaction # Find and delete the corresponding bank transaction try: # Look for the transaction created for this payment transaction = BankTransaction.objects.filter( konto=instance.konto, betrag=-instance.betrag, kommentare__contains=f'Unterstützung {instance.id}' ).first() if transaction: transaction.delete() # Reverse the account balance update instance.konto.saldo += instance.betrag 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}") 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 ) @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. """ 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()