diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py index 786f3ffc9..fd8e07511 100644 --- a/src/pretix/api/serializers/event.py +++ b/src/pretix/api/serializers/event.py @@ -580,6 +580,7 @@ class EventSettingsSerializer(SettingsSerializer): 'locale', 'region', 'last_order_modification_date', + 'allow_modifications_after_checkin', 'show_quota_left', 'waiting_list_enabled', 'waiting_list_hours', diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index 1cfd22765..7af9b5d1b 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -666,6 +666,8 @@ class Order(LockModel, LoggedModel): related to the order. This checks order status and modification deadlines. It also returns ``False`` if there are no questions that can be answered. """ + from .checkin import Checkin + if self.status not in (Order.STATUS_PENDING, Order.STATUS_PAID, Order.STATUS_EXPIRED): return False @@ -681,10 +683,21 @@ class Order(LockModel, LoggedModel): if modify_deadline is not None and now() > modify_deadline: return False + + positions = list( + self.positions.all().annotate( + has_checkin=Exists(Checkin.objects.filter(position_id=OuterRef('pk'))) + ).select_related('item').prefetch_related('item__questions') + ) + if not self.event.settings.allow_modifications_after_checkin: + for cp in positions: + if cp.has_checkin: + return False + if self.event.settings.get('invoice_address_asked', as_type=bool): return True ask_names = self.event.settings.get('attendee_names_asked', as_type=bool) - for cp in self.positions.all().prefetch_related('item__questions'): + for cp in positions: if (cp.item.admission and ask_names) or cp.item.questions.all(): return True diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index 525b424ac..78cb8e5f6 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -1082,6 +1082,15 @@ DEFAULTS = { help_text=_('If your event series has more than 50 dates in the future, only the month or week calendar can be used.') ), }, + 'allow_modifications_after_checkin': { + 'default': 'False', + 'type': bool, + 'form_class': forms.BooleanField, + 'serializer_class': serializers.BooleanField, + 'form_kwargs': dict( + label=_("Allow customers to modify their information after they checked in."), + ) + }, 'last_order_modification_date': { 'default': None, 'type': RelativeDateWrapper, diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py index 64e9b209c..44f294afa 100644 --- a/src/pretix/control/forms/event.py +++ b/src/pretix/control/forms/event.py @@ -457,6 +457,7 @@ class EventSettingsForm(SettingsForm): 'banner_text_bottom', 'order_email_asked_twice', 'last_order_modification_date', + 'allow_modifications_after_checkin', 'checkout_show_copy_answers_button', 'primary_color', 'theme_color_success', diff --git a/src/pretix/control/templates/pretixcontrol/event/settings.html b/src/pretix/control/templates/pretixcontrol/event/settings.html index 535b06588..e5bba833d 100644 --- a/src/pretix/control/templates/pretixcontrol/event/settings.html +++ b/src/pretix/control/templates/pretixcontrol/event/settings.html @@ -243,6 +243,7 @@ {% bootstrap_field form.presale_end layout="control" %} {% bootstrap_field sform.show_items_outside_presale_period layout="control" %} {% bootstrap_field sform.last_order_modification_date layout="control" %} + {% bootstrap_field sform.allow_modifications_after_checkin layout="control" %}
{% trans "Display" %}