diff --git a/src/pretix/plugins/sendmail/models.py b/src/pretix/plugins/sendmail/models.py index 8235eec9c..b68916c01 100644 --- a/src/pretix/plugins/sendmail/models.py +++ b/src/pretix/plugins/sendmail/models.py @@ -162,6 +162,8 @@ class ScheduledMail(models.Model): send_to_orders = self.rule.send_to in (Rule.CUSTOMERS, Rule.BOTH) send_to_attendees = self.rule.send_to in (Rule.ATTENDEES, Rule.BOTH) + position_ids = op_qs.values_list('id', flat=True) + for o in orders: with language(o.locale, e.settings.region): positions = list(o.positions.all()) @@ -191,28 +193,29 @@ class ScheduledMail(models.Model): positions = [p for p in positions if p.subevent_id == self.subevent_id] for p in positions: - if p.attendee_email and (p.attendee_email != o.email or not o_sent): - email_ctx = get_email_context( - event=e, - order=o, - invoice_address=ia, - position=p, - event_or_subevent=self.subevent or e, - ) - p.send_mail(self.rule.subject, self.rule.template, email_ctx, - attach_ical=self.rule.attach_ical, - log_entry_type='pretix.plugins.sendmail.rule.order.position.email.sent') - elif not o_sent and o.email: - email_ctx = get_email_context( - event=e, - order=o, - invoice_address=ia, - event_or_subevent=self.subevent or e, - ) - o.send_mail(self.rule.subject, self.rule.template, email_ctx, - attach_ical=self.rule.attach_ical, - log_entry_type='pretix.plugins.sendmail.rule.order.email.sent') - o_sent = True + if p.id in position_ids: + if p.attendee_email and (p.attendee_email != o.email or not o_sent): + email_ctx = get_email_context( + event=e, + order=o, + invoice_address=ia, + position=p, + event_or_subevent=self.subevent or e, + ) + p.send_mail(self.rule.subject, self.rule.template, email_ctx, + attach_ical=self.rule.attach_ical, + log_entry_type='pretix.plugins.sendmail.rule.order.position.email.sent') + elif not o_sent and o.email: + email_ctx = get_email_context( + event=e, + order=o, + invoice_address=ia, + event_or_subevent=self.subevent or e, + ) + o.send_mail(self.rule.subject, self.rule.template, email_ctx, + attach_ical=self.rule.attach_ical, + log_entry_type='pretix.plugins.sendmail.rule.order.email.sent') + o_sent = True self.last_successful_order_id = o.pk @@ -270,7 +273,7 @@ class Rule(models.Model, LoggingMixin): date_is_absolute = models.BooleanField(default=True, blank=True) offset_to_event_end = models.BooleanField(default=False, blank=True) # no verbose name because not actually - offset_is_after = models.BooleanField(default=False, blank=True) # displayed in any forms + offset_is_after = models.BooleanField(default=False, blank=True) # displayed in any forms send_to = models.CharField(max_length=10, choices=SEND_TO_CHOICES, default=CUSTOMERS, verbose_name=_('Send email to')) diff --git a/src/tests/plugins/sendmail/test_rules.py b/src/tests/plugins/sendmail/test_rules.py index 7b33f063f..41147c6ae 100644 --- a/src/tests/plugins/sendmail/test_rules.py +++ b/src/tests/plugins/sendmail/test_rules.py @@ -426,6 +426,97 @@ def test_sendmail_rule_checked_in_get_mail(event, order, item): assert len(djmail.outbox) == 1, "email not sent" +@pytest.mark.django_db +@scopes_disabled() +def test_sendmail_rule_checked_in_mixed_order(event, order, item): + order.status = Order.STATUS_PAID + order.save() + p1 = order.all_positions.create(item=item, price=13, attendee_email='item1@dummy.test') + order.all_positions.create(item=item, price=13, attendee_email='item2@dummy.test') # p2 + clist = event.checkin_lists.create(name="Default", all_products=True) + + # receives no mail when checked in + djmail.outbox = [] + perform_checkin(p1, clist, {}) + assert clist.checkin_count == 1 + event.sendmail_rules.create(send_date=dt_now - datetime.timedelta(hours=1), checked_in_status="checked_in", + subject='meow', template='meow meow meow') + sendmail_run_rules(None) + assert len(djmail.outbox) == 1 + + +@pytest.mark.django_db +@scopes_disabled() +def test_sendmail_rule_not_checked_in_mixed_order(event, order, item): + order.status = Order.STATUS_PAID + order.save() + p1 = order.all_positions.create(item=item, price=13, attendee_email='item1@dummy.test') + order.all_positions.create(item=item, price=13, attendee_email='item2@dummy.test') # p2 + clist = event.checkin_lists.create(name="Default", all_products=True) + + # receives no mail when checked in + djmail.outbox = [] + perform_checkin(p1, clist, {}) + assert clist.checkin_count == 1 + event.sendmail_rules.create(send_date=dt_now - datetime.timedelta(hours=1), checked_in_status="no_checkin", + subject='meow', template='meow meow meow') + sendmail_run_rules(None) + assert len(djmail.outbox) == 1 + + +@pytest.mark.django_db +@scopes_disabled() +def test_sendmail_rule_not_checked_in_mixed_order_position_without_email_not_matching_status(event, order, item, item2): + order.status = Order.STATUS_PAID + order.save() + p1 = order.all_positions.create(item=item, price=13, attendee_email='item1@dummy.test') + p2 = order.all_positions.create(item=item, price=13, attendee_email='item2@dummy.test') + clist = event.checkin_lists.create(name="Default", all_products=True) + + # receives no mail when checked in + djmail.outbox = [] + perform_checkin(p1, clist, {}) + + # we have no email and we are checked in + # we shouldn't trigger a fallback to order.email + p3 = order.all_positions.create(item=item, price=13) + perform_checkin(p3, clist, {}) + assert clist.checkin_count == 2 + + event.sendmail_rules.create(send_date=dt_now - datetime.timedelta(hours=1), checked_in_status="no_checkin", + subject='meow', template='meow meow meow', send_to=Rule.ATTENDEES) + sendmail_run_rules(None) + + assert len(djmail.outbox) == 1 + recipients = [m.to for m in djmail.outbox] + assert [p2.attendee_email] in recipients # for p2 + + +@pytest.mark.django_db +@scopes_disabled() +def test_sendmail_rule_not_checked_in_mixed_order_position_without_email(event, order, item, item2): + order.status = Order.STATUS_PAID + order.save() + p1 = order.all_positions.create(item=item, price=13, attendee_email='item1@dummy.test') + p2 = order.all_positions.create(item=item, price=13, attendee_email='item2@dummy.test') + order.all_positions.create(item=item, price=13) # p3 + order.all_positions.create(item=item, price=13) # p4 + clist = event.checkin_lists.create(name="Default", all_products=True) + + # receives no mail when checked in + djmail.outbox = [] + perform_checkin(p1, clist, {}) + assert clist.checkin_count == 1 + event.sendmail_rules.create(send_date=dt_now - datetime.timedelta(hours=1), checked_in_status="no_checkin", + subject='meow', template='meow meow meow', send_to=Rule.ATTENDEES) + sendmail_run_rules(None) + + assert len(djmail.outbox) == 2 + recipients = [m.to for m in djmail.outbox] + assert [order.email] in recipients # for p3 and p4 + assert [p2.attendee_email] in recipients # for p2 + + @pytest.mark.django_db @scopes_disabled() def run_restriction_test(event, order, restrictions_pass=[], restrictions_fail=[]):