mirror of
https://github.com/pretix/pretix.git
synced 2026-08-28 13:34:40 +00:00
New check-in features (#3022)
This commit is contained in:
@@ -74,6 +74,7 @@ class PretixScanSecurityProfile(AllowListSecurityProfile):
|
||||
('GET', 'api-v1:checkinlistpos-list'),
|
||||
('POST', 'api-v1:checkinlistpos-redeem'),
|
||||
('GET', 'api-v1:revokedsecrets-list'),
|
||||
('GET', 'api-v1:blockedsecrets-list'),
|
||||
('GET', 'api-v1:order-list'),
|
||||
('GET', 'api-v1:orderposition-pdf_image'),
|
||||
('GET', 'api-v1:event.settings'),
|
||||
@@ -108,6 +109,7 @@ class PretixScanNoSyncNoSearchSecurityProfile(AllowListSecurityProfile):
|
||||
('POST', 'api-v1:checkinlist-failed_checkins'),
|
||||
('POST', 'api-v1:checkinlistpos-redeem'),
|
||||
('GET', 'api-v1:revokedsecrets-list'),
|
||||
('GET', 'api-v1:blockedsecrets-list'),
|
||||
('GET', 'api-v1:orderposition-pdf_image'),
|
||||
('GET', 'api-v1:event.settings'),
|
||||
('POST', 'api-v1:upload'),
|
||||
@@ -142,6 +144,7 @@ class PretixScanNoSyncSecurityProfile(AllowListSecurityProfile):
|
||||
('GET', 'api-v1:checkinlistpos-list'),
|
||||
('POST', 'api-v1:checkinlistpos-redeem'),
|
||||
('GET', 'api-v1:revokedsecrets-list'),
|
||||
('GET', 'api-v1:blockedsecrets-list'),
|
||||
('GET', 'api-v1:orderposition-pdf_image'),
|
||||
('GET', 'api-v1:event.settings'),
|
||||
('POST', 'api-v1:upload'),
|
||||
@@ -208,6 +211,7 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
|
||||
('POST', 'plugins:pretix_posbackend:stripeterminal.paymentintent'),
|
||||
('PUT', 'plugins:pretix_posbackend:file.upload'),
|
||||
('GET', 'api-v1:revokedsecrets-list'),
|
||||
('GET', 'api-v1:blockedsecrets-list'),
|
||||
('GET', 'api-v1:event.settings'),
|
||||
('GET', 'plugins:pretix_seating:event.event'),
|
||||
('GET', 'plugins:pretix_seating:event.event.subevent'),
|
||||
|
||||
@@ -60,8 +60,8 @@ class InlineItemVariationSerializer(I18nAwareModelSerializer):
|
||||
model = ItemVariation
|
||||
fields = ('id', 'value', 'active', 'description',
|
||||
'position', 'default_price', 'price', 'original_price', 'require_approval',
|
||||
'require_membership', 'require_membership_types',
|
||||
'require_membership_hidden', 'available_from', 'available_until',
|
||||
'require_membership', 'require_membership_types', 'require_membership_hidden',
|
||||
'checkin_attention', 'available_from', 'available_until',
|
||||
'sales_channels', 'hide_without_voucher', 'meta_data')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -84,8 +84,8 @@ class ItemVariationSerializer(I18nAwareModelSerializer):
|
||||
model = ItemVariation
|
||||
fields = ('id', 'value', 'active', 'description',
|
||||
'position', 'default_price', 'price', 'original_price', 'require_approval',
|
||||
'require_membership', 'require_membership_types',
|
||||
'require_membership_hidden', 'available_from', 'available_until',
|
||||
'require_membership', 'require_membership_types', 'require_membership_hidden',
|
||||
'checkin_attention', 'available_from', 'available_until',
|
||||
'sales_channels', 'hide_without_voucher', 'meta_data')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
@@ -52,7 +52,8 @@ from pretix.base.models import (
|
||||
SubEvent, TaxRule, Voucher,
|
||||
)
|
||||
from pretix.base.models.orders import (
|
||||
CartPosition, OrderFee, OrderPayment, OrderRefund, RevokedTicketSecret,
|
||||
BlockedTicketSecret, CartPosition, OrderFee, OrderPayment, OrderRefund,
|
||||
RevokedTicketSecret,
|
||||
)
|
||||
from pretix.base.pdf import get_images, get_variables
|
||||
from pretix.base.services.cart import error_messages
|
||||
@@ -300,7 +301,9 @@ class FailedCheckinSerializer(I18nAwareModelSerializer):
|
||||
class OrderDownloadsField(serializers.Field):
|
||||
def to_representation(self, instance: Order):
|
||||
if instance.status != Order.STATUS_PAID:
|
||||
if instance.status != Order.STATUS_PENDING or instance.require_approval or not instance.event.settings.ticket_download_pending:
|
||||
if instance.status != Order.STATUS_PENDING or instance.require_approval or (
|
||||
not instance.valid_if_pending and not instance.event.settings.ticket_download_pending
|
||||
):
|
||||
return []
|
||||
|
||||
request = self.context['request']
|
||||
@@ -324,7 +327,9 @@ class OrderDownloadsField(serializers.Field):
|
||||
class PositionDownloadsField(serializers.Field):
|
||||
def to_representation(self, instance: OrderPosition):
|
||||
if instance.order.status != Order.STATUS_PAID:
|
||||
if instance.order.status != Order.STATUS_PENDING or instance.order.require_approval or not instance.order.event.settings.ticket_download_pending:
|
||||
if instance.order.status != Order.STATUS_PENDING or instance.order.require_approval or (
|
||||
not instance.order.valid_if_pending and not instance.order.event.settings.ticket_download_pending
|
||||
):
|
||||
return []
|
||||
if not instance.generate_ticket:
|
||||
return []
|
||||
@@ -437,11 +442,12 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
|
||||
fields = ('id', 'order', 'positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts',
|
||||
'company', 'street', 'zipcode', 'city', 'country', 'state', 'discount',
|
||||
'attendee_email', 'voucher', 'tax_rate', 'tax_value', 'secret', 'addon_to', 'subevent', 'checkins',
|
||||
'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat', 'canceled')
|
||||
'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat', 'canceled',
|
||||
'valid_from', 'valid_until', 'blocked')
|
||||
read_only_fields = (
|
||||
'id', 'order', 'positionid', 'item', 'variation', 'price', 'voucher', 'tax_rate', 'tax_value', 'secret',
|
||||
'addon_to', 'subevent', 'checkins', 'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data',
|
||||
'seat', 'canceled', 'discount',
|
||||
'seat', 'canceled', 'discount', 'valid_from', 'valid_until', 'blocked'
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -463,7 +469,7 @@ class OrderPositionSerializer(I18nAwareModelSerializer):
|
||||
|
||||
class RequireAttentionField(serializers.Field):
|
||||
def to_representation(self, instance: OrderPosition):
|
||||
return instance.order.checkin_attention or instance.item.checkin_attention
|
||||
return instance.require_checkin_attention
|
||||
|
||||
|
||||
class AttendeeNameField(serializers.Field):
|
||||
@@ -508,7 +514,7 @@ class CheckinListOrderPositionSerializer(OrderPositionSerializer):
|
||||
'company', 'street', 'zipcode', 'city', 'country', 'state',
|
||||
'attendee_email', 'voucher', 'tax_rate', 'tax_value', 'secret', 'addon_to', 'subevent', 'checkins',
|
||||
'downloads', 'answers', 'tax_rule', 'pseudonymization_id', 'pdf_data', 'seat', 'require_attention',
|
||||
'order__status')
|
||||
'order__status', 'valid_from', 'valid_until', 'blocked')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -622,7 +628,7 @@ class OrderSerializer(I18nAwareModelSerializer):
|
||||
'code', 'status', 'testmode', 'secret', 'email', 'phone', 'locale', 'datetime', 'expires', 'payment_date',
|
||||
'payment_provider', 'fees', 'total', 'comment', 'custom_followup_at', 'invoice_address', 'positions', 'downloads',
|
||||
'checkin_attention', 'last_modified', 'payments', 'refunds', 'require_approval', 'sales_channel',
|
||||
'url', 'customer'
|
||||
'url', 'customer', 'valid_if_pending'
|
||||
)
|
||||
read_only_fields = (
|
||||
'code', 'status', 'testmode', 'secret', 'datetime', 'expires', 'payment_date',
|
||||
@@ -677,7 +683,8 @@ class OrderSerializer(I18nAwareModelSerializer):
|
||||
def update(self, instance, validated_data):
|
||||
# Even though all fields that shouldn't be edited are marked as read_only in the serializer
|
||||
# (hopefully), we'll be extra careful here and be explicit about the model fields we update.
|
||||
update_fields = ['comment', 'custom_followup_at', 'checkin_attention', 'email', 'locale', 'phone']
|
||||
update_fields = ['comment', 'custom_followup_at', 'checkin_attention', 'email', 'locale', 'phone',
|
||||
'valid_if_pending']
|
||||
|
||||
if 'invoice_address' in validated_data:
|
||||
iadata = validated_data.pop('invoice_address')
|
||||
@@ -783,7 +790,7 @@ class OrderPositionCreateSerializer(I18nAwareModelSerializer):
|
||||
model = OrderPosition
|
||||
fields = ('positionid', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts', 'attendee_email',
|
||||
'company', 'street', 'zipcode', 'city', 'country', 'state', 'is_bundled',
|
||||
'secret', 'addon_to', 'subevent', 'answers', 'seat', 'voucher')
|
||||
'secret', 'addon_to', 'subevent', 'answers', 'seat', 'voucher', 'valid_from', 'valid_until')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -941,7 +948,8 @@ class OrderCreateSerializer(I18nAwareModelSerializer):
|
||||
model = Order
|
||||
fields = ('code', 'status', 'testmode', 'email', 'phone', 'locale', 'payment_provider', 'fees', 'comment', 'sales_channel',
|
||||
'invoice_address', 'positions', 'checkin_attention', 'payment_info', 'payment_date', 'consume_carts',
|
||||
'force', 'send_email', 'simulate', 'customer', 'custom_followup_at', 'require_approval')
|
||||
'force', 'send_email', 'simulate', 'customer', 'custom_followup_at', 'require_approval',
|
||||
'valid_if_pending')
|
||||
|
||||
def validate_payment_provider(self, pp):
|
||||
if pp is None:
|
||||
@@ -1532,3 +1540,10 @@ class RevokedTicketSecretSerializer(I18nAwareModelSerializer):
|
||||
class Meta:
|
||||
model = RevokedTicketSecret
|
||||
fields = ('id', 'secret', 'created')
|
||||
|
||||
|
||||
class BlockedTicketSecretSerializer(I18nAwareModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = BlockedTicketSecret
|
||||
fields = ('id', 'secret', 'updated', 'blocked')
|
||||
|
||||
@@ -24,6 +24,7 @@ import os
|
||||
|
||||
import pycountry
|
||||
from django.core.files import File
|
||||
from django.core.validators import RegexValidator
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
@@ -53,7 +54,7 @@ class OrderPositionCreateForExistingOrderSerializer(OrderPositionCreateSerialize
|
||||
model = OrderPosition
|
||||
fields = ('order', 'item', 'variation', 'price', 'attendee_name', 'attendee_name_parts', 'attendee_email',
|
||||
'company', 'street', 'zipcode', 'city', 'country', 'state',
|
||||
'secret', 'addon_to', 'subevent', 'answers', 'seat')
|
||||
'secret', 'addon_to', 'subevent', 'answers', 'seat', 'valid_from', 'valid_until')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -89,6 +90,8 @@ class OrderPositionCreateForExistingOrderSerializer(OrderPositionCreateSerialize
|
||||
addon_to=validated_data.get('addon_to'),
|
||||
subevent=validated_data.get('subevent'),
|
||||
seat=validated_data.get('seat'),
|
||||
valid_from=validated_data.get('valid_from'),
|
||||
valid_until=validated_data.get('valid_until'),
|
||||
)
|
||||
if self.context.get('commit', True):
|
||||
ocm.commit()
|
||||
@@ -198,7 +201,7 @@ class OrderPositionChangeSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = OrderPosition
|
||||
fields = (
|
||||
'item', 'variation', 'subevent', 'seat', 'price', 'tax_rule',
|
||||
'item', 'variation', 'subevent', 'seat', 'price', 'tax_rule', 'valid_from', 'valid_until'
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -264,6 +267,8 @@ class OrderPositionChangeSerializer(serializers.ModelSerializer):
|
||||
price = validated_data.get('price', instance.price)
|
||||
seat = validated_data.get('seat', current_seat)
|
||||
tax_rule = validated_data.get('tax_rule', instance.tax_rule)
|
||||
valid_from = validated_data.get('valid_from', instance.valid_from)
|
||||
valid_until = validated_data.get('valid_until', instance.valid_until)
|
||||
|
||||
change_item = None
|
||||
if item != instance.item or variation != instance.variation:
|
||||
@@ -290,6 +295,12 @@ class OrderPositionChangeSerializer(serializers.ModelSerializer):
|
||||
if tax_rule != instance.tax_rule:
|
||||
ocm.change_tax_rule(instance, tax_rule)
|
||||
|
||||
if valid_from != instance.valid_from:
|
||||
ocm.change_valid_from(instance, valid_from)
|
||||
|
||||
if valid_until != instance.valid_until:
|
||||
ocm.change_valid_until(instance, valid_until)
|
||||
|
||||
if self.context.get('commit', True):
|
||||
ocm.commit()
|
||||
instance.refresh_from_db()
|
||||
@@ -423,3 +434,7 @@ class OrderChangeOperationSerializer(serializers.Serializer):
|
||||
seen_positions.add(d['fee'])
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class BlockNameSerializer(serializers.Serializer):
|
||||
name = serializers.CharField(validators=[RegexValidator('^(admin|api:[a-zA-Z0-9._]+)$')])
|
||||
|
||||
@@ -81,6 +81,7 @@ event_router.register(r'orders', order.OrderViewSet)
|
||||
event_router.register(r'orderpositions', order.OrderPositionViewSet)
|
||||
event_router.register(r'invoices', order.InvoiceViewSet)
|
||||
event_router.register(r'revokedsecrets', order.RevokedSecretViewSet, basename='revokedsecrets')
|
||||
event_router.register(r'blockedsecrets', order.BlockedSecretViewSet, basename='blockedsecrets')
|
||||
event_router.register(r'taxrules', event.TaxRuleViewSet)
|
||||
event_router.register(r'waitinglistentries', waitinglist.WaitingListViewSet)
|
||||
event_router.register(r'checkinlists', checkin.CheckinListViewSet)
|
||||
|
||||
@@ -273,7 +273,13 @@ with scopes_disabled():
|
||||
def check_rules_qs(self, queryset, name, value):
|
||||
if not self.checkinlist.rules:
|
||||
return queryset
|
||||
return queryset.filter(SQLLogic(self.checkinlist).apply(self.checkinlist.rules))
|
||||
return queryset.filter(
|
||||
SQLLogic(self.checkinlist).apply(self.checkinlist.rules)
|
||||
).filter(
|
||||
Q(valid_from__isnull=True) | Q(valid_from__lte=now()),
|
||||
Q(valid_until__isnull=True) | Q(valid_until__gte=now()),
|
||||
blocked__isnull=True,
|
||||
)
|
||||
|
||||
|
||||
def _handle_file_upload(data, user, auth):
|
||||
@@ -325,7 +331,13 @@ def _checkin_list_position_queryset(checkinlists, ignore_status=False, ignore_pr
|
||||
if checkinlist.subevent:
|
||||
list_q &= Q(subevent=checkinlist.subevent)
|
||||
if not ignore_status:
|
||||
list_q &= Q(order__status__in=[Order.STATUS_PAID, Order.STATUS_PENDING] if checkinlist.include_pending else [Order.STATUS_PAID])
|
||||
if checkinlist.include_pending:
|
||||
list_q &= Q(order__status__in=[Order.STATUS_PAID, Order.STATUS_PENDING])
|
||||
else:
|
||||
list_q &= Q(
|
||||
Q(order__status=Order.STATUS_PAID) |
|
||||
Q(order__status=Order.STATUS_PENDING, order__valid_if_pending=True)
|
||||
)
|
||||
if not checkinlist.all_products and not ignore_products:
|
||||
list_q &= Q(item__in=checkinlist.limit_products.values_list('id', flat=True))
|
||||
lists_qs.append(list_q)
|
||||
@@ -582,7 +594,7 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
||||
'status': 'error',
|
||||
'reason': Checkin.REASON_AMBIGUOUS,
|
||||
'reason_explanation': None,
|
||||
'require_attention': op.item.checkin_attention or op.order.checkin_attention,
|
||||
'require_attention': op.require_checkin_attention,
|
||||
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
|
||||
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
|
||||
}, status=400)
|
||||
@@ -627,7 +639,7 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
||||
except RequiredQuestionsError as e:
|
||||
return Response({
|
||||
'status': 'incomplete',
|
||||
'require_attention': op.item.checkin_attention or op.order.checkin_attention,
|
||||
'require_attention': op.require_checkin_attention,
|
||||
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
|
||||
'questions': [
|
||||
QuestionSerializer(q).data for q in e.questions
|
||||
@@ -656,14 +668,14 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
||||
'status': 'error',
|
||||
'reason': e.code,
|
||||
'reason_explanation': e.reason,
|
||||
'require_attention': op.item.checkin_attention or op.order.checkin_attention,
|
||||
'require_attention': op.require_checkin_attention,
|
||||
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
|
||||
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
|
||||
}, status=400)
|
||||
else:
|
||||
return Response({
|
||||
'status': 'ok',
|
||||
'require_attention': op.item.checkin_attention or op.order.checkin_attention,
|
||||
'require_attention': op.require_checkin_attention,
|
||||
'position': CheckinListOrderPositionSerializer(op, context=_make_context(context, op.order.event)).data,
|
||||
'list': MiniCheckinListSerializer(list_by_event[op.order.event_id]).data,
|
||||
}, status=201)
|
||||
|
||||
@@ -49,15 +49,15 @@ from rest_framework.response import Response
|
||||
|
||||
from pretix.api.models import OAuthAccessToken
|
||||
from pretix.api.serializers.order import (
|
||||
InvoiceSerializer, OrderCreateSerializer, OrderPaymentCreateSerializer,
|
||||
OrderPaymentSerializer, OrderPositionSerializer,
|
||||
OrderRefundCreateSerializer, OrderRefundSerializer, OrderSerializer,
|
||||
PriceCalcSerializer, RevokedTicketSecretSerializer,
|
||||
SimulatedOrderSerializer,
|
||||
BlockedTicketSecretSerializer, InvoiceSerializer, OrderCreateSerializer,
|
||||
OrderPaymentCreateSerializer, OrderPaymentSerializer,
|
||||
OrderPositionSerializer, OrderRefundCreateSerializer,
|
||||
OrderRefundSerializer, OrderSerializer, PriceCalcSerializer,
|
||||
RevokedTicketSecretSerializer, SimulatedOrderSerializer,
|
||||
)
|
||||
from pretix.api.serializers.orderchange import (
|
||||
OrderChangeOperationSerializer, OrderFeeChangeSerializer,
|
||||
OrderPositionChangeSerializer,
|
||||
BlockNameSerializer, OrderChangeOperationSerializer,
|
||||
OrderFeeChangeSerializer, OrderPositionChangeSerializer,
|
||||
OrderPositionCreateForExistingOrderSerializer,
|
||||
OrderPositionInfoPatchSerializer,
|
||||
)
|
||||
@@ -70,7 +70,9 @@ from pretix.base.models import (
|
||||
OrderRefund, Quota, SubEvent, SubEventMetaValue, TaxRule, TeamAPIToken,
|
||||
generate_secret,
|
||||
)
|
||||
from pretix.base.models.orders import QuestionAnswer, RevokedTicketSecret
|
||||
from pretix.base.models.orders import (
|
||||
BlockedTicketSecret, QuestionAnswer, RevokedTicketSecret,
|
||||
)
|
||||
from pretix.base.payment import PaymentException
|
||||
from pretix.base.pdf import get_images
|
||||
from pretix.base.secrets import assign_ticket_secret
|
||||
@@ -287,7 +289,7 @@ class OrderViewSet(viewsets.ModelViewSet):
|
||||
if order.status in (Order.STATUS_CANCELED, Order.STATUS_EXPIRED):
|
||||
raise PermissionDenied("Downloads are not available for canceled or expired orders.")
|
||||
|
||||
if order.status == Order.STATUS_PENDING and not request.event.settings.ticket_download_pending:
|
||||
if order.status == Order.STATUS_PENDING and not (order.valid_if_pending or request.event.settings.ticket_download_pending):
|
||||
raise PermissionDenied("Downloads are not available for pending orders.")
|
||||
|
||||
ct = CachedCombinedTicket.objects.filter(
|
||||
@@ -763,6 +765,16 @@ class OrderViewSet(viewsets.ModelViewSet):
|
||||
}
|
||||
)
|
||||
|
||||
if 'valid_if_pending' in self.request.data and serializer.instance.valid_if_pending != self.request.data.get('valid_if_pending'):
|
||||
serializer.instance.log_action(
|
||||
'pretix.event.order.valid_if_pending',
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data={
|
||||
'new_value': self.request.data.get('valid_if_pending')
|
||||
}
|
||||
)
|
||||
|
||||
if 'email' in self.request.data and serializer.instance.email != self.request.data.get('email'):
|
||||
serializer.instance.email_known_to_work = False
|
||||
serializer.instance.log_action(
|
||||
@@ -1183,7 +1195,7 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
|
||||
if pos.order.status in (Order.STATUS_CANCELED, Order.STATUS_EXPIRED):
|
||||
raise PermissionDenied("Downloads are not available for canceled or expired orders.")
|
||||
|
||||
if pos.order.status == Order.STATUS_PENDING and not request.event.settings.ticket_download_pending:
|
||||
if pos.order.status == Order.STATUS_PENDING and not (pos.order.valid_if_pending or request.event.settings.ticket_download_pending):
|
||||
raise PermissionDenied("Downloads are not available for pending orders.")
|
||||
if not pos.generate_ticket:
|
||||
raise PermissionDenied("Downloads are not enabled for this product.")
|
||||
@@ -1225,6 +1237,54 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
|
||||
raise ValidationError(str(e))
|
||||
return self.retrieve(request, [], **kwargs)
|
||||
|
||||
@action(detail=True, methods=['POST'])
|
||||
def add_block(self, request, **kwargs):
|
||||
serializer = BlockNameSerializer(
|
||||
data=request.data,
|
||||
context=self.get_serializer_context(),
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
instance = self.get_object()
|
||||
try:
|
||||
ocm = OrderChangeManager(
|
||||
instance.order,
|
||||
user=self.request.user if self.request.user.is_authenticated else None,
|
||||
auth=self.request.auth,
|
||||
notify=False,
|
||||
reissue_invoice=False,
|
||||
)
|
||||
ocm.add_block(instance, serializer.validated_data['name'])
|
||||
ocm.commit()
|
||||
except OrderError as e:
|
||||
raise ValidationError(str(e))
|
||||
except Quota.QuotaExceededException as e:
|
||||
raise ValidationError(str(e))
|
||||
return self.retrieve(request, [], **kwargs)
|
||||
|
||||
@action(detail=True, methods=['POST'])
|
||||
def remove_block(self, request, **kwargs):
|
||||
serializer = BlockNameSerializer(
|
||||
data=request.data,
|
||||
context=self.get_serializer_context(),
|
||||
)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
instance = self.get_object()
|
||||
try:
|
||||
ocm = OrderChangeManager(
|
||||
instance.order,
|
||||
user=self.request.user if self.request.user.is_authenticated else None,
|
||||
auth=self.request.auth,
|
||||
notify=False,
|
||||
reissue_invoice=False,
|
||||
)
|
||||
ocm.remove_block(instance, serializer.validated_data['name'])
|
||||
ocm.commit()
|
||||
except OrderError as e:
|
||||
raise ValidationError(str(e))
|
||||
except Quota.QuotaExceededException as e:
|
||||
raise ValidationError(str(e))
|
||||
return self.retrieve(request, [], **kwargs)
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
try:
|
||||
ocm = OrderChangeManager(
|
||||
@@ -1785,3 +1845,25 @@ class RevokedSecretViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
|
||||
def get_queryset(self):
|
||||
return RevokedTicketSecret.objects.filter(event=self.request.event)
|
||||
|
||||
|
||||
with scopes_disabled():
|
||||
class BlockedSecretFilter(FilterSet):
|
||||
updated_since = django_filters.IsoDateTimeFilter(field_name='updated', lookup_expr='gte')
|
||||
|
||||
class Meta:
|
||||
model = BlockedTicketSecret
|
||||
fields = ['blocked']
|
||||
|
||||
|
||||
class BlockedSecretViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
serializer_class = BlockedTicketSecretSerializer
|
||||
queryset = BlockedTicketSecret.objects.none()
|
||||
filter_backends = (DjangoFilterBackend, OrderingFilter)
|
||||
ordering = ('-updated', '-pk')
|
||||
filterset_class = BlockedSecretFilter
|
||||
permission = 'can_view_orders'
|
||||
write_permission = 'can_change_orders'
|
||||
|
||||
def get_queryset(self):
|
||||
return BlockedTicketSecret.objects.filter(event=self.request.event)
|
||||
|
||||
Reference in New Issue
Block a user