From 8c84b6c634e74f61641e098c6b7db2945ccec7c3 Mon Sep 17 00:00:00 2001 From: Richard Schreiber Date: Wed, 29 Apr 2026 12:17:36 +0200 Subject: [PATCH] combine addon match and time-based validity match --- src/pretix/api/views/checkin.py | 36 +++++++++++++++------------------ 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/pretix/api/views/checkin.py b/src/pretix/api/views/checkin.py index 9fa40b3b6f..c7c571dd45 100644 --- a/src/pretix/api/views/checkin.py +++ b/src/pretix/api/views/checkin.py @@ -664,30 +664,26 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force, op_candidates += list(op.addons.all()) # 3. Handle the "multiple options found" case: Except for the unlikely case of a secret being also a valid primary - # 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. + # key on the same list, we're probably dealing with multiple linked_orderpositions or the ``addon_match`` case + # here and in the latter case need to figure out which add-on has the right product. if len(op_candidates) > 1: - # 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 - if ( - (list_by_event[op.order.event_id].addon_match or op.secret == raw_barcode or legacy_url_support) and + today = now() + # when we have addons and non-addons, we need to match the product as well + match_product = any([op.addon_to for op in op_candidates]) and any([not op.addon_to for op in op_candidates]) + op_candidates_matching_product = [ + op for op in op_candidates + if ( + (not op.valid_from or op.valid_from < today) and + (not op.valid_until or op.valid_until > today) and + (not match_product or ( + (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()) - ) - ] + )) + ) + ] if len(op_candidates_matching_product) == 0: - # None of the found add-ons has the correct product, too bad! We could just error out here, but + # None of the ops is valid today or has the correct product, too bad! We could just error out here, but # instead we just continue with *any* product and have it rejected by the check in perform_checkin. # This has the advantage of a better error message. op_candidates = [op_candidates[0]]