mirror of
https://github.com/pretix/pretix.git
synced 2026-06-11 01:25:13 +00:00
CheckinRPC: Also perform media exchange
This commit is contained in:
committed by
Raphael Michel
parent
1f4189d539
commit
b2380f794e
@@ -26,7 +26,7 @@ from rest_framework.exceptions import ValidationError
|
|||||||
from pretix.api.serializers.event import SubEventSerializer
|
from pretix.api.serializers.event import SubEventSerializer
|
||||||
from pretix.api.serializers.i18n import I18nAwareModelSerializer
|
from pretix.api.serializers.i18n import I18nAwareModelSerializer
|
||||||
from pretix.base.media import MEDIA_TYPES
|
from pretix.base.media import MEDIA_TYPES
|
||||||
from pretix.base.models import Checkin, CheckinList
|
from pretix.base.models import Checkin, CheckinList, Item
|
||||||
|
|
||||||
|
|
||||||
class CheckinListSerializer(I18nAwareModelSerializer):
|
class CheckinListSerializer(I18nAwareModelSerializer):
|
||||||
@@ -88,6 +88,13 @@ class CheckinRPCRedeemInputSerializer(serializers.Serializer):
|
|||||||
nonce = serializers.CharField(required=False, allow_null=True)
|
nonce = serializers.CharField(required=False, allow_null=True)
|
||||||
datetime = serializers.DateTimeField(required=False, allow_null=True)
|
datetime = serializers.DateTimeField(required=False, allow_null=True)
|
||||||
answers = serializers.JSONField(required=False, allow_null=True)
|
answers = serializers.JSONField(required=False, allow_null=True)
|
||||||
|
media_type = serializers.ChoiceField(required=False, choices=MEDIA_TYPES)
|
||||||
|
media_identifier = serializers.CharField(required=False)
|
||||||
|
media_policy = serializers.ChoiceField(required=False, choices=Item.MEDIA_POLICIES)
|
||||||
|
media_action = serializers.ChoiceField(required=False, choices=[
|
||||||
|
('append', 'append'),
|
||||||
|
('replace', 'replace'),
|
||||||
|
])
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ from pretix.base.services.checkin import (
|
|||||||
CheckInError, RequiredMediaExchangeError, RequiredQuestionsError, SQLLogic,
|
CheckInError, RequiredMediaExchangeError, RequiredQuestionsError, SQLLogic,
|
||||||
perform_checkin,
|
perform_checkin,
|
||||||
)
|
)
|
||||||
|
from pretix.base.services.media import perform_media_exchange
|
||||||
from pretix.base.signals import checkin_annulled
|
from pretix.base.signals import checkin_annulled
|
||||||
from pretix.helpers import OF_SELF
|
from pretix.helpers import OF_SELF
|
||||||
|
|
||||||
@@ -455,7 +456,8 @@ def _checkin_list_position_queryset(checkinlists, ignore_status=False, ignore_pr
|
|||||||
|
|
||||||
def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force, checkin_type, ignore_unpaid, nonce,
|
def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force, checkin_type, ignore_unpaid, nonce,
|
||||||
untrusted_input, user, auth, expand, pdf_data, request, questions_supported, canceled_supported,
|
untrusted_input, user, auth, expand, pdf_data, request, questions_supported, canceled_supported,
|
||||||
source_type='barcode', legacy_url_support=False, simulate=False, gate=None, use_order_locale=False):
|
source_type='barcode', legacy_url_support=False, simulate=False, gate=None, use_order_locale=False,
|
||||||
|
media_type=None, media_identifier=None, media_policy=None, media_action=None):
|
||||||
if not checkinlists:
|
if not checkinlists:
|
||||||
raise ValidationError('No check-in list passed.')
|
raise ValidationError('No check-in list passed.')
|
||||||
|
|
||||||
@@ -803,26 +805,59 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
|||||||
locale = op.order.event.settings.locale
|
locale = op.order.event.settings.locale
|
||||||
with language(locale):
|
with language(locale):
|
||||||
try:
|
try:
|
||||||
perform_checkin(
|
if all(k is not None for k in [media_type, media_identifier, media_policy, media_action]) and not media:
|
||||||
op=op,
|
with transaction.atomic():
|
||||||
clist=list_by_event[op.order.event_id],
|
media = perform_media_exchange(
|
||||||
given_answers=given_answers,
|
organizer=request.organizer,
|
||||||
force=force,
|
media_type=media_type,
|
||||||
ignore_unpaid=ignore_unpaid,
|
media_identifier=media_identifier,
|
||||||
nonce=nonce,
|
media_policy=media_policy,
|
||||||
datetime=datetime,
|
media_action=media_action,
|
||||||
questions_supported=questions_supported,
|
op=op,
|
||||||
canceled_supported=canceled_supported,
|
)
|
||||||
user=user,
|
source_type = media.media_type.identifier
|
||||||
auth=auth,
|
|
||||||
type=checkin_type,
|
perform_checkin(
|
||||||
raw_barcode=raw_barcode_for_checkin,
|
op=op,
|
||||||
raw_source_type=source_type,
|
clist=list_by_event[op.order.event_id],
|
||||||
from_revoked_secret=from_revoked_secret,
|
given_answers=given_answers,
|
||||||
simulate=simulate,
|
force=force,
|
||||||
gate=gate,
|
ignore_unpaid=ignore_unpaid,
|
||||||
reusable_media=media,
|
nonce=nonce,
|
||||||
)
|
datetime=datetime,
|
||||||
|
questions_supported=questions_supported,
|
||||||
|
canceled_supported=canceled_supported,
|
||||||
|
user=user,
|
||||||
|
auth=auth,
|
||||||
|
type=checkin_type,
|
||||||
|
raw_barcode=raw_barcode_for_checkin,
|
||||||
|
raw_source_type=source_type,
|
||||||
|
from_revoked_secret=from_revoked_secret,
|
||||||
|
simulate=simulate,
|
||||||
|
gate=gate,
|
||||||
|
reusable_media=media,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
perform_checkin(
|
||||||
|
op=op,
|
||||||
|
clist=list_by_event[op.order.event_id],
|
||||||
|
given_answers=given_answers,
|
||||||
|
force=force,
|
||||||
|
ignore_unpaid=ignore_unpaid,
|
||||||
|
nonce=nonce,
|
||||||
|
datetime=datetime,
|
||||||
|
questions_supported=questions_supported,
|
||||||
|
canceled_supported=canceled_supported,
|
||||||
|
user=user,
|
||||||
|
auth=auth,
|
||||||
|
type=checkin_type,
|
||||||
|
raw_barcode=raw_barcode_for_checkin,
|
||||||
|
raw_source_type=source_type,
|
||||||
|
from_revoked_secret=from_revoked_secret,
|
||||||
|
simulate=simulate,
|
||||||
|
gate=gate,
|
||||||
|
reusable_media=media,
|
||||||
|
)
|
||||||
except RequiredQuestionsError as e:
|
except RequiredQuestionsError as e:
|
||||||
return Response({
|
return Response({
|
||||||
'status': 'incomplete',
|
'status': 'incomplete',
|
||||||
@@ -843,6 +878,17 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
|||||||
'media_policy': e.media_policy,
|
'media_policy': e.media_policy,
|
||||||
'media_type': e.media_type,
|
'media_type': e.media_type,
|
||||||
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
|
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
|
||||||
|
'reason_explanation': e.msg,
|
||||||
|
}, status=400)
|
||||||
|
except ReusableMedium.DuplicateEntry:
|
||||||
|
return Response({
|
||||||
|
'status': 'error',
|
||||||
|
'reason': Checkin.REASON_AMBIGUOUS,
|
||||||
|
'reason_explanation': 'Reusable medium identifier is ambigous',
|
||||||
|
'require_attention': op.require_checkin_attention,
|
||||||
|
'checkin_texts': op.checkin_texts,
|
||||||
|
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
|
||||||
|
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
|
||||||
}, status=400)
|
}, status=400)
|
||||||
except CheckInError as e:
|
except CheckInError as e:
|
||||||
if not simulate:
|
if not simulate:
|
||||||
@@ -1031,6 +1077,10 @@ class CheckinRPCRedeemView(views.APIView):
|
|||||||
canceled_supported=True,
|
canceled_supported=True,
|
||||||
request=self.request, # this is not clean, but we need it in the serializers for URL generation
|
request=self.request, # this is not clean, but we need it in the serializers for URL generation
|
||||||
legacy_url_support=False,
|
legacy_url_support=False,
|
||||||
|
media_type=s.validated_data.get('media_type'),
|
||||||
|
media_identifier=s.validated_data.get('media_identifier'),
|
||||||
|
media_policy=s.validated_data.get('media_policy'),
|
||||||
|
media_action=s.validated_data.get('media_action'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ class ReusableMediaViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
return Response({"result": None})
|
return Response({"result": None})
|
||||||
|
|
||||||
@scopes_disabled() # we are sure enough that get_queryset() is correct, so we save some perforamnce
|
@scopes_disabled() # we are sure enough that get_queryset() is correct, so we save some performance
|
||||||
def list(self, request, **kwargs):
|
def list(self, request, **kwargs):
|
||||||
date = serializers.DateTimeField().to_representation(now())
|
date = serializers.DateTimeField().to_representation(now())
|
||||||
queryset = self.filter_queryset(self.get_queryset())
|
queryset = self.filter_queryset(self.get_queryset())
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ class ReusableMedium(LoggedModel):
|
|||||||
]
|
]
|
||||||
ordering = "identifier", "type", "organizer"
|
ordering = "identifier", "type", "organizer"
|
||||||
|
|
||||||
|
class DuplicateEntry(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MediumKeySet(models.Model):
|
class MediumKeySet(models.Model):
|
||||||
organizer = models.ForeignKey('Organizer', on_delete=models.CASCADE, related_name='medium_key_sets')
|
organizer = models.ForeignKey('Organizer', on_delete=models.CASCADE, related_name='medium_key_sets')
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ from django.db import IntegrityError
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django_scopes import scopes_disabled
|
from django_scopes import scopes_disabled
|
||||||
|
|
||||||
from pretix.base.models import GiftCardAcceptance
|
from pretix.base.models import GiftCardAcceptance, Item
|
||||||
from pretix.base.models.media import MediumKeySet
|
from pretix.base.models.media import MediumKeySet, ReusableMedium
|
||||||
|
|
||||||
|
|
||||||
def create_nfc_mf0aes_keyset(organizer):
|
def create_nfc_mf0aes_keyset(organizer):
|
||||||
@@ -70,3 +70,36 @@ def get_keysets_for_organizer(organizer):
|
|||||||
if new_set:
|
if new_set:
|
||||||
sets.append(new_set)
|
sets.append(new_set)
|
||||||
return sets
|
return sets
|
||||||
|
|
||||||
|
|
||||||
|
def perform_media_exchange(organizer, media_type, media_identifier, media_policy, media_action, op):
|
||||||
|
medium = None
|
||||||
|
|
||||||
|
if media_policy in [Item.MEDIA_POLICY_REUSE, Item.MEDIA_POLICY_REUSE_OR_NEW]:
|
||||||
|
try:
|
||||||
|
medium = ReusableMedium.objects.get(
|
||||||
|
type=media_type,
|
||||||
|
identifier=media_identifier,
|
||||||
|
organizer=organizer,
|
||||||
|
)
|
||||||
|
except ReusableMedium.DoesNotExist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not medium and media_policy in [Item.MEDIA_POLICY_NEW, Item.MEDIA_POLICY_REUSE_OR_NEW]:
|
||||||
|
try:
|
||||||
|
medium = ReusableMedium.objects.create(
|
||||||
|
type=media_type,
|
||||||
|
identifier=media_identifier,
|
||||||
|
organizer=organizer,
|
||||||
|
)
|
||||||
|
except IntegrityError:
|
||||||
|
raise ReusableMedium.DuplicateEntry()
|
||||||
|
|
||||||
|
if medium:
|
||||||
|
if media_action == 'append':
|
||||||
|
medium.linked_orderpositions.add(*[op])
|
||||||
|
elif media_action == 'replace':
|
||||||
|
medium.linked_orderpositions.set([op])
|
||||||
|
medium.save()
|
||||||
|
|
||||||
|
return medium
|
||||||
|
|||||||
Reference in New Issue
Block a user