forked from CGM_Public/pretix_original
Add Reusable Media Exchange to Checkin API
This commit is contained in:
@@ -89,7 +89,7 @@ class NfcUidMediaType(BaseMediaType):
|
||||
icon = 'pretixbase/img/media/nfc_uid.svg'
|
||||
medium_created_by_server = False
|
||||
supports_giftcard = True
|
||||
supports_orderposition = False
|
||||
supports_orderposition = True
|
||||
|
||||
def handle_unknown(self, organizer, identifier, user, auth):
|
||||
from pretix.base.models import GiftCard, ReusableMedium
|
||||
@@ -129,7 +129,7 @@ class NfcMf0aesMediaType(BaseMediaType):
|
||||
icon = 'pretixbase/img/media/nfc_secure.svg'
|
||||
medium_created_by_server = False
|
||||
supports_giftcard = True
|
||||
supports_orderposition = False
|
||||
supports_orderposition = True
|
||||
|
||||
def handle_new(self, organizer, medium, user, auth):
|
||||
from pretix.base.models import GiftCard
|
||||
|
||||
@@ -351,6 +351,7 @@ class Checkin(models.Model):
|
||||
REASON_UNAPPROVED = 'unapproved'
|
||||
REASON_INVALID_TIME = 'invalid_time'
|
||||
REASON_ANNULLED = 'annulled'
|
||||
REASON_ALREADY_EXCHANGED = 'already_exchanged'
|
||||
REASONS = (
|
||||
(REASON_CANCELED, _('Order canceled')),
|
||||
(REASON_INVALID, _('Unknown ticket')),
|
||||
@@ -366,6 +367,7 @@ class Checkin(models.Model):
|
||||
(REASON_UNAPPROVED, _('Order not approved')),
|
||||
(REASON_INVALID_TIME, _('Ticket not valid at this time')),
|
||||
(REASON_ANNULLED, _('Check-in annulled')),
|
||||
(REASON_ALREADY_EXCHANGED, _('Ticket already exchanged')),
|
||||
)
|
||||
|
||||
successful = models.BooleanField(
|
||||
|
||||
@@ -867,6 +867,15 @@ class RequiredQuestionsError(Exception):
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
class RequiredMediaExchangeError(Exception):
|
||||
def __init__(self, msg, code, media_policy, media_type):
|
||||
self.msg = msg
|
||||
self.code = code
|
||||
self.media_policy = media_policy
|
||||
self.media_type = media_type
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
def _save_answers(op, answers, given_answers):
|
||||
def _create_answer(question, answer):
|
||||
try:
|
||||
@@ -939,7 +948,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
|
||||
ignore_unpaid=False, nonce=None, datetime=None, questions_supported=True,
|
||||
user=None, auth=None, canceled_supported=False, type=Checkin.TYPE_ENTRY,
|
||||
raw_barcode=None, raw_source_type=None, from_revoked_secret=False, simulate=False,
|
||||
gate=None):
|
||||
gate=None, media_exchange_supported=False, reusable_media=None):
|
||||
"""
|
||||
Create a checkin for this particular order position and check-in list. Fails with CheckInError if the check in is
|
||||
not valid at this time.
|
||||
@@ -951,6 +960,8 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
|
||||
questions are not filled out.
|
||||
:param ignore_unpaid: When set to True, this will succeed even when the order is unpaid.
|
||||
:param questions_supported: When set to False, questions are ignored
|
||||
:param media_exchange_supported: When set to False, media exchanges are ignored and access with un-exchanged media
|
||||
might be permitted
|
||||
:param nonce: A random nonce to prevent race conditions.
|
||||
:param datetime: The datetime of the checkin, defaults to now.
|
||||
:param simulate: If true, the check-in is not saved.
|
||||
@@ -1100,6 +1111,34 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
|
||||
'incomplete',
|
||||
require_answers
|
||||
)
|
||||
media_exchange_supported = True
|
||||
|
||||
required_media_policy = op.item.media_policy
|
||||
required_media_type = op.item.media_type
|
||||
linked_media = op.linked_media
|
||||
require_media_exchange = required_media_policy and required_media_type and not linked_media.exists()
|
||||
if require_media_exchange and not force and media_exchange_supported:
|
||||
raise RequiredMediaExchangeError(
|
||||
_('You need to exchange your ticket to complete this check-in.'),
|
||||
'exchange',
|
||||
required_media_policy,
|
||||
required_media_type
|
||||
)
|
||||
|
||||
require_reusable_media_usage = required_media_policy and required_media_type and op.organizer.settings.reusable_media_usage_enforced
|
||||
if require_reusable_media_usage and not force:
|
||||
if not reusable_media and not linked_media.exists() and media_exchange_supported:
|
||||
raise RequiredMediaExchangeError(
|
||||
_('You need to exchange your ticket to complete this check-in.'),
|
||||
'exchange',
|
||||
required_media_policy,
|
||||
required_media_type
|
||||
)
|
||||
elif not reusable_media and linked_media.exists():
|
||||
raise CheckInError(
|
||||
_('This ticket has already been exchanged - use the reusable media instead.'),
|
||||
'already_exchanged',
|
||||
)
|
||||
|
||||
device = None
|
||||
if isinstance(auth, Device):
|
||||
|
||||
@@ -217,6 +217,19 @@ DEFAULTS = {
|
||||
"later.")
|
||||
)
|
||||
},
|
||||
'reusable_media_usage_enforced': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
'form_class': forms.BooleanField,
|
||||
'serializer_class': serializers.BooleanField,
|
||||
'form_kwargs': dict(
|
||||
label=_("Enforce the usage of issued re-usable media for check-in"),
|
||||
help_text=_("If enabled, a ticket barcode will not be accepted anymore, if a re-usable media has been "
|
||||
"created and linked to a ticket. Keeping this option turned off will treat the re-usable "
|
||||
"medium and ticket as equals."),
|
||||
widget=forms.CheckboxInput(attrs={'data-display-dependency': '#id_settings-reusable_media_active'}),
|
||||
)
|
||||
},
|
||||
'reusable_media_type_barcode': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
|
||||
Reference in New Issue
Block a user