diff --git a/src/pretix/control/logdisplay.py b/src/pretix/control/logdisplay.py
index 66818f54e5..3d87cd7bac 100644
--- a/src/pretix/control/logdisplay.py
+++ b/src/pretix/control/logdisplay.py
@@ -1,8 +1,11 @@
import json
from decimal import Decimal
+import dateutil.parser
+import pytz
from django.dispatch import receiver
from django.utils import formats
+from django.utils.formats import date_format
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
from i18nfield.strings import LazyI18nString
@@ -200,6 +203,21 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
if logentry.action_type.startswith('pretix.event.tickets.provider.'):
return _('The settings of a ticket output provider have been changed.')
+ if logentry.action_type == 'pretix.control.views.checkin':
+ dt = dateutil.parser.parse(data.get('datetime'))
+ tz = pytz.timezone(sender.settings.timezone)
+ dt_formatted = date_format(dt.astimezone(tz), "SHORT_DATETIME_FORMAT")
+
+ if data.get('first'):
+ return _('Position #{posid} has been checked in manually at {datetime}.').format(
+ posid=data.get('positionid'),
+ datetime=dt_formatted
+ )
+ return _('Position #{posid} has been checked in again at {datetime}.').format(
+ posid=data.get('positionid'),
+ datetime=dt_formatted
+ )
+
if logentry.action_type == 'pretix.team.member.added':
return _('{user} has been added to the team.').format(user=data.get('email'))
diff --git a/src/pretix/control/templates/pretixcontrol/checkin/index.html b/src/pretix/control/templates/pretixcontrol/checkin/index.html
index 087420bf17..b8f07ca2cd 100644
--- a/src/pretix/control/templates/pretixcontrol/checkin/index.html
+++ b/src/pretix/control/templates/pretixcontrol/checkin/index.html
@@ -52,6 +52,7 @@
+
+ {% include "pretixcontrol/pagination.html" %}
{% endif %}
{% endblock %}
diff --git a/src/pretix/control/views/checkin.py b/src/pretix/control/views/checkin.py
index 64dfda5701..60fd65befa 100644
--- a/src/pretix/control/views/checkin.py
+++ b/src/pretix/control/views/checkin.py
@@ -1,8 +1,13 @@
+from django.contrib import messages
+from django.core.urlresolvers import reverse
from django.db.models import F, Prefetch, Q
from django.db.models.functions import Coalesce
+from django.shortcuts import redirect
+from django.utils.timezone import now
+from django.utils.translation import ugettext_lazy as _
from django.views.generic import ListView
-from pretix.base.models import Checkin, Item, OrderPosition
+from pretix.base.models import Checkin, Item, Order, OrderPosition
from pretix.control.permissions import EventPermissionRequiredMixin
@@ -69,6 +74,31 @@ class CheckInView(EventPermissionRequiredMixin, ListView):
or "subevent" in self.request.GET)
return ctx
+ def post(self, request, *args, **kwargs):
+ positions = OrderPosition.objects.select_related('item', 'variation', 'order', 'addon_to').filter(
+ order__event=self.request.event,
+ pk__in=request.POST.getlist('checkin')
+ )
+
+ for op in positions:
+ created = False
+ if op.order.status == Order.STATUS_PAID:
+ ci, created = Checkin.objects.get_or_create(position=op, defaults={
+ 'datetime': now(),
+ })
+ op.order.log_action('pretix.control.views.checkin', data={
+ 'position': op.id,
+ 'positionid': op.positionid,
+ 'first': created,
+ 'datetime': now()
+ }, user=request.user)
+
+ messages.success(request, _('The selected tickets have been marked as checked in.'))
+ return redirect(reverse('control:event.orders.checkins', kwargs={
+ 'event': self.request.event.slug,
+ 'organizer': self.request.event.organizer.slug
+ }) + '?' + request.GET.urlencode())
+
@staticmethod
def get_ordering_keys_mappings():
return {
diff --git a/src/tests/control/test_checkins.py b/src/tests/control/test_checkins.py
index 76119e2fc4..d693ccc34c 100644
--- a/src/tests/control/test_checkins.py
+++ b/src/tests/control/test_checkins.py
@@ -5,8 +5,8 @@ import pytest
from django.utils.timezone import now
from pretix.base.models import (
- Checkin, Event, Item, ItemAddOn, ItemCategory, Order, OrderPosition,
- Organizer, Team, User,
+ Checkin, Event, Item, ItemAddOn, ItemCategory, LogEntry, Order,
+ OrderPosition, Organizer, Team, User,
)
from pretix.control.views.dashboards import checkin_widget
@@ -269,6 +269,19 @@ def test_checkins_list_mixed(client, checkin_list_env, query, expected):
assert item_keys == expected
+@pytest.mark.django_db
+def test_manual_checkins(client, checkin_list_env):
+ client.login(email='dummy@dummy.dummy', password='dummy')
+ assert not checkin_list_env[5][3].checkins.exists()
+ client.post('/control/event/dummy/dummy/checkins/', {
+ 'checkin': [checkin_list_env[5][3].pk]
+ })
+ assert checkin_list_env[5][3].checkins.exists()
+ assert LogEntry.objects.filter(
+ action_type='pretix.control.views.checkin', object_id=checkin_list_env[5][3].order.pk
+ ).exists()
+
+
@pytest.fixture
def checkin_list_with_addon_env():
# permission