Compare commits

..

3 Commits

Author SHA1 Message Date
Mira Weller
9099a38bc8 Use shorter OrderPosition.code 2024-09-24 12:43:48 +02:00
Mira Weller
7cb66879ba Add inline "json_script" as supported data source for select2 2024-09-23 18:47:09 +02:00
Mira Weller
29808ad92d Add full_code property to OrderPosition 2024-09-23 18:47:09 +02:00
65 changed files with 33924 additions and 37331 deletions

View File

@@ -19,4 +19,4 @@
# 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/>.
#
__version__ = "2024.10.0.dev0"
__version__ = "2024.9.0.dev0"

View File

@@ -50,7 +50,6 @@ class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('--tasks', action='store', type=str, help='Only execute the tasks with this name '
'(dotted path, comma separation)')
parser.add_argument('--list-tasks', action='store_true', help='Only list all tasks')
parser.add_argument('--exclude', action='store', type=str, help='Exclude the tasks with this name '
'(dotted path, comma separation)')
@@ -62,9 +61,6 @@ class Command(BaseCommand):
for receiver in periodic_task._live_receivers(self):
name = f'{receiver.__module__}.{receiver.__name__}'
if options['list_tasks']:
print(name)
continue
if options.get('tasks'):
if name not in options.get('tasks').split(','):
continue

View File

