diff --git a/src/pretix/control/templates/pretixcontrol/checkin/bulk_revert_confirm.html b/src/pretix/control/templates/pretixcontrol/checkin/bulk_revert_confirm.html
new file mode 100644
index 0000000000..475fbc2201
--- /dev/null
+++ b/src/pretix/control/templates/pretixcontrol/checkin/bulk_revert_confirm.html
@@ -0,0 +1,31 @@
+{% extends "pretixcontrol/items/base.html" %}
+{% load i18n %}
+{% load bootstrap3 %}
+{% block title %}{% trans "Delete check-ins" %}{% endblock %}
+{% block inside %}
+
{% trans "Delete check-ins" %}
+
+{% endblock %}
diff --git a/src/pretix/control/templates/pretixcontrol/checkin/index.html b/src/pretix/control/templates/pretixcontrol/checkin/index.html
index 28fc1d6500..ef812a93a2 100644
--- a/src/pretix/control/templates/pretixcontrol/checkin/index.html
+++ b/src/pretix/control/templates/pretixcontrol/checkin/index.html
@@ -205,22 +205,27 @@
- {% if "can_change_orders" in request.eventpermset or "can_checkin_orders" in request.eventpermset %}
-
-
- {% trans "Check-In selected attendees" %}
-
-
-
- {% trans "Check-Out selected attendees" %}
-
- {% endif %}
- {% if "can_change_orders" in request.eventpermset %}
-
-
- {% trans "Delete all check-ins of selected attendees" %}
-
- {% endif %}
+
+ {% if "can_change_orders" in request.eventpermset or "can_checkin_orders" in request.eventpermset %}
+
+
+ {% trans "Check-In selected attendees" %}
+
+
+
+ {% trans "Check-Out selected attendees" %}
+
+ {% endif %}
+ {% if "can_change_orders" in request.eventpermset %}
+
+
+ {% trans "Delete all check-ins of selected attendees" %}
+
+ {% endif %}
+
{% include "pretixcontrol/pagination.html" %}
{% endif %}
diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py
index b0dbfc9137..bef3a19c7e 100644
--- a/src/pretix/control/urls.py
+++ b/src/pretix/control/urls.py
@@ -438,6 +438,7 @@ urlpatterns = [
re_path(r'^checkinlists/select2$', typeahead.checkinlist_select2, name='event.orders.checkinlists.select2'),
re_path(r'^checkinlists/(?P\d+)/$', checkin.CheckInListShow.as_view(), name='event.orders.checkinlists.show'),
re_path(r'^checkinlists/(?P\d+)/simulator$', checkin.CheckInListSimulator.as_view(), name='event.orders.checkinlists.simulator'),
+ re_path(r'^checkinlists/(?P\d+)/bulk_revert$', checkin.CheckInListBulkRevertConfirmView.as_view(), name='event.orders.checkinlists.bulk_revert'),
re_path(r'^checkinlists/(?P\d+)/bulk_action$', checkin.CheckInListBulkActionView.as_view(), name='event.orders.checkinlists.bulk_action'),
re_path(r'^checkinlists/(?P\d+)/change$', checkin.CheckinListUpdate.as_view(),
name='event.orders.checkinlists.edit'),
diff --git a/src/pretix/control/views/checkin.py b/src/pretix/control/views/checkin.py
index 22b30512b5..b2dd89e6cf 100644
--- a/src/pretix/control/views/checkin.py
+++ b/src/pretix/control/views/checkin.py
@@ -45,7 +45,7 @@ from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.timezone import is_aware, make_aware, now
from django.utils.translation import gettext_lazy as _
-from django.views.generic import FormView, ListView
+from django.views.generic import FormView, ListView, TemplateView
from i18nfield.strings import LazyI18nString
from pretix.api.views.checkin import _redeem_process
@@ -191,9 +191,23 @@ class CheckInListShow(EventPermissionRequiredMixin, PaginationMixin, CheckInList
return ctx
+class CheckInListBulkRevertConfirmView(CheckInListQueryMixin, EventPermissionRequiredMixin, TemplateView):
+ template_name = "pretixcontrol/checkin/bulk_revert_confirm.html"
+
+ def post(self, request, *args, **kwargs):
+ self.list = get_object_or_404(self.request.event.checkin_lists.all(), pk=kwargs.get("list"))
+ return super().get(request, *args, **kwargs)
+
+ def get_context_data(self, **kwargs):
+ return super().get_context_data(
+ **kwargs,
+ cnt=self.get_queryset().count(),
+ checkinlist=self.list,
+ )
+
+
class CheckInListBulkActionView(CheckInListQueryMixin, EventPermissionRequiredMixin, AsyncPostView):
permission = ('can_change_orders', 'can_checkin_orders')
- context_object_name = 'device'
def dispatch(self, request, *args, **kwargs):
self.list = get_object_or_404(self.request.event.checkin_lists.all(), pk=kwargs.get("list"))
@@ -216,14 +230,15 @@ class CheckInListBulkActionView(CheckInListQueryMixin, EventPermissionRequiredMi
if op.order.status == Order.STATUS_PAID or (
(self.list.include_pending or op.order.valid_if_pending) and op.order.status == Order.STATUS_PENDING
):
- Checkin.objects.filter(position=op, list=self.list).delete()
- op.order.log_action('pretix.event.checkin.reverted', data={
- 'position': op.id,
- 'positionid': op.positionid,
- 'list': self.list.pk,
- 'web': True
- }, user=request.user)
- op.order.touch()
+ _, deleted = Checkin.objects.filter(position=op, list=self.list).delete()
+ if deleted:
+ op.order.log_action('pretix.event.checkin.reverted', data={
+ 'position': op.id,
+ 'positionid': op.positionid,
+ 'list': self.list.pk,
+ 'web': True
+ }, user=request.user)
+ op.order.touch()
return 'reverted', request.POST.get('returnquery')
else: