diff --git a/src/pretix/api/views/checkin.py b/src/pretix/api/views/checkin.py index 8fa1290563..adddf28ec9 100644 --- a/src/pretix/api/views/checkin.py +++ b/src/pretix/api/views/checkin.py @@ -667,19 +667,19 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force, # key on the same list, we're probably dealing with the ``addon_match`` case here and need to figure out # which add-on has the right product. if len(op_candidates) > 1: - op_candidates_matching_product = [ - op for op in op_candidates - if ( - (list_by_event[op.order.event_id].addon_match or op.secret == raw_barcode or legacy_url_support) and - (list_by_event[op.order.event_id].all_products or op.item_id in {i.pk for i in list_by_event[op.order.event_id].limit_products.all()}) - ) - ] - - if len(op_candidates_matching_product) > 1: - # if none of the above filters the results to 1, filter based on op.valid_from/until - # keep ops without valid_from/until + # only check addons if at most one non-addon-op is in op_candidates + # otherwise it is likely a medium linked to multiple orderpositions, which we need to filter based on validity + if len([op for op in op_candidates if not op.addon_to]) <= 1: op_candidates_matching_product = [ - op for op in op_candidates_matching_product + op for op in op_candidates + if ( + (list_by_event[op.order.event_id].addon_match or op.secret == raw_barcode or legacy_url_support) and + (list_by_event[op.order.event_id].all_products or op.item_id in {i.pk for i in list_by_event[op.order.event_id].limit_products.all()}) + ) + ] + else: + op_candidates_matching_product = [ + op for op in op_candidates if ( (not op.valid_from or op.valid_from < now()) and (not op.valid_until or op.valid_until > now()) diff --git a/src/tests/api/test_checkinrpc.py b/src/tests/api/test_checkinrpc.py index ed3169d027..ff09d36e16 100644 --- a/src/tests/api/test_checkinrpc.py +++ b/src/tests/api/test_checkinrpc.py @@ -310,30 +310,37 @@ def test_by_medium_multiple_orderpositions(token_client, organizer, clist, event organizer=organizer, ) rm.linked_orderpositions.add(order.positions.first()) - rm.linked_orderpositions.add(order.positions.all()[1]) + op_item_other = order.positions.all()[1] + rm.linked_orderpositions.add(op_item_other) + + # multiple tickets are valid => no check-in resp = _redeem(token_client, organizer, clist, "abcdef", {"source_type": "barcode"}) - # TODO: AMBIGIOUS - assert resp.status_code == 201 + assert resp.status_code == 400 + assert resp.data['status'] == 'error' + assert resp.data['reason'] == 'ambiguous' with scopes_disabled(): - op = order.positions.first() - op.valid_from = datetime.datetime(2020, 1, 1, 12, 0, 0, tzinfo=event.timezone) - op.save() + op_item_other.valid_from = datetime.datetime(2020, 1, 1, 12, 0, 0, tzinfo=event.timezone) + op_item_other.valid_until = datetime.datetime(2020, 1, 1, 15, 0, 0, tzinfo=event.timezone) + op_item_other.save() with freeze_time("2020-01-01 13:45:00"): + # multiple tickets are valid => no check-in resp = _redeem(token_client, organizer, clist, "abcdef", {"source_type": "barcode"}) - # TODO: AMBIGIOUS - assert resp.status_code == 201 + assert resp.status_code == 400 + assert resp.data['status'] == 'error' + assert resp.data['reason'] == 'ambiguous' with freeze_time("2020-01-01 10:45:00"): resp = _redeem(token_client, organizer, clist, "abcdef", {"source_type": "barcode"}) assert resp.status_code == 201 assert resp.data['status'] == 'ok' - with scopes_disabled(): - ci = clist.checkins.get(position=order.positions.first()) - assert ci.raw_barcode == "abcdef" - assert ci.raw_source_type == "barcode" + with freeze_time("2020-01-01 15:45:00"): + resp = _redeem(token_client, organizer, clist, "abcdef", {"source_type": "barcode"}) + assert resp.status_code == 400 + assert resp.data['status'] == 'error' + assert resp.data['reason'] == 'already_redeemed' @pytest.mark.django_db