diff --git a/src/pretix/api/views/checkin.py b/src/pretix/api/views/checkin.py index 59efe19f26..bf32a63409 100644 --- a/src/pretix/api/views/checkin.py +++ b/src/pretix/api/views/checkin.py @@ -157,6 +157,7 @@ class CheckinListViewSet(viewsets.ModelViewSet): list=self.get_object(), successful=False, forced=True, + force_sent=True, device=self.request.auth if isinstance(self.request.auth, Device) else None, gate=self.request.auth.gate if isinstance(self.request.auth, Device) else None, **kwargs, diff --git a/src/pretix/base/migrations/0216_checkin_forced_sent.py b/src/pretix/base/migrations/0216_checkin_forced_sent.py new file mode 100644 index 0000000000..0b436e8235 --- /dev/null +++ b/src/pretix/base/migrations/0216_checkin_forced_sent.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2022-04-29 13:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0215_customer_organizer_identifier_unique'), + ] + + operations = [ + migrations.AddField( + model_name='checkin', + name='force_sent', + field=models.BooleanField(default=False, null=True), + ), + ] diff --git a/src/pretix/base/models/checkin.py b/src/pretix/base/models/checkin.py index 8affd3fbb6..00b8238868 100644 --- a/src/pretix/base/models/checkin.py +++ b/src/pretix/base/models/checkin.py @@ -326,7 +326,13 @@ class Checkin(models.Model): type = models.CharField(max_length=100, choices=CHECKIN_TYPES, default=TYPE_ENTRY) nonce = models.CharField(max_length=190, null=True, blank=True) + + # Whether or not the scan was made offline + force_sent = models.BooleanField(default=False, null=True, blank=True) + + # Whether the scan was made offline AND would have not been possible online forced = models.BooleanField(default=False) + device = models.ForeignKey( 'pretixbase.Device', related_name='checkins', on_delete=models.PROTECT, null=True, blank=True ) diff --git a/src/pretix/base/services/checkin.py b/src/pretix/base/services/checkin.py index 175533db78..b295309e23 100644 --- a/src/pretix/base/services/checkin.py +++ b/src/pretix/base/services/checkin.py @@ -796,6 +796,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict, gate=device.gate if device else None, nonce=nonce, forced=force and (not entry_allowed or from_revoked_secret), + force_sent=force, raw_barcode=raw_barcode, ) op.order.log_action('pretix.event.checkin', data={ diff --git a/src/pretix/control/templates/pretixcontrol/checkin/checkins.html b/src/pretix/control/templates/pretixcontrol/checkin/checkins.html index 40589b141f..721b116d10 100644 --- a/src/pretix/control/templates/pretixcontrol/checkin/checkins.html +++ b/src/pretix/control/templates/pretixcontrol/checkin/checkins.html @@ -80,13 +80,17 @@ {% elif c.forced and c.successful %} - {% elif c.forced and not c.successful %} -
- {% trans "Failed in offline mode" %} + {% elif c.force_sent %} + {% elif c.auto_checked_in %} {% endif %} + {% if c.forced and not c.successful %} +
+ {% trans "Failed in offline mode" %} + {% endif %} {% if c.type == "exit" %}{% endif %} diff --git a/src/pretix/plugins/checkinlists/exporters.py b/src/pretix/plugins/checkinlists/exporters.py index 3ed802c8de..86fcd22313 100644 --- a/src/pretix/plugins/checkinlists/exporters.py +++ b/src/pretix/plugins/checkinlists/exporters.py @@ -618,6 +618,7 @@ class CheckinLogList(ListExporter): _('Product'), _('Name'), _('Device'), + _('Offline'), _('Offline override'), _('Automatically checked in'), _('Gate'), @@ -664,6 +665,7 @@ class CheckinLogList(ListExporter): str(ci.position.item) if ci.position else (str(ci.raw_item) if ci.raw_item else ''), (ci.position.attendee_name or ia.name) if ci.position else '', str(ci.device) if ci.device else '', + _('Yes') if ci.force_sent is True else (_('No') if ci.force_sent is False else '?'), _('Yes') if ci.forced else _('No'), _('Yes') if ci.auto_checked_in else _('No'), str(ci.gate or ''), diff --git a/src/tests/api/test_checkin.py b/src/tests/api/test_checkin.py index def405da9d..09a5ccf49f 100644 --- a/src/tests/api/test_checkin.py +++ b/src/tests/api/test_checkin.py @@ -814,6 +814,7 @@ def test_forced_flag_set_if_required(token_client, organizer, clist, event, orde ), {'force': True}, format='json') with scopes_disabled(): assert not p.checkins.order_by('pk').last().forced + assert p.checkins.order_by('pk').last().force_sent assert resp.status_code == 201 assert resp.data['status'] == 'ok' resp = token_client.post('/api/v1/organizers/{}/events/{}/checkinlists/{}/positions/{}/redeem/'.format( @@ -821,6 +822,7 @@ def test_forced_flag_set_if_required(token_client, organizer, clist, event, orde ), {'force': True}, format='json') with scopes_disabled(): assert p.checkins.order_by('pk').last().forced + assert p.checkins.order_by('pk').last().force_sent assert resp.status_code == 201 assert resp.data['status'] == 'ok' @@ -1184,6 +1186,7 @@ def test_redeem_unknown_revoked_force(token_client, organizer, clist, event, ord assert resp.data["status"] == "ok" with scopes_disabled(): assert Checkin.objects.last().forced + assert Checkin.objects.last().force_sent @pytest.mark.django_db