Compare commits

...
Author SHA1 Message Date
Richard Schreiber 4119d04fba Add border to and increase text-contrast on disabled button (#6622)
* Add border to and increase text-contrast on disabled button

* Do not add border to disabled btn-link

* Add background to disabled button to further distinguish
2026-10-02 09:15:05 +02:00
84858bc048 Allow mails to be sent to addon-attendees (Z#23213551) (#6235)
Remove restrictions that prevent mails to be sent to addon-product-attendees while reducing amount of mails sent to the same email-addresses

Co-authored-by: Richard Schreiber <schreiber@rami.io>
Co-authored-by: Kara Engelhardt <engelhardt@pretix.eu>
2026-10-01 13:13:55 +02:00
Raphael Michel e8c091741b Invoice export: Fix crash PRETIXEU-FGE 2026-10-01 11:43:36 +02:00
Richard Schreiber ebdfd21b0f Fix JavaScript error description passing HTML (#6630) 2026-10-01 11:28:36 +02:00
pajowuandRichard Schreiber ed0b3cab7f Fix addon legend overlapping with profile checkbox (Z#23247075) (#6568)
* Fix addon legend overlapping with profile checkbox (Z#23247075)

* Update src/pretix/static/pretixpresale/scss/_checkout.scss

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

---------

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
2026-10-01 09:49:23 +02:00
10 changed files with 501 additions and 49 deletions
+2 -2
View File
@@ -71,8 +71,8 @@ class EmailTransmissionType(TransmissionType):
def transmission_info_to_form_data(self, transmission_info: dict) -> dict:
return {
"transmission_email_other": bool(transmission_info.get("transmission_email_address")),
"transmission_email_address": transmission_info.get("transmission_email_address"),
"transmission_email_other": bool((transmission_info or {}).get("transmission_email_address")),
"transmission_email_address": (transmission_info or {}).get("transmission_email_address"),
}
def form_data_to_transmission_info(self, form_data: dict) -> dict:
+11 -1
View File
@@ -161,7 +161,12 @@ class OrderMailForm(BaseMailForm):
),
label=pgettext_lazy('sendmail_form', 'Restrict to products'),
required=True,
queryset=Item.objects.none()
queryset=Item.objects.none(),
help_text=pgettext_lazy(
'sendmail_form',
'There may be multiple mails sent out to the same mail address if one order contains multiple attendee '
'products for it, if you restrict to products while also restricting mails to attendees only. '
'This is intended, as every one of those get linked to their own separate order page restricted to only that product.')
)
filter_checkins = forms.BooleanField(
label=_('Filter check-in status'),
@@ -374,6 +379,11 @@ class RuleForm(FormPlaceholderMixin, I18nModelForm):
del self.fields['subevent']
self.fields['limit_products'].queryset = Item.objects.filter(event=self.event)
self.fields['limit_products'].help_text = pgettext_lazy(
'sendmail_form',
'There may be multiple mails sent out to the same mail address if one order contains multiple attendee '
'products for it, if you restrict to products while also restricting mails to attendees only. '
'This is intended, as every one of those get linked to their own separate order page restricted to only that product.')
self.fields['schedule_type'] = forms.ChoiceField(
label=_('Type of schedule time'),
+50 -35
View File
@@ -19,6 +19,7 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
import logging
from datetime import datetime, time, timedelta
from dateutil.tz import datetime_exists
@@ -39,6 +40,8 @@ from pretix.base.models import (
)
from pretix.base.models.base import LoggingMixin
logger = logging.getLogger(__name__)
class ScheduledMail(models.Model):
STATE_SCHEDULED = 'scheduled'
@@ -115,7 +118,7 @@ class ScheduledMail(models.Model):
filter_orders_by_op = False
op_qs = OrderPosition.objects.filter(
order__event=self.event,
order__event=e,
canceled=False,
)
@@ -167,14 +170,58 @@ class ScheduledMail(models.Model):
for o in orders:
with language(o.locale, e.settings.region):
positions = list(o.positions.all())
o_sent = False
send_to_order = send_to_orders
try:
ia = o.invoice_address
except InvoiceAddress.DoesNotExist:
ia = InvoiceAddress(order=o)
if send_to_orders and o.email:
if send_to_attendees:
parent_op = None
sent_to_positions = set()
for p in positions:
if p.addon_to_id is None:
# this op might have matching add-ons, so save for later
parent_op = p
elif not parent_op or p.addon_to_id != parent_op.id:
# this op is an add-on, but not to the current parent_op
# something got mixed up as add-ons should always come directly after their parent
logger.warning(f"Add-ons are mixed up for position #{p.positionid} in order {o.full_code}")
continue
if p.id not in position_ids:
# not a matching op, just there for parent_op
continue
if p.addon_to_id and (
not p.attendee_email or p.attendee_email == parent_op.attendee_email
):
# if op is addon and either has no mail or the same as parent => send to parent
p = parent_op
if p.pk in sent_to_positions:
# this position already got an email
continue
if not p.attendee_email or p.attendee_email == o.email:
# no email or same as order => send to order
send_to_order = True
continue
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')
sent_to_positions.add(p.pk)
if send_to_order and o.email:
email_ctx = get_email_context(
event=e,
order=o,
@@ -184,38 +231,6 @@ class ScheduledMail(models.Model):
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 send_to_attendees:
if not self.rule.all_products:
positions = [p for p in positions if p.item_id in limit_products]
if self.subevent_id:
positions = [p for p in positions if p.subevent_id == self.subevent_id]
for p in positions:
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
+31 -9
View File
@@ -31,6 +31,7 @@
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
import logging
from datetime import datetime
from django.db.models import Exists, OuterRef, Q
@@ -43,6 +44,8 @@ from pretix.base.services.mail import mail
from pretix.base.services.tasks import ProfiledEventTask
from pretix.celery_app import app
logger = logging.getLogger(__name__)
def _chunks(lst, n):
"""
@@ -70,6 +73,8 @@ def send_mails_to_orders(event: Event, user: int, subject: dict, message: dict,
except InvoiceAddress.DoesNotExist:
ia = InvoiceAddress(order=o)
parent_op = None
sent_to_positions = set()
if recipients in ('both', 'attendees'):
for p in o.positions.annotate(
any_checkins=Exists(
@@ -85,10 +90,16 @@ def send_mails_to_orders(event: Event, user: int, subject: dict, message: dict,
)
),
).prefetch_related('addons', 'subevent'):
if p.addon_to_id is not None:
if p.addon_to_id is None:
parent_op = p
elif not parent_op or p.addon_to_id != parent_op.id:
# this op is an add-on, but not to the current parent_op
# something got mixed up as add-ons should always come directly after their parent
logger.warning(f"Add-ons are mixed up for position #{p.positionid} in order {o.full_code}")
continue
if p.item_id not in items and not any(a.item_id in items for a in p.addons.all()):
if p.item_id not in items:
continue
if filter_checkins:
@@ -99,13 +110,11 @@ def send_mails_to_orders(event: Event, user: int, subject: dict, message: dict,
if not allowed:
continue
if not p.attendee_email:
if recipients == 'attendees':
send_to_order = True
continue
if p.attendee_email == o.email and send_to_order:
continue
if p.addon_to_id and (
not p.attendee_email or p.attendee_email == parent_op.attendee_email
):
# if op is addon and either has no mail or the same as parent => send to parent
p = parent_op
if subevent and p.subevent_id != subevent:
continue
@@ -116,6 +125,18 @@ def send_mails_to_orders(event: Event, user: int, subject: dict, message: dict,
if subevents_to and p.subevent.date_from >= subevents_to:
continue
if not p.attendee_email:
send_to_order = True
continue
if p.pk in sent_to_positions:
# this position already got an email
continue
if p.attendee_email == o.email:
send_to_order = True
continue
with language(o.locale, event.settings.region):
email_context = get_email_context(event=event, order=o, invoice_address=ia, position=p)
outgoing_mail = mail(
@@ -137,6 +158,7 @@ def send_mails_to_orders(event: Event, user: int, subject: dict, message: dict,
user=user,
data=outgoing_mail.log_data(),
)
sent_to_positions.add(p.pk)
if send_to_order and o.email:
with language(o.locale, event.settings.region):
-1
View File
@@ -183,7 +183,6 @@ class OrderPositionDetailMixin(NoSearchIndexViewMixin):
def position(self):
qs = OrderPosition.objects.filter(
order__event=self.request.event,
addon_to__isnull=True,
order__code=self.kwargs['order'],
positionid=self.kwargs['position']
).select_related('order', 'order__event')
@@ -117,6 +117,16 @@ input[type=number]::-webkit-outer-spin-button {
border: transparent;
}
.btn.disabled, .btn[disabled], fieldset[disabled] .btn {
box-shadow: 0px 0px 0px 1px #ccc inset, inset 0 1px 3px 0 #d9d9d9;
background: #eee;
opacity: 0.7;
}
.btn-link.disabled, .btn-link[disabled], fieldset[disabled] .btn-link {
box-shadow: none;
background: none;
}
.btn-group-flex { display: flex; }
.btn-group-flex > .btn { flex: 1; }
@@ -813,7 +813,13 @@ function setup_basics (el) {
return
}
$('<li><a href="#' + scrollTarget.id + '">' + $.trim(label) + '</a> – ' + description + '</li>')
$('<li></li>')
.append(
$('<a></a>').attr("href", "#" + scrollTarget.id).text($.trim(label))
)
.append(
document.createTextNode(' - ' + description)
)
.appendTo(alert.querySelector('ul') || $('<ul>').appendTo(alert))
.find('a').on('click', function (e) {
$panel.collapse('show')
@@ -157,6 +157,12 @@
.profile-scope:last-child .profile-save-container {
margin-bottom: -15px;
}
/* the rule above removes blank space after any profile-save-container at the end of every fieldset, which causes an overlay if addon-questions are used. So re-add margin for non-addon-fieldsets followed by fieldsets (currently only happens it addon-questions are available) */
fieldset:not(:has(.profile-add-on)):has( + fieldset) .profile-scope:last-child .profile-save-container {
margin-bottom: 0;
}
.profile-save-container .help-block {
margin-bottom: 0;
}
+78
View File
@@ -199,6 +199,84 @@ def test_sendmail_rule_send_order_vs_pos(send_to, amount_mails, recipients, orde
assert djmail.outbox[0].subject == 'Dummy: Foo City @ 11:30'
@pytest.mark.django_db
@pytest.mark.parametrize('send_to,amount_mails,recipients,ticket_mail,addon_mail, products', [
(Rule.ATTENDEES, 1, ['addon-attendee@dummy.test'], 'attendee@dummy.test', 'addon-attendee@dummy.test', 'addon'),
(Rule.ATTENDEES, 2, ['attendee@dummy.test', 'addon-attendee@dummy.test'], 'attendee@dummy.test',
'addon-attendee@dummy.test', 'both'),
(Rule.ATTENDEES, 1, ['attendee@dummy.test'], 'attendee@dummy.test', 'attendee@dummy.test', 'both'),
(Rule.ATTENDEES, 1, ['attendee@dummy.test'], 'attendee@dummy.test', None, 'addon'),
(Rule.ATTENDEES, 1, ['attendee@dummy.test'], 'attendee@dummy.test', None, 'both'),
(Rule.ATTENDEES, 1, ['dummy@dummy.test'], None, None, 'addon'),
(Rule.ATTENDEES, 1, ['dummy@dummy.test'], None, None, 'both'),
(Rule.ATTENDEES, 2, ['dummy@dummy.test', 'addon-attendee@dummy.test'], None, 'addon-attendee@dummy.test', 'both'),
(Rule.ATTENDEES, 1, ['dummy@dummy.test'], None, None, 'all'),
(Rule.ATTENDEES, 2, ['dummy@dummy.test', 'addon-attendee@dummy.test'], None, 'addon-attendee@dummy.test', 'all'),
])
@scopes_disabled()
def test_sendmail_rule_send_addons(send_to, amount_mails, recipients, ticket_mail, addon_mail, products, order,
event, pos, item, item2):
djmail.outbox = []
order.status = order.STATUS_PAID
order.save()
p = pos
p.attendee_email = ticket_mail
p.save()
order.all_positions.create(item=item2, price=0, attendee_email=addon_mail, addon_to=p)
rule = order.event.sendmail_rules.create(date_is_absolute=True, send_date=dt_now - datetime.timedelta(hours=1),
send_to=send_to, subject='meow', template='meow meow meow',
all_products=products == 'all')
if products == 'addon':
rule.limit_products.set([item2])
if products == 'both':
rule.limit_products.set([item, item2])
sendmail_run_rules(None)
assert len(djmail.outbox) == amount_mails
_recipients = [mail.to[0] for mail in djmail.outbox]
assert set(recipients) == set(_recipients)
@pytest.mark.django_db
@pytest.mark.parametrize('send_to,amount_mails,recipients,ticket_mail,addon_mail, products', [
(Rule.ATTENDEES, 2, ['attendee@dummy.test', 'addon-attendee@dummy.test'], 'attendee@dummy.test',
'addon-attendee@dummy.test', 'addon'),
(Rule.ATTENDEES, 2, ['attendee@dummy.test', 'addon-attendee@dummy.test'], 'attendee@dummy.test',
'addon-attendee@dummy.test', 'both'),
])
@scopes_disabled()
def test_sendmail_rule_send_addons_one_unp(send_to, amount_mails, recipients, ticket_mail, addon_mail, products, order,
event, pos, item, item2):
djmail.outbox = []
order.status = order.STATUS_PAID
order.save()
p = pos
p.attendee_email = ticket_mail
p.save()
order.all_positions.create(item=item2, price=0, attendee_email=addon_mail, addon_to=p)
order.all_positions.create(item=item2, price=0, addon_to=p)
rule = order.event.sendmail_rules.create(date_is_absolute=True, send_date=dt_now - datetime.timedelta(hours=1),
send_to=send_to, subject='meow', template='meow meow meow',
all_products=False)
if products == 'addon':
rule.limit_products.set([item2])
if products == 'both':
rule.limit_products.set([item, item2])
sendmail_run_rules(None)
assert len(djmail.outbox) == amount_mails
_recipients = [mail.to[0] for mail in djmail.outbox]
assert set(recipients) == set(_recipients)
@pytest.mark.django_db
@scopes_disabled()
def test_sendmail_rule_send_attendees_unset_mail(order, event, item):
+306
View File
@@ -406,6 +406,312 @@ def test_sendmail_attendee_product_filter(logged_in_client, sendmail_url, event,
assert '/order/' not in djmail.outbox[0].body
@pytest.mark.django_db
def test_sendmail_attendee_addon_filter(logged_in_client, sendmail_url, event, order, pos):
event.settings.attendee_emails_asked = True
with scopes_disabled():
addon = Item.objects.create(name='Test addon', event=event, default_price=12)
p = pos
p.attendee_email = 'attendee1@dummy.test'
p.save()
order.positions.create(
item=addon, price=0, attendee_email='add-on-attendee@dummy.test', addon_to=p
)
djmail.outbox = []
response = logged_in_client.post(sendmail_url + 'orders/',
{'sendto': 'na',
'action': 'send',
'recipients': 'attendees',
'items': addon.pk,
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
},
follow=True)
assert response.status_code == 200
assert 'alert-success' in response.rendered_content
assert len(djmail.outbox) == 1
assert djmail.outbox[0].to == ['add-on-attendee@dummy.test']
assert '/ticket/' in djmail.outbox[0].body
assert '/order/' not in djmail.outbox[0].body
@pytest.mark.django_db
def test_sendmail_attendee_ticket_and_addon_filter(logged_in_client, sendmail_url, event, order, pos):
event.settings.attendee_emails_asked = True
with scopes_disabled():
addon = Item.objects.create(name='Test addon', event=event, default_price=12)
p = pos
p.attendee_email = 'attendee1@dummy.test'
p.save()
order.positions.create(
item=addon, price=0, attendee_email='add-on-attendee@dummy.test', addon_to=p
)
djmail.outbox = []
response = logged_in_client.post(sendmail_url + 'orders/',
{'sendto': 'na',
'action': 'send',
'recipients': 'attendees',
'items': {addon.pk, p.item_id},
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
},
follow=True)
assert response.status_code == 200
assert 'alert-success' in response.rendered_content
assert len(djmail.outbox) == 2
for msg in djmail.outbox:
assert msg.to in [['attendee1@dummy.test'], ['add-on-attendee@dummy.test']]
assert '/ticket/' in msg.body
assert '/order/' not in msg.body
@pytest.mark.django_db
def test_sendmail_attendee_ticket_and_same_addon_filter(logged_in_client, sendmail_url, event, order, pos):
event.settings.attendee_emails_asked = True
with scopes_disabled():
addon = Item.objects.create(name='Test addon', event=event, default_price=12)
p = pos
p.attendee_email = 'attendee1@dummy.test'
p.save()
order.positions.create(
item=addon, price=0, attendee_email='attendee1@dummy.test', addon_to=p
)
djmail.outbox = []
response = logged_in_client.post(sendmail_url + 'orders/',
{'sendto': 'na',
'action': 'send',
'recipients': 'attendees',
'items': {addon.pk, p.item_id},
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
},
follow=True)
assert response.status_code == 200
assert 'alert-success' in response.rendered_content
assert len(djmail.outbox) == 1
assert djmail.outbox[0].to == ['attendee1@dummy.test']
assert '/ticket/' in djmail.outbox[0].body
assert '/order/' not in djmail.outbox[0].body
@pytest.mark.django_db
def test_sendmail_attendee_addon_unpersonalized_filter(logged_in_client, sendmail_url, event, order, pos):
event.settings.attendee_emails_asked = True
with scopes_disabled():
addon = Item.objects.create(name='Test addon', event=event, default_price=12)
p = pos
p.attendee_email = 'attendee1@dummy.test'
p.save()
order.positions.create(
item=addon, price=0, addon_to=p
)
djmail.outbox = []
response = logged_in_client.post(sendmail_url + 'orders/',
{'sendto': 'na',
'action': 'send',
'recipients': 'attendees',
'items': addon.pk,
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
},
follow=True)
assert response.status_code == 200
assert 'alert-success' in response.rendered_content
assert len(djmail.outbox) == 1
assert djmail.outbox[0].to == ['attendee1@dummy.test']
assert '/ticket/' in djmail.outbox[0].body
assert '/order/' not in djmail.outbox[0].body
@pytest.mark.django_db
def test_sendmail_attendee_ticket_and_addon_unp_filter(logged_in_client, sendmail_url, event, order, pos):
event.settings.attendee_emails_asked = True
with scopes_disabled():
addon = Item.objects.create(name='Test addon', event=event, default_price=12)
p = pos
p.attendee_email = 'attendee1@dummy.test'
p.save()
order.positions.create(
item=addon, price=0, addon_to=p
)
djmail.outbox = []
response = logged_in_client.post(sendmail_url + 'orders/',
{'sendto': 'na',
'action': 'send',
'recipients': 'attendees',
'items': {addon.pk, p.item_id},
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
},
follow=True)
assert response.status_code == 200
assert 'alert-success' in response.rendered_content
assert len(djmail.outbox) == 1
assert djmail.outbox[0].to == ['attendee1@dummy.test']
assert '/ticket/' in djmail.outbox[0].body
assert '/order/' not in djmail.outbox[0].body
@pytest.mark.django_db
def test_sendmail_attendee_ticket_unp_and_addon_filter(logged_in_client, sendmail_url, event, order, pos):
event.settings.attendee_emails_asked = True
with scopes_disabled():
addon = Item.objects.create(name='Test addon', event=event, default_price=12)
order.positions.create(
item=addon, price=0, attendee_email='add-on-attendee@dummy.test', addon_to=pos
)
djmail.outbox = []
response = logged_in_client.post(sendmail_url + 'orders/',
{'sendto': 'na',
'action': 'send',
'recipients': 'attendees',
'items': {addon.pk, pos.item_id},
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
},
follow=True)
assert response.status_code == 200
assert 'alert-success' in response.rendered_content
assert len(djmail.outbox) == 2
for msg in djmail.outbox:
assert msg.to in [[order.email], ['add-on-attendee@dummy.test']]
if msg.to == [order.email]:
assert '/ticket/' not in msg.body
assert '/order/' in msg.body
else:
assert msg.to == ['add-on-attendee@dummy.test']
assert '/ticket/' in msg.body
assert '/order/' not in msg.body
@pytest.mark.django_db
def test_sendmail_attendee_addon_unp_unp_filter(logged_in_client, sendmail_url, event, order, pos):
event.settings.attendee_emails_asked = True
with scopes_disabled():
addon = Item.objects.create(name='Test addon', event=event, default_price=12)
order.positions.create(
item=addon, price=0, addon_to=pos
)
djmail.outbox = []
response = logged_in_client.post(sendmail_url + 'orders/',
{'sendto': 'na',
'action': 'send',
'recipients': 'attendees',
'items': addon.pk,
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
},
follow=True)
assert response.status_code == 200
assert 'alert-success' in response.rendered_content
assert len(djmail.outbox) == 1
assert djmail.outbox[0].to == [order.email]
assert '/ticket/' not in djmail.outbox[0].body
assert '/order/' in djmail.outbox[0].body
@pytest.mark.django_db
def test_sendmail_attendee_and_addon_unp_unp_filter(logged_in_client, sendmail_url, event, order, pos):
event.settings.attendee_emails_asked = True
with scopes_disabled():
addon = Item.objects.create(name='Test addon', event=event, default_price=12)
order.positions.create(
item=addon, price=0, addon_to=pos
)
djmail.outbox = []
response = logged_in_client.post(sendmail_url + 'orders/',
{'sendto': 'na',
'action': 'send',
'recipients': 'attendees',
'items': {addon.pk, pos.item_id},
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
},
follow=True)
assert response.status_code == 200
assert 'alert-success' in response.rendered_content
assert len(djmail.outbox) == 1
assert djmail.outbox[0].to == [order.email]
assert '/ticket/' not in djmail.outbox[0].body
assert '/order/' in djmail.outbox[0].body
@pytest.mark.django_db
def test_sendmail_attendee_two_addons_one_unp_filter(logged_in_client, sendmail_url, event, order, pos):
event.settings.attendee_emails_asked = True
with scopes_disabled():
p = pos
p.attendee_email = 'attendee1@dummy.test'
p.save()
addon = Item.objects.create(name='Test addon', event=event, default_price=12)
order.positions.create(
item=addon, price=0, attendee_email='add-on-attendee@dummy.test', addon_to=p
)
order.positions.create(
item=addon, price=0, addon_to=p
)
djmail.outbox = []
response = logged_in_client.post(sendmail_url + 'orders/',
{'sendto': 'na',
'action': 'send',
'recipients': 'attendees',
'items': addon.pk,
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
},
follow=True)
assert response.status_code == 200
assert 'alert-success' in response.rendered_content
assert len(djmail.outbox) == 2
for msg in djmail.outbox:
assert msg.to in [['attendee1@dummy.test'], ['add-on-attendee@dummy.test']]
assert '/ticket/' in msg.body
assert '/order/' not in msg.body
@pytest.mark.django_db
def test_sendmail_attendee_and_two_addons_one_unp_filter(logged_in_client, sendmail_url, event, order, pos):
event.settings.attendee_emails_asked = True
with scopes_disabled():
p = pos
p.attendee_email = 'attendee1@dummy.test'
p.save()
addon = Item.objects.create(name='Test addon', event=event, default_price=12)
order.positions.create(
item=addon, price=0, attendee_email='add-on-attendee@dummy.test', addon_to=p
)
order.positions.create(
item=addon, price=0, addon_to=p
)
djmail.outbox = []
response = logged_in_client.post(sendmail_url + 'orders/',
{'sendto': 'na',
'action': 'send',
'recipients': 'attendees',
'items': {addon.pk, p.item_id},
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
},
follow=True)
assert response.status_code == 200
assert 'alert-success' in response.rendered_content
assert len(djmail.outbox) == 2
for msg in djmail.outbox:
assert msg.to in [['attendee1@dummy.test'], ['add-on-attendee@dummy.test']]
assert '/ticket/' in msg.body
assert '/order/' not in msg.body
@pytest.mark.django_db
def test_sendmail_attendee_subevent_filter(logged_in_client, sendmail_url, event, item, order, pos):
event.settings.attendee_emails_asked = True