mirror of
https://github.com/pretix/pretix.git
synced 2026-06-11 01:25:13 +00:00
Refactor link_action into media policy, gift card support
This commit is contained in:
@@ -48,7 +48,6 @@ Checking a ticket in
|
||||
allows for a certain level of idempotency and enables you to re-try after a connection failure.
|
||||
:<json string exchange_medium_type: To perform an exchange to a reusable medium, pass the type of the new reusable medium
|
||||
:<json string exchange_medium_identifier: To perform an exchange to a reusable media, pass the identifier of the new medium
|
||||
:<json string exchange_link_action: To perform an exchange to a reusable media, pass `"append"` or `"replace"` depending on whether any previous ticket links of the medium should be kept
|
||||
:<json boolean use_order_locale: Specifies that pretix should use the customer's language (``locale`` field from the
|
||||
order) when building texts (currently only the ``reason_explanation`` response field).
|
||||
Defaults to ``false`` in which case the server will determine the language (currently
|
||||
|
||||
@@ -131,7 +131,7 @@ allow_waitinglist boolean If ``false``,
|
||||
product when it is sold out.
|
||||
issue_giftcard boolean If ``true``, buying this product will yield a gift card.
|
||||
media_policy string Policy on how to handle reusable media (experimental feature).
|
||||
Possible values are ``null``, ``"new"``, ``"reuse"``, and ``"reuse_or_new"``.
|
||||
Possible values are ``null``, ``"new"``, ``"reuse"``, ``"reuse_or_new"``, ``"append"``, and ``"append_or_new"``.
|
||||
media_type string Type of reusable media to work on (experimental feature). See :ref:`rest-reusablemedia` for possible choices.
|
||||
show_quota_left boolean Publicly show how many tickets are still available.
|
||||
If this is ``null``, the event default is used.
|
||||
|
||||
@@ -90,20 +90,15 @@ class CheckinRPCRedeemInputSerializer(serializers.Serializer):
|
||||
answers = serializers.JSONField(required=False, allow_null=True)
|
||||
exchange_medium_type = serializers.ChoiceField(required=False, choices=MEDIA_TYPES)
|
||||
exchange_medium_identifier = serializers.CharField(required=False)
|
||||
exchange_link_action = serializers.ChoiceField(required=False, choices=[
|
||||
('append', 'append'),
|
||||
('replace', 'replace'),
|
||||
])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['lists'].child_relation.queryset = CheckinList.objects.filter(event__in=self.context['events']).select_related('event')
|
||||
|
||||
def validate(self, attrs):
|
||||
exchange_fields = ["exchange_medium_type", "exchange_medium_identifier", "exchange_link_action"]
|
||||
exchange_fields = ["exchange_medium_type", "exchange_medium_identifier"]
|
||||
if any(attrs.get(k) is None for k in exchange_fields) and not all(attrs.get(k) is None for k in exchange_fields):
|
||||
raise ValidationError("If you set any of exchange_medium_type, exchange_medium_identifier, or "
|
||||
"exchange_link_action, you need to set all of them.")
|
||||
raise ValidationError("If you set any of exchange_medium_type or exchange_medium_identifier, you need to set both of them.")
|
||||
return attrs
|
||||
|
||||
|
||||
|
||||
@@ -457,7 +457,7 @@ 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,
|
||||
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,
|
||||
exchange_medium_type=None, exchange_medium_identifier=None, exchange_link_action=None):
|
||||
exchange_medium_type=None, exchange_medium_identifier=None):
|
||||
if not checkinlists:
|
||||
raise ValidationError('No check-in list passed.')
|
||||
|
||||
@@ -805,7 +805,7 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
||||
locale = op.order.event.settings.locale
|
||||
with language(locale):
|
||||
try:
|
||||
if exchange_link_action and medium:
|
||||
if exchange_medium_identifier and medium:
|
||||
# Cannot scan a medium and then request to exchange it
|
||||
raise CheckInError(
|
||||
gettext('You cannot exchange a medium for a medium.'),
|
||||
@@ -833,14 +833,13 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
|
||||
reusable_medium=medium,
|
||||
)
|
||||
|
||||
if exchange_link_action: # other fields are filled, see CheckinRPCRedeemInputSerializer.validate
|
||||
if exchange_medium_identifier: # other fields are filled, see CheckinRPCRedeemInputSerializer.validate
|
||||
with transaction.atomic():
|
||||
# Do exchange and check-in atomically, i.e. both succeed or both fail
|
||||
medium = perform_media_exchange(
|
||||
organizer=request.organizer,
|
||||
media_type=exchange_medium_type,
|
||||
identifier=exchange_medium_identifier,
|
||||
link_action=exchange_link_action,
|
||||
link_orderposition=op,
|
||||
user=user,
|
||||
auth=auth,
|
||||
@@ -1061,7 +1060,6 @@ class CheckinRPCRedeemView(views.APIView):
|
||||
legacy_url_support=False,
|
||||
exchange_medium_type=s.validated_data.get('exchange_medium_type'),
|
||||
exchange_medium_identifier=s.validated_data.get('exchange_medium_identifier'),
|
||||
exchange_link_action=s.validated_data.get('exchange_link_action'),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
class BaseMediaType:
|
||||
medium_created_by_server = False
|
||||
medium_created_from_unknown_supported = False
|
||||
supports_orderposition = False
|
||||
supports_giftcard = False
|
||||
|
||||
@@ -56,7 +57,7 @@ class BaseMediaType:
|
||||
def is_active(self, organizer):
|
||||
return organizer.settings.get(f'reusable_media_type_{self.identifier}', as_type=bool, default=False)
|
||||
|
||||
def handle_unknown(self, organizer, identifier, user, auth):
|
||||
def handle_unknown(self, organizer, identifier, user, auth, force_create=False):
|
||||
pass
|
||||
|
||||
def handle_new(self, organizer, medium, user, auth):
|
||||
@@ -88,23 +89,32 @@ class NfcUidMediaType(BaseMediaType):
|
||||
verbose_name = _('NFC UID-based')
|
||||
icon = 'pretixbase/img/media/nfc_uid.svg'
|
||||
medium_created_by_server = False
|
||||
medium_created_from_unknown_supported = True
|
||||
supports_giftcard = True
|
||||
supports_orderposition = True
|
||||
|
||||
def handle_unknown(self, organizer, identifier, user, auth):
|
||||
def handle_unknown(self, organizer, identifier, user, auth, force_create=False):
|
||||
from pretix.base.models import GiftCard, ReusableMedium
|
||||
|
||||
if organizer.settings.get(f'reusable_media_type_{self.identifier}_autocreate_giftcard', as_type=bool):
|
||||
create_giftcard = organizer.settings.get(f'reusable_media_type_{self.identifier}_autocreate_giftcard', as_type=bool)
|
||||
if create_giftcard or force_create:
|
||||
if identifier.startswith("08"):
|
||||
# Don't create gift cards for NFC UIDs that start with 08, which represents NFC cards that issue random
|
||||
# UIDs on every read, so they won't be useful.
|
||||
return
|
||||
with transaction.atomic():
|
||||
gc = GiftCard.objects.create(
|
||||
issuer=organizer,
|
||||
expires=organizer.default_gift_card_expiry,
|
||||
currency=organizer.settings.get(f'reusable_media_type_{self.identifier}_autocreate_giftcard_currency'),
|
||||
)
|
||||
if create_giftcard:
|
||||
gc = GiftCard.objects.create(
|
||||
issuer=organizer,
|
||||
expires=organizer.default_gift_card_expiry,
|
||||
currency=organizer.settings.get(f'reusable_media_type_{self.identifier}_autocreate_giftcard_currency'),
|
||||
)
|
||||
gc.log_action(
|
||||
'pretix.giftcards.created',
|
||||
user=user, auth=auth,
|
||||
)
|
||||
else:
|
||||
gc = None
|
||||
m = ReusableMedium.objects.create(
|
||||
type=self.identifier,
|
||||
identifier=identifier,
|
||||
@@ -116,10 +126,6 @@ class NfcUidMediaType(BaseMediaType):
|
||||
'pretix.reusable_medium.created.auto',
|
||||
user=user, auth=auth,
|
||||
)
|
||||
gc.log_action(
|
||||
'pretix.giftcards.created',
|
||||
user=user, auth=auth,
|
||||
)
|
||||
return m
|
||||
|
||||
|
||||
|
||||
@@ -452,11 +452,16 @@ class Item(LoggedModel):
|
||||
MEDIA_POLICY_REUSE = 'reuse'
|
||||
MEDIA_POLICY_NEW = 'new'
|
||||
MEDIA_POLICY_REUSE_OR_NEW = 'reuse_or_new'
|
||||
MEDIA_POLICY_APPEND = 'append'
|
||||
MEDIA_POLICY_APPEND_OR_NEW = 'append_or_new'
|
||||
MEDIA_POLICIES = (
|
||||
(None, _("Don't use reusable media, use regular one-off tickets")),
|
||||
(MEDIA_POLICY_REUSE, _('Require an existing medium to be reused')),
|
||||
(MEDIA_POLICY_NEW, _('Require a previously unknown medium to be newly added')),
|
||||
(MEDIA_POLICY_REUSE_OR_NEW, _('Require either an existing or a new medium to be used')),
|
||||
(MEDIA_POLICY_REUSE, _('Require an existing medium to be reused, replacing any previous tickets')),
|
||||
(MEDIA_POLICY_REUSE_OR_NEW, _('Require either an existing or a new medium to be used, replacing any previous tickets')),
|
||||
(MEDIA_POLICY_APPEND, _('Require an existing medium to be reused, adding to any previous tickets')),
|
||||
(MEDIA_POLICY_APPEND_OR_NEW,
|
||||
_('Require either an existing or a new medium to be used, adding to any previous tickets')),
|
||||
)
|
||||
|
||||
objects = ItemQuerySetManager()
|
||||
|
||||
@@ -287,11 +287,11 @@ def _check_position_constraints(
|
||||
raise CartPositionError(error_messages['unavailable'])
|
||||
|
||||
# Invalid media policy for online sale
|
||||
if item.media_policy in (Item.MEDIA_POLICY_NEW, Item.MEDIA_POLICY_REUSE_OR_NEW):
|
||||
if item.media_policy in (Item.MEDIA_POLICY_NEW, Item.MEDIA_POLICY_REUSE_OR_NEW, Item.MEDIA_POLICY_APPEND_OR_NEW, Item.MEDIA_POLICY_REUSE_OR_NEW):
|
||||
mt = MEDIA_TYPES[item.media_type]
|
||||
if not mt.medium_created_by_server:
|
||||
raise CartPositionError(error_messages['media_usage_not_implemented'])
|
||||
elif item.media_policy == Item.MEDIA_POLICY_REUSE:
|
||||
elif item.media_policy in (Item.MEDIA_POLICY_REUSE, Item.MEDIA_POLICY_APPEND):
|
||||
raise CartPositionError(error_messages['media_usage_not_implemented'])
|
||||
|
||||
# Item removed from sales channel
|
||||
|
||||
@@ -75,21 +75,18 @@ def get_keysets_for_organizer(organizer):
|
||||
return sets
|
||||
|
||||
|
||||
def perform_media_exchange(organizer, media_type, identifier, link_action, link_orderposition, user, auth):
|
||||
def perform_media_exchange(organizer, media_type, identifier, link_orderposition, user, auth):
|
||||
"""
|
||||
Create or retrieve a medium, then link the order position to it. Expected to be called in a transaction.
|
||||
|
||||
:param organizer: Organizer to operate in
|
||||
:param media_type: Type of medium to operate with
|
||||
:param identifier: Identifier of the medium
|
||||
:param link_action: one of `"append"` or `"replace"`
|
||||
:param link_orderposition: Position to link to the medium
|
||||
:return: ReusableMedium
|
||||
"""
|
||||
medium = None
|
||||
media_policy = link_orderposition.item.media_policy
|
||||
if link_action not in ('append', 'replace'):
|
||||
raise ValueError("Invalid link_action")
|
||||
|
||||
if media_type not in MEDIA_TYPES: # should be caught by serializer already
|
||||
raise CheckInError(
|
||||
@@ -119,7 +116,12 @@ def perform_media_exchange(organizer, media_type, identifier, link_action, link_
|
||||
reason=_('Ticket is already exchanged for reusable medium.'),
|
||||
)
|
||||
|
||||
if media_policy == Item.MEDIA_POLICY_REUSE:
|
||||
if media_policy in (Item.MEDIA_POLICY_APPEND, Item.MEDIA_POLICY_APPEND_OR_NEW, Item.MEDIA_POLICY_NEW):
|
||||
link_action = "append"
|
||||
else:
|
||||
link_action = "replace"
|
||||
|
||||
if media_policy in (Item.MEDIA_POLICY_REUSE, Item.MEDIA_POLICY_APPEND):
|
||||
try:
|
||||
medium = ReusableMedium.objects.get(
|
||||
type=media_type,
|
||||
@@ -140,19 +142,28 @@ def perform_media_exchange(organizer, media_type, identifier, link_action, link_
|
||||
reason=_('Reusable medium is inactive or expired.'),
|
||||
)
|
||||
|
||||
elif media_policy == Item.MEDIA_POLICY_REUSE_OR_NEW:
|
||||
medium, created = ReusableMedium.objects.get_or_create(
|
||||
type=media_type,
|
||||
identifier=identifier,
|
||||
organizer=organizer,
|
||||
)
|
||||
if created:
|
||||
medium.log_action(
|
||||
'pretix.reusable_medium.created.auto',
|
||||
user=user,
|
||||
auth=auth,
|
||||
elif media_policy in (Item.MEDIA_POLICY_REUSE_OR_NEW, Item.MEDIA_POLICY_APPEND_OR_NEW):
|
||||
try:
|
||||
medium = ReusableMedium.objects.get(
|
||||
type=media_type,
|
||||
identifier=identifier,
|
||||
organizer=organizer,
|
||||
)
|
||||
elif medium.is_expired or not medium.active:
|
||||
except ReusableMedium.DoesNotExist:
|
||||
if not MEDIA_TYPES[media_type].medium_created_from_unknown_supported:
|
||||
raise CheckInError(
|
||||
_('Reusable medium not found and could not be created.'),
|
||||
Checkin.REASON_MEDIUM_INVALID,
|
||||
)
|
||||
|
||||
medium = MEDIA_TYPES[media_type].handle_unknown(organizer, identifier, user, auth, force_create=True)
|
||||
if not medium:
|
||||
raise CheckInError(
|
||||
_('Reusable medium not found and could not be created.'),
|
||||
Checkin.REASON_MEDIUM_INVALID,
|
||||
)
|
||||
|
||||
if medium.is_expired or not medium.active:
|
||||
raise CheckInError(
|
||||
_('Reusable medium is inactive or expired.'),
|
||||
Checkin.REASON_MEDIUM_INVALID,
|
||||
@@ -160,23 +171,24 @@ def perform_media_exchange(organizer, media_type, identifier, link_action, link_
|
||||
)
|
||||
|
||||
elif media_policy == Item.MEDIA_POLICY_NEW:
|
||||
try:
|
||||
medium = ReusableMedium.objects.create(
|
||||
type=media_type,
|
||||
identifier=identifier,
|
||||
organizer=organizer,
|
||||
if not MEDIA_TYPES[media_type].medium_created_from_unknown_supported:
|
||||
raise CheckInError(
|
||||
_('Reusable medium not found and could not be created.'),
|
||||
Checkin.REASON_MEDIUM_INVALID,
|
||||
)
|
||||
try:
|
||||
medium = MEDIA_TYPES[media_type].handle_unknown(organizer, identifier, user, auth, force_create=True)
|
||||
except IntegrityError:
|
||||
raise CheckInError(
|
||||
_('Reusable medium already exists.'),
|
||||
Checkin.REASON_MEDIUM_EXISTS,
|
||||
)
|
||||
else:
|
||||
medium.log_action(
|
||||
'pretix.reusable_medium.created.auto',
|
||||
user=user,
|
||||
auth=auth,
|
||||
)
|
||||
if not medium:
|
||||
raise CheckInError(
|
||||
_('Reusable medium could not be created.'),
|
||||
Checkin.REASON_MEDIUM_INVALID,
|
||||
)
|
||||
|
||||
else:
|
||||
raise CheckInError(
|
||||
|
||||
@@ -3506,7 +3506,7 @@ def signal_listener_issue_media(sender: Event, order: Order, **kwargs):
|
||||
from pretix.base.models import ReusableMedium
|
||||
|
||||
for p in order.positions.all():
|
||||
if p.item.media_policy in (Item.MEDIA_POLICY_NEW, Item.MEDIA_POLICY_REUSE_OR_NEW):
|
||||
if p.item.media_policy in (Item.MEDIA_POLICY_NEW, Item.MEDIA_POLICY_REUSE_OR_NEW, Item.MEDIA_POLICY_APPEND_OR_NEW):
|
||||
mt = MEDIA_TYPES[p.item.media_type]
|
||||
if mt.medium_created_by_server and not p.linked_media.exists():
|
||||
rm = ReusableMedium.objects.create(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
|
||||
'use strict';
|
||||
{
|
||||
const globals = this;
|
||||
|
||||
@@ -1263,8 +1263,7 @@ def test_exchange_incomplete_body(token_client, organizer, clist, event, order):
|
||||
})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {
|
||||
'non_field_errors': ['If you set any of exchange_medium_type, exchange_medium_identifier, or '
|
||||
'exchange_link_action, you need to set all of them.']
|
||||
'non_field_errors': ['If you set any of exchange_medium_type or exchange_medium_identifier, you need to set both of them.']
|
||||
}
|
||||
|
||||
|
||||
@@ -1281,7 +1280,6 @@ def test_exchange_medium_for_medium(token_client, organizer, clist, event, order
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "barcode",
|
||||
"exchange_medium_identifier": "hijkl",
|
||||
"exchange_link_action": "replace",
|
||||
})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data['status'] == 'error'
|
||||
@@ -1294,7 +1292,6 @@ def test_exchange_unknown_media_type(token_client, organizer, clist, event, orde
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "unknown",
|
||||
"exchange_medium_identifier": "hijkl",
|
||||
"exchange_link_action": "replace",
|
||||
})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"exchange_medium_type": ["\"unknown\" is not a valid choice."]}
|
||||
@@ -1306,7 +1303,6 @@ def test_exchange_disabled_media_type(token_client, organizer, clist, event, ord
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "nfc_uid",
|
||||
"exchange_medium_identifier": "hijkl",
|
||||
"exchange_link_action": "replace",
|
||||
})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data['status'] == 'error'
|
||||
@@ -1323,7 +1319,6 @@ def test_exchange_mismatch_media_type(token_client, organizer, clist, event, ord
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "nfc_uid",
|
||||
"exchange_medium_identifier": "12345678",
|
||||
"exchange_link_action": "replace",
|
||||
})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data['status'] == 'error'
|
||||
@@ -1340,7 +1335,6 @@ def test_exchange_no_item_policy(token_client, organizer, clist, event, order, i
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "nfc_uid",
|
||||
"exchange_medium_identifier": "12345678",
|
||||
"exchange_link_action": "replace",
|
||||
})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data['status'] == 'error'
|
||||
@@ -1352,13 +1346,12 @@ def test_exchange_no_item_policy(token_client, organizer, clist, event, order, i
|
||||
def test_exchange_reuse_or_new_new(token_client, organizer, clist, event, order, item):
|
||||
organizer.settings.reusable_media_type_nfc_uid = True
|
||||
item.media_type = "nfc_uid"
|
||||
item.media_policy = Item.MEDIA_POLICY_NEW
|
||||
item.media_policy = Item.MEDIA_POLICY_REUSE_OR_NEW
|
||||
item.save()
|
||||
resp = _redeem(token_client, organizer, clist, "z3fsn8jyufm5kpk768q69gkbyr5f4h6w", {
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "nfc_uid",
|
||||
"exchange_medium_identifier": "12345678",
|
||||
"exchange_link_action": "replace",
|
||||
})
|
||||
assert resp.status_code == 201
|
||||
assert resp.data['status'] == 'ok'
|
||||
@@ -1388,7 +1381,6 @@ def test_exchange_reuse_or_new_reuse_replace(token_client, organizer, clist, eve
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "nfc_uid",
|
||||
"exchange_medium_identifier": "12345678",
|
||||
"exchange_link_action": "replace",
|
||||
})
|
||||
assert resp.status_code == 201
|
||||
assert resp.data['status'] == 'ok'
|
||||
@@ -1401,7 +1393,7 @@ def test_exchange_reuse_or_new_reuse_replace(token_client, organizer, clist, eve
|
||||
def test_exchange_reuse_or_new_reuse_append(token_client, organizer, clist, event, order, item):
|
||||
organizer.settings.reusable_media_type_nfc_uid = True
|
||||
item.media_type = "nfc_uid"
|
||||
item.media_policy = Item.MEDIA_POLICY_REUSE_OR_NEW
|
||||
item.media_policy = Item.MEDIA_POLICY_APPEND_OR_NEW
|
||||
item.save()
|
||||
with scopes_disabled():
|
||||
rm = ReusableMedium.objects.create(
|
||||
@@ -1414,7 +1406,6 @@ def test_exchange_reuse_or_new_reuse_append(token_client, organizer, clist, even
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "nfc_uid",
|
||||
"exchange_medium_identifier": "12345678",
|
||||
"exchange_link_action": "append",
|
||||
})
|
||||
assert resp.status_code == 201
|
||||
assert resp.data['status'] == 'ok'
|
||||
@@ -1428,7 +1419,7 @@ def test_exchange_reuse_or_new_reuse_append(token_client, organizer, clist, even
|
||||
def test_exchange_reuse_exists_append(token_client, organizer, clist, event, order, item):
|
||||
organizer.settings.reusable_media_type_nfc_uid = True
|
||||
item.media_type = "nfc_uid"
|
||||
item.media_policy = Item.MEDIA_POLICY_REUSE_OR_NEW
|
||||
item.media_policy = Item.MEDIA_POLICY_APPEND_OR_NEW
|
||||
item.save()
|
||||
with scopes_disabled():
|
||||
rm = ReusableMedium.objects.create(
|
||||
@@ -1441,7 +1432,6 @@ def test_exchange_reuse_exists_append(token_client, organizer, clist, event, ord
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "nfc_uid",
|
||||
"exchange_medium_identifier": "12345678",
|
||||
"exchange_link_action": "append",
|
||||
})
|
||||
assert resp.status_code == 201
|
||||
assert resp.data['status'] == 'ok'
|
||||
@@ -1469,7 +1459,6 @@ def test_exchange_reuse_expired(token_client, organizer, clist, event, order, it
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "nfc_uid",
|
||||
"exchange_medium_identifier": "12345678",
|
||||
"exchange_link_action": "replace",
|
||||
})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data['status'] == 'error'
|
||||
@@ -1486,7 +1475,6 @@ def test_exchange_reuse_not_exists(token_client, organizer, clist, event, order,
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "nfc_uid",
|
||||
"exchange_medium_identifier": "12345678",
|
||||
"exchange_link_action": "replace",
|
||||
})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data['status'] == 'error'
|
||||
@@ -1672,3 +1660,82 @@ def test_exchanged_double_exchange(token_client, organizer, clist, event, order,
|
||||
assert resp.status_code == 400
|
||||
assert resp.data['status'] == 'error'
|
||||
assert resp.data['reason'] == 'already_exchanged'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"media_policy,media_type",
|
||||
[
|
||||
(Item.MEDIA_POLICY_NEW, "nfc_mf0aes"),
|
||||
(Item.MEDIA_POLICY_REUSE_OR_NEW, "nfc_mf0aes"),
|
||||
(Item.MEDIA_POLICY_APPEND_OR_NEW, "nfc_mf0aes"),
|
||||
(Item.MEDIA_POLICY_NEW, "barcode"),
|
||||
(Item.MEDIA_POLICY_REUSE_OR_NEW, "barcode"),
|
||||
(Item.MEDIA_POLICY_APPEND_OR_NEW, "barcode"),
|
||||
]
|
||||
)
|
||||
def test_exchange_unsupported_media_type_for_new(token_client, organizer, clist, event, order, item, media_policy, media_type):
|
||||
organizer.settings.set(f'reusable_media_type_{media_type}', True)
|
||||
# Shouldn't be configurable, but test that the logic is solid anyway
|
||||
item.media_type = media_type
|
||||
item.media_policy = media_policy
|
||||
item.save()
|
||||
resp = _redeem(token_client, organizer, clist, "z3fsn8jyufm5kpk768q69gkbyr5f4h6w", {
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": media_type,
|
||||
"exchange_medium_identifier": "12345678",
|
||||
})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data['status'] == 'error'
|
||||
assert resp.data['reason'] == 'medium_invalid'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"media_policy",
|
||||
[
|
||||
Item.MEDIA_POLICY_NEW,
|
||||
Item.MEDIA_POLICY_REUSE_OR_NEW,
|
||||
Item.MEDIA_POLICY_APPEND_OR_NEW,
|
||||
]
|
||||
)
|
||||
def test_exchange_rejected_media_identifier(token_client, organizer, clist, event, order, item, media_policy):
|
||||
organizer.settings.reusable_media_type_nfc_uid = True
|
||||
item.media_type = "nfc_uid"
|
||||
item.media_policy = media_policy
|
||||
item.save()
|
||||
resp = _redeem(token_client, organizer, clist, "z3fsn8jyufm5kpk768q69gkbyr5f4h6w", {
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "nfc_uid",
|
||||
"exchange_medium_identifier": "08RANDOM",
|
||||
})
|
||||
assert resp.status_code == 400
|
||||
assert resp.data['status'] == 'error'
|
||||
assert resp.data['reason'] == 'medium_invalid'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"media_policy",
|
||||
[
|
||||
Item.MEDIA_POLICY_NEW,
|
||||
Item.MEDIA_POLICY_REUSE_OR_NEW,
|
||||
Item.MEDIA_POLICY_APPEND_OR_NEW,
|
||||
]
|
||||
)
|
||||
def test_exchange_create_gift_card(token_client, organizer, clist, event, order, item, media_policy):
|
||||
organizer.settings.reusable_media_type_nfc_uid = True
|
||||
organizer.settings.reusable_media_type_nfc_uid_autocreate_giftcard = True
|
||||
organizer.settings.reusable_media_type_nfc_uid_autocreate_giftcard_currency = "EUR"
|
||||
item.media_type = "nfc_uid"
|
||||
item.media_policy = media_policy
|
||||
item.save()
|
||||
resp = _redeem(token_client, organizer, clist, "z3fsn8jyufm5kpk768q69gkbyr5f4h6w", {
|
||||
"source_type": "barcode",
|
||||
"exchange_medium_type": "nfc_uid",
|
||||
"exchange_medium_identifier": "0412345",
|
||||
})
|
||||
assert resp.status_code == 201
|
||||
with scopes_disabled():
|
||||
rm = ReusableMedium.objects.get(identifier="0412345")
|
||||
assert rm.linked_giftcard.currency == "EUR"
|
||||
|
||||
Reference in New Issue
Block a user