Example #1
0
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
Example #2
0
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
Example #3
0
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
Example #4
0
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