From 5bbbf0334dd220db7c5fd509b471e2401eddd0b9 Mon Sep 17 00:00:00 2001 From: pajowu Date: Wed, 6 May 2026 15:56:06 +0200 Subject: [PATCH] sendmail: Do not copy rules with subevent when closing an event (Z#23233683) (#6156) --- ...0011_remove_cross_event_scheduled_mails.py | 19 +++++++++ src/pretix/plugins/sendmail/signals.py | 2 +- src/tests/plugins/sendmail/test_rules.py | 41 ++++++++++++++++++- 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 src/pretix/plugins/sendmail/migrations/0011_remove_cross_event_scheduled_mails.py diff --git a/src/pretix/plugins/sendmail/migrations/0011_remove_cross_event_scheduled_mails.py b/src/pretix/plugins/sendmail/migrations/0011_remove_cross_event_scheduled_mails.py new file mode 100644 index 0000000000..67e252a12b --- /dev/null +++ b/src/pretix/plugins/sendmail/migrations/0011_remove_cross_event_scheduled_mails.py @@ -0,0 +1,19 @@ +from django.db import migrations +from django.db.models import F + + +def remove_cross_event_scheduled_mails(apps, schema_editor): + Rule = apps.get_model("sendmail", "Rule") + ScheduledMail = apps.get_model("sendmail", "ScheduledMail") + ScheduledMail.objects.filter(rule__subevent__isnull=False).exclude(rule__subevent__event=F('rule__event')).delete() + Rule.objects.filter(subevent__isnull=False).exclude(subevent__event=F('event')).delete() + + +class Migration(migrations.Migration): + dependencies = [ + ("sendmail", "0010_auto_20250801_1342"), + ] + + operations = [ + migrations.RunPython(remove_cross_event_scheduled_mails), + ] diff --git a/src/pretix/plugins/sendmail/signals.py b/src/pretix/plugins/sendmail/signals.py index e573ce71d0..76626097fe 100644 --- a/src/pretix/plugins/sendmail/signals.py +++ b/src/pretix/plugins/sendmail/signals.py @@ -232,7 +232,7 @@ def sendmail_copy_data_receiver(sender, other, item_map, **kwargs): if sender.sendmail_rules.exists(): # idempotency return - for r in other.sendmail_rules.prefetch_related('limit_products'): + for r in other.sendmail_rules.filter(subevent__isnull=True).prefetch_related('limit_products'): limit_products = list(r.limit_products.all()) r = copy.copy(r) r.pk = None diff --git a/src/tests/plugins/sendmail/test_rules.py b/src/tests/plugins/sendmail/test_rules.py index 58f26aaae2..7b33f063f7 100644 --- a/src/tests/plugins/sendmail/test_rules.py +++ b/src/tests/plugins/sendmail/test_rules.py @@ -25,10 +25,11 @@ from zoneinfo import ZoneInfo import pytest from django.core import mail as djmail +from django.db.models import F from django.utils.timezone import now from django_scopes import scopes_disabled -from pretix.base.models import InvoiceAddress, Order +from pretix.base.models import Event, InvoiceAddress, Order from pretix.base.services.checkin import perform_checkin from pretix.plugins.sendmail.models import Rule, ScheduledMail from pretix.plugins.sendmail.signals import sendmail_run_rules @@ -687,3 +688,41 @@ def test_sendmail_context_localization(event, order, pos): sendmail_run_rules(None) assert "Hallo Herr Mustermann" in djmail.outbox[0].body + + +@pytest.mark.django_db +@scopes_disabled() +def test_event_clone_ignores_rules_with_subevent(event, order, pos): + event.has_subevents = True + event.save() + se1 = event.subevents.create(name="subevent 1", date_from=dt_now) + + event.sendmail_rules.create(date_is_absolute=False, send_offset_days=1, send_offset_time=datetime.time(hour=12), + subject='Mail To Subevent', template='TestBody', subevent=se1) + event.sendmail_rules.create(date_is_absolute=False, send_offset_days=1, send_offset_time=datetime.time(hour=12), + subject='Mail To All', template='TestBody') + + assert event.sendmail_rules.count() == 2 + assert event.scheduledmail_set.count() == 2 + for rule in event.sendmail_rules.all(): + assert rule.scheduledmail_set.count() == 1 + + copied_event = Event.objects.create( + organizer=event.organizer, name='Dummy2', slug='dummy2', + date_from=datetime.datetime(2022, 4, 15, 9, 0, 0, tzinfo=datetime.timezone.utc), + has_subevents=True + ) + copied_event.copy_data_from(event) + copied_event.refresh_from_db() + event.refresh_from_db() + + assert copied_event.sendmail_rules.count() == 1 + assert copied_event.sendmail_rules.first().scheduledmail_set.count() == 0 + assert str(copied_event.sendmail_rules.first().subject) == "Mail To All" + + copied_event.subevents.create(name="subevent 1 in copied", date_from=dt_now) + assert copied_event.sendmail_rules.first().scheduledmail_set.count() == 1 + + # Double-Check: no scheduled mails and rules exist where subevent.event and event do not align + assert ScheduledMail.objects.filter(rule__subevent__isnull=False).exclude(rule__subevent__event=F('rule__event')).count() == 0 + assert Rule.objects.filter(subevent__isnull=False).exclude(subevent__event=F('event')).count() == 0