svthalia/concrexit

View on GitHub
website/moneybirdsynchronization/signals.py

Summary

Maintainability
A
3 hrs
Test Coverage
"""The signals checked by the moneybrid synchronization package."""
import logging

from django.contrib.auth import get_user_model
from django.db.models.signals import post_delete, post_save

from members.models import Member, Profile
from moneybirdsynchronization import services
from moneybirdsynchronization.administration import Administration
from moneybirdsynchronization.emails import send_sync_error
from moneybirdsynchronization.models import MoneybirdExternalInvoice
from payments.models import BankAccount
from payments.signals import processed_batch
from utils.models.signals import suspendingreceiver

logger = logging.getLogger(__name__)
User = get_user_model()


@suspendingreceiver(post_save, sender="members.Profile")
def post_profile_save(sender, instance, **kwargs):
    """Update the contact in Moneybird when the profile is saved."""
    updated_fields = kwargs.get("update_fields", None)
    if updated_fields is not None and not any(
        field in updated_fields
        for field in [
            "is_minimized",
            "address_street",
            "address_street2",
            "address_postal_code",
            "address_city",
            "address_country",
        ]
    ):
        return

    if not instance.user.first_name or not instance.user.last_name:
        return

    if hasattr(instance.user, "moneybird_contact"):
        instance.user.moneybird_contact.needs_synchronization = True
        instance.user.moneybird_contact.save()


@suspendingreceiver(post_delete, sender="members.Profile")
def post_profile_delete(sender, instance, **kwargs):
    """Delete the contact in Moneybird when the profile is deleted."""
    if hasattr(instance.user, "moneybird_contact"):
        instance.user.moneybird_contact.needs_synchronization = True
        instance.user.moneybird_contact.save()


@suspendingreceiver(
    post_save,
    sender=User,
)
def post_user_save(sender, instance, **kwargs):
    """Update the contact in Moneybird when the user is saved."""
    try:
        instance.profile
    except Profile.DoesNotExist:
        return

    updated_fields = kwargs.get("update_fields", None)
    if updated_fields is not None and not any(
        field in updated_fields for field in ["first_name", "last_name", "email"]
    ):
        # Only update the contact when the name is changed
        return

    if hasattr(instance, "moneybird_contact"):
        instance.moneybird_contact.needs_synchronization = True
        instance.moneybird_contact.save()


@suspendingreceiver(post_delete, sender=User)
def post_user_delete(sender, instance, **kwargs):
    """Delete the contact in Moneybird when the user is deleted."""
    if hasattr(instance, "moneybird_contact"):
        instance.moneybird_contact.needs_synchronization = True
        instance.moneybird_contact.save()


@suspendingreceiver(post_save, sender=BankAccount)
def post_bank_account_save(sender, instance, **kwargs):
    """Update the contact in Moneybird when the bank account is saved."""
    updated_fields = kwargs.get("update_fields", None)
    if updated_fields is not None and not any(
        field in updated_fields
        for field in ["owner", "iban", "bic", "initials", "last_name"]
    ):
        # Only update the contact when the bank account is changed
        return

    member = Member.objects.get(pk=instance.owner.pk)
    if hasattr(member, "moneybird_contact"):
        member.moneybird_contact.needs_synchronization = True
        member.moneybird_contact.save()


@suspendingreceiver(post_delete, sender=BankAccount)
def post_bank_account_delete(sender, instance, **kwargs):
    """Update the contact in Moneybird when the bank account is deleted."""
    member = Member.objects.get(pk=instance.owner.pk)
    if hasattr(member, "moneybird_contact"):
        member.moneybird_contact.needs_synchronization = True
        member.moneybird_contact.save()


@suspendingreceiver(
    post_save,
    sender="registrations.Renewal",
    dispatch_uid="mark_renewal_invoice_outdated",
)
@suspendingreceiver(
    post_save,
    sender="registrations.Registration",
    dispatch_uid="mark_registration_invoice_outdated",
)
@suspendingreceiver(
    post_save, sender="pizzas.FoodOrder", dispatch_uid="mark_foodorder_invoice_outdated"
)
@suspendingreceiver(
    post_save, sender="sales.Order", dispatch_uid="mark_salesorder_invoice_outdated"
)
@suspendingreceiver(
    post_save,
    sender="events.EventRegistration",
    dispatch_uid="mark_eventregistration_invoice_outdated",
)
def mark_invoice_outdated(sender, instance, **kwargs):
    """Mark the invoice as outdated if it exists, so that it will be resynchronized."""
    invoice = MoneybirdExternalInvoice.get_for_object(instance)
    if invoice and not invoice.needs_synchronization:
        invoice.needs_synchronization = True
        invoice.save()


@suspendingreceiver(post_delete, sender="registrations.Renewal")
@suspendingreceiver(post_delete, sender="registrations.Registration")
@suspendingreceiver(post_delete, sender="pizzas.FoodOrder")
@suspendingreceiver(post_delete, sender="events.EventRegistration")
@suspendingreceiver(post_delete, sender="sales.Order")
def post_renewal_delete(sender, instance, **kwargs):
    """When a payable is deleted, other than during data minimisation, delete the invoice.

    When objects are deleted for data minimisation, we don't want to delete the
    Moneybird invoice as well, because we are obligated to store those for 7 years.
    """
    # During data minimisation, deletions are marked with a flag. This is currently
    # the case only for registrations and renewals. The other payables are not deleted
    # for data minimisation, but bulk-updated to remove personally identifiable
    # information. Those bulk updates do not trigger post_save signals.
    if getattr(instance, "__deleting_for_dataminimisation", False):
        return

    invoice = MoneybirdExternalInvoice.get_for_object(instance)
    if invoice:
        invoice.needs_deletion = True
        invoice.save()


@suspendingreceiver(post_delete, sender="moneybirdsynchronization.MoneybirdPayment")
def post_payment_delete(sender, instance, **kwargs):
    try:
        services.delete_moneybird_payment(instance)
    except Administration.Error as e:
        logger.exception("Moneybird synchronization error: %s", e)
        send_sync_error(e, instance)


@suspendingreceiver(
    processed_batch,
)
def post_processed_batch(sender, instance, **kwargs):
    try:
        services.process_thalia_pay_batch(instance)
    except Administration.Error as e:
        logger.exception("Moneybird synchronization error: %s", e)
        send_sync_error(e, instance)