diff --git a/src/pretix/plugins/sendmail/forms.py b/src/pretix/plugins/sendmail/forms.py index e407df6fb0..45980c5701 100644 --- a/src/pretix/plugins/sendmail/forms.py +++ b/src/pretix/plugins/sendmail/forms.py @@ -309,8 +309,8 @@ class RuleForm(FormPlaceholderMixin, I18nModelForm): class Meta: model = Rule - fields = ['subject', 'template', 'attach_ical', - 'send_date', 'send_offset_days', 'send_offset_time', + fields = ['subject', 'template', 'attach_ical', 'send_date', + 'send_offset_days', 'send_offset_time', 'subevent', 'all_products', 'limit_products', 'restrict_to_status', 'checked_in_status', 'send_to', 'enabled'] @@ -359,6 +359,29 @@ class RuleForm(FormPlaceholderMixin, I18nModelForm): super().__init__(*args, **kwargs) + self.fields['subevent'] = forms.ModelChoiceField( + SubEvent.objects.none(), + label=pgettext_lazy('sendmail_form', 'Restrict to a specific event date'), + required=False, + empty_label=pgettext_lazy('subevent', 'All dates') + ) + + if self.event.has_subevents: + self.fields['subevent'].queryset = self.event.subevents.all() + self.fields['subevent'].widget = Select2( + attrs={ + 'data-model-select2': 'event', + 'data-select2-url': reverse('control:event.subevents.select2', kwargs={ + 'event': self.event.slug, + 'organizer': self.event.organizer.slug, + }), + 'data-placeholder': pgettext_lazy('subevent', 'Date') + } + ) + self.fields['subevent'].widget.choices = self.fields['subevent'].choices + else: + del self.fields['subevent'] + self.fields['limit_products'].queryset = Item.objects.filter(event=self.event) self.fields['schedule_type'] = forms.ChoiceField( diff --git a/src/pretix/plugins/sendmail/migrations/0006_rule_subevent_alter_rule_checked_in_status.py b/src/pretix/plugins/sendmail/migrations/0006_rule_subevent_alter_rule_checked_in_status.py new file mode 100644 index 0000000000..0ddaf6f53c --- /dev/null +++ b/src/pretix/plugins/sendmail/migrations/0006_rule_subevent_alter_rule_checked_in_status.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.4 on 2023-08-29 15:31 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sendmail', '0005_rule_checked_in_status'), + ] + + operations = [ + migrations.AddField( + model_name='rule', + name='subevent', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='pretixbase.subevent'), + ), + migrations.AlterField( + model_name='rule', + name='checked_in_status', + field=models.CharField(default=None, max_length=10, null=True), + ), + ] diff --git a/src/pretix/plugins/sendmail/models.py b/src/pretix/plugins/sendmail/models.py index e3e6089bb8..c252a07122 100644 --- a/src/pretix/plugins/sendmail/models.py +++ b/src/pretix/plugins/sendmail/models.py @@ -227,6 +227,7 @@ class Rule(models.Model, LoggingMixin): id = models.BigAutoField(primary_key=True) event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='sendmail_rules') + subevent = models.ForeignKey(SubEvent, null=True, on_delete=models.PROTECT) subject = I18nCharField(max_length=255, verbose_name=_('Subject')) template = I18nTextField(verbose_name=_('Message')) @@ -278,16 +279,27 @@ class Rule(models.Model, LoggingMixin): create_sms = [] if self.event.has_subevents: - for se in self.event.subevents.annotate(has_sm=Exists(ScheduledMail.objects.filter( - subevent=OuterRef('pk'), rule=self))).filter(has_sm=False): - sm = ScheduledMail(rule=self, subevent=se, event=self.event) - sm.recompute() - create_sms.append(sm) + if self.subevent: + ScheduledMail.objects.get_or_create(rule=self, subevent=self.subevent, event=self.event) + else: + for se in self.event.subevents.annotate(has_sm=Exists(ScheduledMail.objects.filter( + subevent=OuterRef('pk'), rule=self))).filter(has_sm=False): + sm = ScheduledMail(rule=self, subevent=se, event=self.event) + sm.recompute() + create_sms.append(sm) ScheduledMail.objects.bulk_create(create_sms) else: ScheduledMail.objects.get_or_create(rule=self, event=self.event) if not is_creation: + if self.subevent: + keep_states = [ScheduledMail.STATE_COMPLETED] # we keep rules where mails have already been sent + ScheduledMail.objects.filter( + Q(rule=self), + ~Q(subevent=self.subevent), + ~Q(state__in=keep_states) + ).delete() + update_sms = [] for sm in self.scheduledmail_set.prefetch_related('event').select_related('subevent'): if sm in create_sms: diff --git a/src/pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html b/src/pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html index 292c014560..c1207576fb 100644 --- a/src/pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html +++ b/src/pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_create.html @@ -27,9 +27,12 @@
{% trans "Recipients" %} {% bootstrap_field form.send_to layout='control' %} + {% if form.subevent %} + {% bootstrap_field form.subevent layout='control' %} + {% endif %} {% bootstrap_field form.restrict_to_status layout='control' %} {% bootstrap_field form.checked_in_status layout='control' %} -
+
{% bootstrap_field form.all_products layout='control' %} {% bootstrap_field form.limit_products layout='horizontal' %}
diff --git a/src/pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html b/src/pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html index b4d7081882..b9b62fb5f7 100644 --- a/src/pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html +++ b/src/pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_inspect.html @@ -47,7 +47,7 @@ {{ sm.subevent.name }}
- {{ sm.get_date_range_display }} + {{ sm.subevent.get_date_range_display }} {% endif %} diff --git a/src/pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html b/src/pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html index f7453f232a..3dfe6d0153 100644 --- a/src/pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html +++ b/src/pretix/plugins/sendmail/templates/pretixplugins/sendmail/rule_update.html @@ -41,9 +41,12 @@
{% trans "Recipients" %} {% bootstrap_field form.send_to layout='control' %} + {% if form.subevent %} + {% bootstrap_field form.subevent layout='control' %} + {% endif %} {% bootstrap_field form.restrict_to_status layout='control' %} {% bootstrap_field form.checked_in_status layout='control' %} -
+
{% bootstrap_field form.all_products layout='control' %} {% bootstrap_field form.limit_products layout='horizontal' %}
diff --git a/src/tests/plugins/sendmail/test_rules.py b/src/tests/plugins/sendmail/test_rules.py index cedc7debf5..cbc992196c 100644 --- a/src/tests/plugins/sendmail/test_rules.py +++ b/src/tests/plugins/sendmail/test_rules.py @@ -253,6 +253,58 @@ def test_sendmail_rule_send_correct_subevent(order, event_series, subevent1, sub assert djmail.outbox[0].to[0] == p1.attendee_email +@pytest.mark.django_db +@scopes_disabled() +def test_sendmail_rule_specified_subevent(order, event_series, subevent1, subevent2, item): + djmail.outbox = [] + + order.all_positions.create(item=item, price=13, subevent=subevent1) + order.all_positions.create(item=item, price=23, subevent=subevent2) + + order.status = order.STATUS_PAID + order.save() + + rule = event_series.sendmail_rules.create(date_is_absolute=False, offset_is_after=False, send_offset_days=1, + send_offset_time=datetime.time(4, 30), subevent=subevent1, + subject='meow', template='meow meow meow') + + sendmail_run_rules(None) + + assert len(djmail.outbox) == 1 + + m = ScheduledMail.objects.filter(rule=rule).first() + assert m.subevent == subevent1 + + +@pytest.mark.django_db +@scopes_disabled() +def test_sendmail_rule_all_subevents(event_series, subevent1, subevent2, item): + djmail.outbox = [] + + o1 = Order.objects.create(event=item.event, status=Order.STATUS_PAID, + expires=now() + datetime.timedelta(hours=1), + total=13, code='DUMMY', email='dummy1@dummy.test', + datetime=now(), locale='en') + o1.all_positions.create(item=item, price=13, subevent=subevent1) + o1.all_positions.create(item=item, price=13, subevent=subevent2) + o2 = Order.objects.create(event=item.event, status=Order.STATUS_PAID, + expires=now() + datetime.timedelta(hours=1), + total=13, code='DUMMY', email='dummy2@dummy.test', + datetime=now(), locale='en') + o2.all_positions.create(item=item, price=23, subevent=subevent1) + o2.all_positions.create(item=item, price=23, subevent=subevent2) + + rule = event_series.sendmail_rules.create(date_is_absolute=False, offset_is_after=False, send_offset_days=1, + send_offset_time=datetime.time(4, 30), subevent=None, + subject='meow', template='meow meow meow') + sendmail_run_rules(None) + + ms = ScheduledMail.objects.filter(rule=rule) + assert len(ms) == 2 # one for each subevent + assert ms.get(subevent=subevent1) is not None + assert ms.get(subevent=subevent2) is not None + + @pytest.mark.django_db @scopes_disabled() def test_sendmail_rule_send_correct_products(event, order, item, item2):