@@ -313,9 +313,38 @@ class EventMixin:
items=GroupConcat('pk', delimiter=',')
).values('items')
sq_active_variation = ItemVariation.objects.filter_available(channel=channel, voucher=voucher).filter(
Q(quotas__pk=OuterRef('pk'))
).order_by().values_list('quotas__pk').annotate(
q_variation = (
Q(active=True)
& Q(Q(available_from__isnull=True) | Q(available_from__lte=time_machine_now()))
& Q(Q(available_until__isnull=True) | Q(available_until__gte=time_machine_now()))
& Q(item__active=True)
& Q(Q(item__available_from__isnull=True) | Q(item__available_from__lte=time_machine_now()))
& Q(Q(item__available_until__isnull=True) | Q(item__available_until__gte=time_machine_now()))
& Q(Q(item__category__isnull=True) | Q(item__category__is_addon=False))
& Q(item__require_bundling=False)
& Q(quotas__pk=OuterRef('pk'))
)
if isinstance(channel, str):
q_variation &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel))
q_variation &= Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels__identifier=channel))
else:
q_variation &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels=channel))
q_variation &= Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels=channel))
if voucher:
if voucher.variation_id:
q_variation &= Q(pk=voucher.variation_id)
elif voucher.item_id:
q_variation &= Q(item_id=voucher.item_id)
elif voucher.quota_id:
q_variation &= Q(quotas__in=[voucher.quota_id])
if not voucher or not voucher.show_hidden_items:
q_variation &= Q(hide_without_voucher=False)
q_variation &= Q(item__hide_without_voucher=False)
sq_active_variation = ItemVariation.objects.filter(q_variation).order_by().values_list('quotas__pk').annotate(
items=GroupConcat('pk', delimiter=',')
).values('items')
quota_base_qs = Quota.objects.using(settings.DATABASE_REPLICA).filter(

View File

@@ -303,48 +303,6 @@ def filter_available(qs, channel='web', voucher=None, allow_addons=False):
return qs.filter(q)
def filter_variations_available(qs, channel='web', voucher=None, allow_addons=False):
# Channel can currently be a SalesChannel or a str, since we need that compatibility, but a SalesChannel
# makes the query SIGNIFICANTLY faster
from .organizer import SalesChannel
assert isinstance(channel, (SalesChannel, str))
q = (
Q(active=True)
& Q(Q(available_from__isnull=True) | Q(available_from__lte=time_machine_now()))
& Q(Q(available_until__isnull=True) | Q(available_until__gte=time_machine_now()))
& Q(item__active=True)
& Q(Q(item__available_from__isnull=True) | Q(item__available_from__lte=time_machine_now()))
& Q(Q(item__available_until__isnull=True) | Q(item__available_until__gte=time_machine_now()))
& Q(Q(item__category__isnull=True) | Q(item__category__is_addon=False))
& Q(item__require_bundling=False)
)
if isinstance(channel, str):
q &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=channel))
q &= Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels__identifier=channel))
else:
q &= Q(Q(all_sales_channels=True) | Q(limit_sales_channels=channel))
q &= Q(Q(item__all_sales_channels=True) | Q(item__limit_sales_channels=channel))
if not allow_addons:
q &= Q(Q(item__category__isnull=True) | Q(item__category__is_addon=False))
if voucher:
if voucher.variation_id:
q &= Q(pk=voucher.variation_id)
elif voucher.item_id:
q &= Q(item_id=voucher.item_id)
elif voucher.quota_id:
q &= Q(quotas__in=[voucher.quota_id])
if not voucher or not voucher.show_hidden_items:
q &= Q(hide_without_voucher=False)
q &= Q(item__hide_without_voucher=False)
return qs.filter(q)
class ItemQuerySet(models.QuerySet):
def filter_available(self, channel='web', voucher=None, allow_addons=False):
return filter_available(self, channel, voucher, allow_addons)
@@ -359,20 +317,6 @@ class ItemQuerySetManager(ScopedManager(organizer='event__organizer').__class__)
return filter_available(self.get_queryset(), channel, voucher, allow_addons)
class ItemVariationQuerySet(models.QuerySet):
def filter_available(self, channel='web', voucher=None, allow_addons=False):
return filter_variations_available(self, channel, voucher, allow_addons)
class ItemVariationQuerySetManager(ScopedManager(organizer='item__event__organizer').__class__):
def __init__(self):
super().__init__()
self._queryset_class = ItemVariationQuerySet
def filter_available(self, channel='web', voucher=None, allow_addons=False):
return filter_variations_available(self.get_queryset(), channel, voucher, allow_addons)
class Item(LoggedModel):
"""
An item is a thing which can be sold. It belongs to an event and may or may not belong to a category.
@@ -1255,7 +1199,7 @@ class ItemVariation(models.Model):
help_text=_('This text will be shown by the check-in app if a ticket of this type is scanned.')
)
objects = ItemVariationQuerySetManager()
objects = ScopedManager(organizer='item__event__organizer')
class Meta:
verbose_name = _("Product variation")

View File

@@ -1182,11 +1182,10 @@ def process_exit_all(sender, **kwargs):
positions = cl.positions_inside_query(ignore_status=True, at_time=cl.exit_all_at)
for p in positions:
with scope(organizer=cl.event.organizer):
ci, created = Checkin.objects.get_or_create(
ci = Checkin.objects.create(
position=p, list=cl, auto_checked_in=True, type=Checkin.TYPE_EXIT, datetime=cl.exit_all_at
)
if created:
checkin_created.send(cl.event, checkin=ci)
checkin_created.send(cl.event, checkin=ci)
d = cl.exit_all_at.astimezone(cl.event.timezone)
if cl.event.settings.get(f'autocheckin_dst_hack_{cl.pk}'): # move time back if yesterday was DST switch
d -= timedelta(hours=1)

View File

@@ -41,7 +41,7 @@
{% bootstrap_field form.eu_reverse_charge layout="control" %}
{% bootstrap_field form.home_country layout="control" %}
{% bootstrap_field form.keep_gross_if_rate_changes layout="control" %}
<h3>{% trans "Custom rules" %}</h3>
<h3>{% trans "Custom taxation rules" %}</h3>
<div class="alert alert-warning">
{% blocktrans trimmed %}
These settings are intended for professional users with very specific taxation situations.

View File

@@ -133,7 +133,7 @@
{% endif %}
{{ i.default_price|money:request.event.currency }}
{% if i.original_price %}<strike class="text-muted">{{ i.original_price|money:request.event.currency }}</strike>{% endif %}
{% if i.tax_rule %}
{% if i.tax_rule and i.default_price %}
<br/>
<small class="text-muted">
{% if not i.tax_rule.price_includes_tax %}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,6 @@ AES
Absenderadresse
Absenderinformation
Absendername
ABGEKÜNDIGT
Admin
Adminbereich
Affirm
@@ -26,7 +25,6 @@ Ausgangsscans
ausgeklappt
ausgecheckt
auswahl
Auth
Authentication
Authenticator
Authentifizierungsmechanismus
@@ -38,7 +36,6 @@ Bancontact
BankID
Banking
barcodes
Baskisch
Bcc
BCC
Beispielevent

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,6 @@ AES
Absenderadresse
Absenderinformation
Absendername
ABGEKÜNDIGT
Admin
Adminbereich
Affirm
@@ -26,7 +25,6 @@ Ausgangsscans
ausgeklappt
ausgecheckt
auswahl
Auth
Authentication
Authenticator
Authentifizierungsmechanismus
@@ -38,7 +36,6 @@ Bancontact
BankID
Banking
barcodes
Baskisch
Bcc
BCC
Beispielevent

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-09-26 11:23+0000\n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-09-26 11:23+0000\n"
"POT-Creation-Date: 2024-08-27 13:34+0000\n"
"PO-Revision-Date: 2024-09-06 08:47+0000\n"
"Last-Translator: Albizuri <oier@puntu.eus>\n"
"Language-Team: Basque <https://translate.pretix.eu/projects/pretix/pretix-js/"
@@ -557,14 +557,17 @@ msgid "absent"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:171
#: pretix/static/pretixcontrol/js/ui/editor.js:72
msgid "Check-in QR"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:543
#: pretix/static/pretixcontrol/js/ui/editor.js:387
msgid "The PDF background file could not be loaded for the following reason:"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:894
#: pretix/static/pretixcontrol/js/ui/editor.js:656
msgid "Group of objects"
msgstr ""
@@ -577,35 +580,44 @@ msgid "Text box"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:903
#: pretix/static/pretixcontrol/js/ui/editor.js:663
msgid "Barcode area"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:905
#: pretix/static/pretixcontrol/js/ui/editor.js:665
msgid "Image area"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:907
#: pretix/static/pretixcontrol/js/ui/editor.js:667
msgid "Powered by pretix"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:909
#: pretix/static/pretixcontrol/js/ui/editor.js:669
msgid "Object"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:913
#: pretix/static/pretixcontrol/js/ui/editor.js:673
msgid "Ticket design"
msgstr "Sarrera diseinua"
#: pretix/static/pretixcontrol/js/ui/editor.js:1250
#: pretix/static/pretixcontrol/js/ui/editor.js:972
msgid "Saving failed."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1319
#: pretix/static/pretixcontrol/js/ui/editor.js:1370
#: pretix/static/pretixcontrol/js/ui/editor.js:1041
#: pretix/static/pretixcontrol/js/ui/editor.js:1091
msgid "Error while uploading your PDF file, please try again."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:1353
#: pretix/static/pretixcontrol/js/ui/editor.js:1074
msgid "Do you really want to leave the editor without saving your changes?"
msgstr ""
@@ -622,14 +634,17 @@ msgid "Unknown error."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:318
#: pretix/static/pretixcontrol/js/ui/main.js:313
msgid "Your color has great contrast and is very easy to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:322
#: pretix/static/pretixcontrol/js/ui/main.js:317
msgid "Your color has decent contrast and is probably good-enough to read!"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:326
#: pretix/static/pretixcontrol/js/ui/main.js:321
msgid ""
"Your color has bad contrast for text on white background, please choose a "
"darker shade."
@@ -637,38 +652,48 @@ msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:491
#: pretix/static/pretixcontrol/js/ui/main.js:511
#: pretix/static/pretixcontrol/js/ui/main.js:475
#: pretix/static/pretixcontrol/js/ui/main.js:495
msgid "Search query"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:509
#: pretix/static/pretixcontrol/js/ui/main.js:493
msgid "All"
msgstr "Guztiak"
#: pretix/static/pretixcontrol/js/ui/main.js:510
#: pretix/static/pretixcontrol/js/ui/main.js:494
msgid "None"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:514
#: pretix/static/pretixcontrol/js/ui/main.js:498
msgid "Selected only"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:847
#: pretix/static/pretixcontrol/js/ui/main.js:828
msgid "Enter page number between 1 and %(max)s."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:850
#: pretix/static/pretixcontrol/js/ui/main.js:831
msgid "Invalid page number."
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1008
#: pretix/static/pretixcontrol/js/ui/main.js:989
msgid "Use a different name internally"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1048
#: pretix/static/pretixcontrol/js/ui/main.js:1029
msgid "Click to close"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/main.js:1123
#: pretix/static/pretixcontrol/js/ui/main.js:1104
msgid "You have unsaved changes!"
msgstr ""
@@ -706,8 +731,7 @@ msgstr "Saskia iraungita"
msgid "The items in your cart are reserved for you for one minute."
msgid_plural "The items in your cart are reserved for you for {num} minutes."
msgstr[0] "Zure saskiko produktuak minutu -ez erreserbatuta daude zuretzat."
msgstr[1] ""
"Zure saskiko produktuak {num} minutuz erreserbatuta daude zuretzat."
msgstr[1] "Zure saskiko produktuak {num} minutuz erreserbatuta daude zuretzat."
#: pretix/static/pretixpresale/js/ui/main.js:203
msgid "The organizer keeps %(currency)s %(amount)s"
@@ -1075,3 +1099,7 @@ msgstr ""
#: pretix/static/pretixpresale/js/widget/widget.js:89
msgid "December"
msgstr ""
#: pretix/static/pretixcontrol/js/ui/editor.js:661
msgid "Text object"
msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,6 @@ acceptor
analytics
anonymize
anonymized
Auth
authenticator
automatical
availabilities

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -31,8 +31,6 @@
# 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.
from datetime import datetime
from django.db.models import Exists, OuterRef, Q
from i18nfield.strings import LazyI18nString
@@ -47,7 +45,6 @@ from pretix.helpers.format import format_map
@app.task(base=ProfiledEventTask, acks_late=True)
def send_mails_to_orders(event: Event, user: int, subject: dict, message: dict, objects: list, items: list,
subevent: int, subevents_from: datetime, subevents_to: datetime,
recipients: str, filter_checkins: bool, not_checked_in: bool, checkin_lists: list,
attachments: list = None, attach_tickets: bool = False,
attach_ical: bool = False) -> None:
@@ -79,7 +76,7 @@ def send_mails_to_orders(event: Event, user: int, subject: dict, message: dict,
list_id__in=checkin_lists or []
)
),
).prefetch_related('addons', 'subevent'):
).prefetch_related('addons'):
if p.addon_to_id is not None:
continue
@@ -102,15 +99,6 @@ def send_mails_to_orders(event: Event, user: int, subject: dict, message: dict,
if p.attendee_email == o.email and send_to_order:
continue
if subevent and p.subevent_id != subevent:
continue
if subevents_from and p.subevent.date_from < subevents_from:
continue
if subevents_to and p.subevent.date_from >= subevents_to:
continue
try:
with language(o.locale, event.settings.region):
email_context = get_email_context(event=event, order=o, invoice_address=ia, position=p)

View File

@@ -429,9 +429,6 @@ class OrderSendView(BaseSenderView):
kwargs.update({
'recipients': form.cleaned_data['recipients'],
'items': [i.pk for i in form.cleaned_data.get('items')],
'subevent': form.cleaned_data['subevent'].pk if form.cleaned_data.get('subevent') else None,
'subevents_from': form.cleaned_data.get('subevents_from'),
'subevents_to': form.cleaned_data.get('subevents_to'),
'not_checked_in': form.cleaned_data.get('not_checked_in'),
'checkin_lists': [i.pk for i in form.cleaned_data.get('checkin_lists')],
'filter_checkins': form.cleaned_data.get('filter_checkins'),

View File

@@ -56,7 +56,7 @@ from django.views.generic.base import TemplateResponseMixin
from django_scopes import scopes_disabled
from pretix.base.models import Customer, Membership, Order
from pretix.base.models.items import ItemAddOn, ItemVariation, Question
from pretix.base.models.items import Question
from pretix.base.models.orders import (
InvoiceAddress, OrderPayment, QuestionAnswer,
)
@@ -486,33 +486,9 @@ class AddOnsStep(CartMixin, AsyncAction, TemplateFlowStep):
label = pgettext_lazy('checkoutflow', 'Add-on products')
icon = 'puzzle-piece'
def _is_applicable(self, request):
categories = set(ItemAddOn.objects.filter(
base_item_id__in=get_cart(request).values_list("item_id", flat=True)
).values_list("addon_category_id", flat=True))
if not categories:
return False
has_available_addons = (
self.event.items.filter_available(
channel=request.sales_channel,
allow_addons=True
).filter(
variations__isnull=True,
category__in=categories,
).exists() or ItemVariation.objects.filter_available(
channel=request.sales_channel,
allow_addons=True
).filter(
item__event=self.event,
item__category__in=categories,
)
)
return has_available_addons
def is_applicable(self, request):
if not hasattr(request, '_checkoutflow_addons_applicable'):
request._checkoutflow_addons_applicable = self._is_applicable(request)
request._checkoutflow_addons_applicable = get_cart(request).filter(item__addons__isnull=False).exists()
return request._checkoutflow_addons_applicable
def is_completed(self, request, warn=False):

View File

@@ -263,8 +263,6 @@ def get_grouped_items(event, *, channel: SalesChannel, subevent=None, voucher=No
quotas_to_compute = []
for item in items:
assert item.event_id == event.pk
item.event = event # save a database query if this is looked up
if item.has_variations:
for v in item.available_variations:
for q in v._subevent_quotas:

View File

@@ -406,79 +406,6 @@ 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_subevent_filter(logged_in_client, sendmail_url, event, item, order, pos):
event.settings.attendee_emails_asked = True
event.has_subevents = True
event.save()
with scopes_disabled():
se1 = event.subevents.create(name='Subevent FOO', date_from=now())
se2 = event.subevents.create(name='Bar', date_from=now())
pos.attendee_email = 'attendee1@dummy.test'
pos.subevent = se1
pos.save()
with scopes_disabled():
order.positions.create(
item=item, price=0, attendee_email='attendee2@dummy.test', subevent=se2
)
djmail.outbox = []
response = logged_in_client.post(sendmail_url + 'orders/',
{'sendto': 'na',
'action': 'send',
'recipients': 'attendees',
'items': item.pk,
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
'subevent': se2.pk,
},
follow=True)
assert response.status_code == 200
assert 'alert-success' in response.rendered_content
assert len(djmail.outbox) == 1
assert djmail.outbox[0].to == ['attendee2@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_subevent_range_filter(logged_in_client, sendmail_url, event, item, order, pos):
event.settings.attendee_emails_asked = True
event.has_subevents = True
event.save()
with scopes_disabled():
se1 = event.subevents.create(name='Subevent FOO', date_from=datetime.datetime(2023, 7, 6, 1, 2, 3, tzinfo=event.timezone))
se2 = event.subevents.create(name='Bar', date_from=datetime.datetime(2023, 8, 9, 1, 2, 3, tzinfo=event.timezone))
pos.attendee_email = 'attendee1@dummy.test'
pos.subevent = se1
pos.save()
with scopes_disabled():
order.positions.create(
item=item, price=0, attendee_email='attendee2@dummy.test', subevent=se2
)
djmail.outbox = []
response = logged_in_client.post(sendmail_url + 'orders/',
{'sendto': 'na',
'action': 'send',
'recipients': 'attendees',
'items': item.pk,
'subject_0': 'Test subject',
'message_0': 'This is a test file for sending mails.',
'subevents_from_0': '2023-07-01',
'subevents_from_1': '00:00:00',
'subevents_to_0': '2023-08-01',
'subevents_to_1': '00:00:00',
},
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_checkin_filter(logged_in_client, sendmail_url, event, order, checkin_list, item, pos):
event.settings.attendee_emails_asked = True