diff --git a/src/pretix/base/services/checkin.py b/src/pretix/base/services/checkin.py index bd0f80e49e..b8f2b863ea 100644 --- a/src/pretix/base/services/checkin.py +++ b/src/pretix/base/services/checkin.py @@ -736,7 +736,11 @@ def process_exit_all(sender, **kwargs): exit_all_at__isnull=False ).select_related('event', 'event__organizer') for cl in qs: - for p in cl.positions_inside: + positions = cl.positions_inside.filter( + Q(last_exit__isnull=True) | Q(last_exit__lte=cl.exit_all_at), + last_entry__lte=cl.exit_all_at, + ) + for p in positions: with scope(organizer=cl.event.organizer): ci = Checkin.objects.create( position=p, list=cl, auto_checked_in=True, type=Checkin.TYPE_EXIT, datetime=cl.exit_all_at @@ -748,6 +752,9 @@ def process_exit_all(sender, **kwargs): cl.event.settings.delete(f'autocheckin_dst_hack_{cl.pk}') try: cl.exit_all_at = make_aware(datetime.combine(d.date() + timedelta(days=1), d.time()), cl.event.timezone) + except pytz.exceptions.AmbiguousTimeError: + cl.exit_all_at = make_aware(datetime.combine(d.date() + timedelta(days=1), d.time()), cl.event.timezone, + is_dst=False) except pytz.exceptions.NonExistentTimeError: cl.event.settings.set(f'autocheckin_dst_hack_{cl.pk}', True) d += timedelta(hours=1) diff --git a/src/tests/base/test_checkin.py b/src/tests/base/test_checkin.py index 943e0d2675..dbaaa29189 100644 --- a/src/tests/base/test_checkin.py +++ b/src/tests/base/test_checkin.py @@ -822,6 +822,17 @@ def test_auto_check_out_only_if_checked_in(event, position, clist): assert position.checkins.count() == 2 +@pytest.mark.django_db(transaction=True) +def test_auto_check_out_only_if_checked_in_before_exit_all_at(event, position, clist): + clist.exit_all_at = event.timezone.localize(datetime(2020, 1, 2, 3, 0)) + clist.save() + with freeze_time("2020-01-02 04:05:00+01:00"): + perform_checkin(position, clist, {}) + + process_exit_all(sender=None) + assert position.checkins.count() == 1 + + @pytest.mark.django_db(transaction=True) def test_auto_check_out_dst(event, position, clist): event.settings.timezone = 'Europe/Berlin' @@ -835,12 +846,16 @@ def test_auto_check_out_dst(event, position, clist): assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 3, 29, 1, 0)) # Survive across a shift that makes the time in question ambigous - clist.exit_all_at = event.timezone.localize(datetime(2021, 10, 28, 2, 30)) + clist.exit_all_at = event.timezone.localize(datetime(2021, 10, 30, 2, 30)) clist.save() with freeze_time(clist.exit_all_at + timedelta(minutes=5)): process_exit_all(sender=None) clist.refresh_from_db() - assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 10, 29, 2, 30)) + assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 10, 31, 2, 30)) + with freeze_time(clist.exit_all_at + timedelta(minutes=5)): + process_exit_all(sender=None) + clist.refresh_from_db() + assert clist.exit_all_at.astimezone(event.timezone) == event.timezone.localize(datetime(2021, 11, 1, 2, 30)) # Doesn't survive across a shift that makes the time in question non-existant clist.exit_all_at = event.timezone.localize(datetime(2021, 3, 27, 2, 30))