class CheckInFilterForm(FilterForm): orders = { 'code': ('order__code', 'item__name'), '-code': ('-order__code', '-item__name'), 'email': ('order__email', 'item__name'), '-email': ('-order__email', '-item__name'), 'status': (FixedOrderBy(F('last_entry'), nulls_first=True, descending=True), 'order__code'), '-status': (FixedOrderBy(F('last_entry'), nulls_last=True), '-order__code'), 'timestamp': (FixedOrderBy(F('last_entry'), nulls_first=True), 'order__code'), '-timestamp': (FixedOrderBy(F('last_entry'), nulls_last=True, descending=True), '-order__code'), 'item': ('item__name', 'variation__value', 'order__code'), '-item': ('-item__name', '-variation__value', '-order__code'), 'seat': ('seat__sorting_rank', 'seat__guid'), '-seat': ('-seat__sorting_rank', '-seat__guid'), 'name': {'_order': F('display_name').asc(nulls_first=True), 'display_name': Coalesce('attendee_name_cached', 'addon_to__attendee_name_cached')}, '-name': {'_order': F('display_name').desc(nulls_last=True), 'display_name': Coalesce('attendee_name_cached', 'addon_to__attendee_name_cached')}, } user = forms.CharField( label=_('Search attendee…'), widget=forms.TextInput(attrs={ 'placeholder': _('Search attendee…'), 'autofocus': 'autofocus' }), required=False ) status = forms.ChoiceField( label=_('Check-in status'), choices=( ('', _('All attendees')), ('3', pgettext_lazy('checkin state', 'Checked in but left')), ('2', pgettext_lazy('checkin state', 'Present')), ('1', _('Checked in')), ('0', _('Not checked in')), ), required=False, ) item = forms.ModelChoiceField( label=_('Products'), queryset=Item.objects.none(), required=False, empty_label=_('All products') ) def __init__(self, *args, **kwargs): self.event = kwargs.pop('event') self.list = kwargs.pop('list') super().__init__(*args, **kwargs) if self.list.all_products: self.fields['item'].queryset = self.event.items.all() else: self.fields['item'].queryset = self.list.limit_products.all() def filter_qs(self, qs): fdata = self.cleaned_data if fdata.get('user'): u = fdata.get('user') qs = qs.filter( Q(order__code__istartswith=u) | Q(secret__istartswith=u) | Q(pseudonymization_id__istartswith=u) | Q(order__email__icontains=u) | Q(attendee_name_cached__icontains=u) | Q(attendee_email__icontains=u) | Q(voucher__code__istartswith=u) | Q(order__invoice_address__name_cached__icontains=u) | Q(order__invoice_address__company__icontains=u) ) if fdata.get('status'): s = fdata.get('status') if s == '1': qs = qs.filter(last_entry__isnull=False) elif s == '2': qs = qs.filter(last_entry__isnull=False).filter( Q(last_exit__isnull=True) | Q(last_exit__lt=F('last_entry')) ) elif s == '3': qs = qs.filter(last_entry__isnull=False).filter( Q(last_exit__isnull=False) & Q(last_exit__gte=F('last_entry')) ) elif s == '0': qs = qs.filter(last_entry__isnull=True) if fdata.get('ordering'): ob = self.orders[fdata.get('ordering')] if isinstance(ob, dict): ob = dict(ob) o = ob.pop('_order') qs = qs.annotate(**ob).order_by(o) elif isinstance(ob, (list, tuple)): qs = qs.order_by(*ob) else: qs = qs.order_by(ob) if fdata.get('item'): qs = qs.filter(item=fdata.get('item')) return qs
class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = CheckinListOrderPositionSerializer queryset = OrderPosition.all.none() filter_backends = (DjangoFilterBackend, RichOrderingFilter) ordering = ('attendee_name_cached', 'positionid') ordering_fields = ( 'order__code', 'order__datetime', 'positionid', 'attendee_name', 'last_checked_in', 'order__email', ) ordering_custom = { 'attendee_name': { '_order': F('display_name').asc(nulls_first=True), 'display_name': Coalesce('attendee_name_cached', 'addon_to__attendee_name_cached') }, '-attendee_name': { '_order': F('display_name').desc(nulls_last=True), 'display_name': Coalesce('attendee_name_cached', 'addon_to__attendee_name_cached') }, 'last_checked_in': { '_order': FixedOrderBy(F('last_checked_in'), nulls_first=True), }, '-last_checked_in': { '_order': FixedOrderBy(F('last_checked_in'), nulls_last=True, descending=True), }, } filterset_class = CheckinOrderPositionFilter permission = 'can_view_orders' write_permission = 'can_change_orders' @cached_property def checkinlist(self): try: return get_object_or_404(CheckinList, event=self.request.event, pk=self.kwargs.get("list")) except ValueError: raise Http404() def get_queryset(self, ignore_status=False): cqs = Checkin.objects.filter( position_id=OuterRef('pk'), list_id=self.checkinlist.pk ).order_by().values('position_id').annotate( m=Max('datetime') ).values('m') qs = OrderPosition.objects.filter( order__event=self.request.event, subevent=self.checkinlist.subevent ).annotate( last_checked_in=Subquery(cqs) ) if self.request.query_params.get('ignore_status', 'false') != 'true' and not ignore_status: qs = qs.filter( order__status__in=[Order.STATUS_PAID, Order.STATUS_PENDING] if self.checkinlist.include_pending else [Order.STATUS_PAID] ) if self.request.query_params.get('pdf_data', 'false') == 'true': qs = qs.prefetch_related( Prefetch( lookup='checkins', queryset=Checkin.objects.filter(list_id=self.checkinlist.pk) ), 'checkins', 'answers', 'answers__options', 'answers__question', Prefetch('addons', OrderPosition.objects.select_related('item', 'variation')), Prefetch('order', Order.objects.select_related('invoice_address').prefetch_related( Prefetch( 'event', Event.objects.select_related('organizer') ), Prefetch( 'positions', OrderPosition.objects.prefetch_related( 'checkins', 'item', 'variation', 'answers', 'answers__options', 'answers__question', ) ) )) ).select_related( 'item', 'variation', 'item__category', 'addon_to', 'order', 'order__invoice_address', 'seat' ) else: qs = qs.prefetch_related( Prefetch( lookup='checkins', queryset=Checkin.objects.filter(list_id=self.checkinlist.pk) ), 'answers', 'answers__options', 'answers__question', Prefetch('addons', OrderPosition.objects.select_related('item', 'variation')) ).select_related('item', 'variation', 'order', 'addon_to', 'order__invoice_address', 'order', 'seat') if not self.checkinlist.all_products: qs = qs.filter(item__in=self.checkinlist.limit_products.values_list('id', flat=True)) return qs @action(detail=True, methods=['POST']) def redeem(self, *args, **kwargs): force = bool(self.request.data.get('force', False)) ignore_unpaid = bool(self.request.data.get('ignore_unpaid', False)) nonce = self.request.data.get('nonce') op = self.get_object(ignore_status=True) if 'datetime' in self.request.data: dt = DateTimeField().to_internal_value(self.request.data.get('datetime')) else: dt = now() given_answers = {} if 'answers' in self.request.data: aws = self.request.data.get('answers') for q in op.item.questions.filter(ask_during_checkin=True): if str(q.pk) in aws: try: given_answers[q] = q.clean_answer(aws[str(q.pk)]) except ValidationError: pass try: perform_checkin( op=op, clist=self.checkinlist, given_answers=given_answers, force=force, ignore_unpaid=ignore_unpaid, nonce=nonce, datetime=dt, questions_supported=self.request.data.get('questions_supported', True), canceled_supported=self.request.data.get('canceled_supported', False), user=self.request.user, auth=self.request.auth, ) except RequiredQuestionsError as e: return Response({ 'status': 'incomplete', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data, 'questions': [ QuestionSerializer(q).data for q in e.questions ] }, status=400) except CheckInError as e: return Response({ 'status': 'error', 'reason': e.code, 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data }, status=400) else: return Response({ 'status': 'ok', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer(op, context=self.get_serializer_context()).data }, status=201) def get_object(self, ignore_status=False): queryset = self.filter_queryset(self.get_queryset(ignore_status=ignore_status)) if self.kwargs['pk'].isnumeric(): obj = get_object_or_404(queryset, Q(pk=self.kwargs['pk']) | Q(secret=self.kwargs['pk'])) else: obj = get_object_or_404(queryset, secret=self.kwargs['pk']) return obj
class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = CheckinListOrderPositionSerializer queryset = OrderPosition.all.none() filter_backends = (DjangoFilterBackend, RichOrderingFilter) ordering = ('attendee_name_cached', 'positionid') ordering_fields = ( 'order__code', 'order__datetime', 'positionid', 'attendee_name', 'last_checked_in', 'order__email', ) ordering_custom = { 'attendee_name': { '_order': F('display_name').asc(nulls_first=True), 'display_name': Coalesce('attendee_name_cached', 'addon_to__attendee_name_cached') }, '-attendee_name': { '_order': F('display_name').desc(nulls_last=True), 'display_name': Coalesce('attendee_name_cached', 'addon_to__attendee_name_cached') }, 'last_checked_in': { '_order': FixedOrderBy(F('last_checked_in'), nulls_first=True), }, '-last_checked_in': { '_order': FixedOrderBy(F('last_checked_in'), nulls_last=True, descending=True), }, } filterset_class = CheckinOrderPositionFilter permission = 'can_view_orders' write_permission = 'can_change_orders' @cached_property def checkinlist(self): try: return get_object_or_404(CheckinList, event=self.request.event, pk=self.kwargs.get("list")) except ValueError: raise Http404() def get_queryset(self, ignore_status=False, ignore_products=False): cqs = Checkin.objects.filter( position_id=OuterRef('pk'), list_id=self.checkinlist.pk).order_by().values( 'position_id').annotate(m=Max('datetime')).values('m') qs = OrderPosition.objects.filter( order__event=self.request.event, ).annotate( last_checked_in=Subquery(cqs)) if self.checkinlist.subevent: qs = qs.filter(subevent=self.checkinlist.subevent) if self.request.query_params.get( 'ignore_status', 'false') != 'true' and not ignore_status: qs = qs.filter( order__status__in=[Order.STATUS_PAID, Order.STATUS_PENDING] if self.checkinlist.include_pending else [Order.STATUS_PAID]) if self.request.query_params.get('pdf_data', 'false') == 'true': qs = qs.prefetch_related( Prefetch(lookup='checkins', queryset=Checkin.objects.filter( list_id=self.checkinlist.pk)), 'checkins', 'answers', 'answers__options', 'answers__question', Prefetch( 'addons', OrderPosition.objects.select_related('item', 'variation')), Prefetch( 'order', Order.objects.select_related( 'invoice_address').prefetch_related( Prefetch( 'event', Event.objects.select_related('organizer')), Prefetch( 'positions', OrderPosition.objects.prefetch_related( 'checkins', 'item', 'variation', 'answers', 'answers__options', 'answers__question', ))))).select_related('item', 'variation', 'item__category', 'addon_to', 'order', 'order__invoice_address', 'seat') else: qs = qs.prefetch_related( Prefetch(lookup='checkins', queryset=Checkin.objects.filter( list_id=self.checkinlist.pk)), 'answers', 'answers__options', 'answers__question', Prefetch( 'addons', OrderPosition.objects.select_related( 'item', 'variation'))).select_related( 'item', 'variation', 'order', 'addon_to', 'order__invoice_address', 'order', 'seat') if not self.checkinlist.all_products and not ignore_products: qs = qs.filter(item__in=self.checkinlist.limit_products. values_list('id', flat=True)) return qs @action(detail=False, methods=['POST'], url_name='redeem', url_path='(?P<pk>.*)/redeem') def redeem(self, *args, **kwargs): force = bool(self.request.data.get('force', False)) type = self.request.data.get('type', None) or Checkin.TYPE_ENTRY if type not in dict(Checkin.CHECKIN_TYPES): raise ValidationError("Invalid check-in type.") ignore_unpaid = bool(self.request.data.get('ignore_unpaid', False)) nonce = self.request.data.get('nonce') if 'datetime' in self.request.data: dt = DateTimeField().to_internal_value( self.request.data.get('datetime')) else: dt = now() try: queryset = self.get_queryset(ignore_status=True, ignore_products=True) if self.kwargs['pk'].isnumeric(): op = queryset.get( Q(pk=self.kwargs['pk']) | Q(secret=self.kwargs['pk'])) else: op = queryset.get(secret=self.kwargs['pk']) except OrderPosition.DoesNotExist: revoked_matches = list( self.request.event.revoked_secrets.filter( secret=self.kwargs['pk'])) if len(revoked_matches) == 0 or not force: self.request.event.log_action('pretix.event.checkin.unknown', data={ 'datetime': dt, 'type': type, 'list': self.checkinlist.pk, 'barcode': self.kwargs['pk'] }, user=self.request.user, auth=self.request.auth) raise Http404() op = revoked_matches[0].position op.order.log_action('pretix.event.checkin.revoked', data={ 'datetime': dt, 'type': type, 'list': self.checkinlist.pk, 'barcode': self.kwargs['pk'] }, user=self.request.user, auth=self.request.auth) given_answers = {} if 'answers' in self.request.data: aws = self.request.data.get('answers') for q in op.item.questions.filter(ask_during_checkin=True): if str(q.pk) in aws: try: if q.type == Question.TYPE_FILE: given_answers[q] = self._handle_file_upload( aws[str(q.pk)]) else: given_answers[q] = q.clean_answer(aws[str(q.pk)]) except ValidationError: pass try: perform_checkin( op=op, clist=self.checkinlist, given_answers=given_answers, force=force, ignore_unpaid=ignore_unpaid, nonce=nonce, datetime=dt, questions_supported=self.request.data.get( 'questions_supported', True), canceled_supported=self.request.data.get( 'canceled_supported', False), user=self.request.user, auth=self.request.auth, type=type, ) except RequiredQuestionsError as e: return Response( { 'status': 'incomplete', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data, 'questions': [QuestionSerializer(q).data for q in e.questions] }, status=400) except CheckInError as e: op.order.log_action('pretix.event.checkin.denied', data={ 'position': op.id, 'positionid': op.positionid, 'errorcode': e.code, 'force': force, 'datetime': dt, 'type': type, 'list': self.checkinlist.pk }, user=self.request.user, auth=self.request.auth) return Response( { 'status': 'error', 'reason': e.code, 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data }, status=400) else: return Response( { 'status': 'ok', 'require_attention': op.item.checkin_attention or op.order.checkin_attention, 'position': CheckinListOrderPositionSerializer( op, context=self.get_serializer_context()).data }, status=201) def _handle_file_upload(self, data): try: cf = CachedFile.objects.get( session_key= f'api-upload-{str(type(self.request.user or self.request.auth))}-{(self.request.user or self.request.auth).pk}', file__isnull=False, pk=data[len("file:"):], ) except (ValidationError, IndexError): # invalid uuid raise ValidationError( 'The submitted file ID "{fid}" was not found.'.format( fid=data)) except CachedFile.DoesNotExist: raise ValidationError( 'The submitted file ID "{fid}" was not found.'.format( fid=data)) allowed_types = ('image/png', 'image/jpeg', 'image/gif', 'application/pdf') if cf.type not in allowed_types: raise ValidationError( 'The submitted file "{fid}" has a file type that is not allowed in this field.' .format(fid=data)) if cf.file.size > 10 * 1024 * 1024: raise ValidationError( 'The submitted file "{fid}" is too large to be used in this field.' .format(fid=data)) return cf.file
class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = OrderPositionSerializer queryset = OrderPosition.objects.none() filter_backends = (DjangoFilterBackend, RichOrderingFilter) ordering = ('attendee_name', 'positionid') ordering_fields = ( 'order__code', 'order__datetime', 'positionid', 'attendee_name', 'last_checked_in', 'order__email', ) ordering_custom = { 'attendee_name': { '_order': F('display_name').asc(nulls_first=True), 'display_name': Coalesce('attendee_name', 'addon_to__attendee_name') }, '-attendee_name': { '_order': F('display_name').desc(nulls_last=True), 'display_name': Coalesce('attendee_name', 'addon_to__attendee_name') }, 'last_checked_in': { '_order': FixedOrderBy(F('last_checked_in'), nulls_first=True), }, '-last_checked_in': { '_order': FixedOrderBy(F('last_checked_in'), nulls_last=True, descending=True), }, } filter_class = OrderPositionFilter permission = 'can_view_orders' @cached_property def checkinlist(self): return get_object_or_404(CheckinList, event=self.request.event, pk=self.kwargs.get("list")) def get_queryset(self): cqs = Checkin.objects.filter( position_id=OuterRef('pk'), list_id=self.checkinlist.pk).order_by().values( 'position_id').annotate(m=Max('datetime')).values('m') qs = OrderPosition.objects.filter( order__event=self.request.event, order__status__in=[Order.STATUS_PAID, Order.STATUS_PENDING] if self.checkinlist.include_pending else [Order.STATUS_PAID], subevent=self.checkinlist.subevent).annotate( last_checked_in=Subquery(cqs)).prefetch_related( Prefetch(lookup='checkins', queryset=Checkin.objects.filter( list_id=self.checkinlist.pk))).select_related( 'item', 'variation', 'order', 'addon_to') if not self.checkinlist.all_products: qs = qs.filter(item__in=self.checkinlist.limit_products. values_list('id', flat=True)) return qs