diff --git a/doc/api/resources/giftcards.rst b/doc/api/resources/giftcards.rst index 99b0bed1df..0d441a3af8 100644 --- a/doc/api/resources/giftcards.rst +++ b/doc/api/resources/giftcards.rst @@ -17,6 +17,7 @@ id integer Internal ID of secret string Gift card code (can not be modified later) value money (string) Current gift card value currency string Currency of the value (can not be modified later) +testmode boolean Whether this is a test gift card ===================================== ========================== ======================================================= Endpoints @@ -51,6 +52,7 @@ Endpoints "id": 1, "secret": "HLBYVELFRC77NCQY", "currency": "EUR", + "testmode": false, "value": "13.37" } ] @@ -86,6 +88,7 @@ Endpoints "id": 1, "secret": "HLBYVELFRC77NCQY", "currency": "EUR", + "testmode": false, "value": "13.37" } @@ -125,6 +128,7 @@ Endpoints { "id": 1, "secret": "HLBYVELFRC77NCQY", + "testmode": false, "currency": "EUR", "value": "13.37" } @@ -141,8 +145,9 @@ Endpoints the resource, other fields will be reset to default. With ``PATCH``, you only need to provide the fields that you want to change. - You can change all fields of the resource except the ``id``, ``secret``, and ``currency`` fields. Be careful when - modifying the ``value`` field to avoid race conditions. We recommend to use the ``transact`` method described below. + You can change all fields of the resource except the ``id``, ``secret``, ``testmode``, and ``currency`` fields. Be + careful when modifying the ``value`` field to avoid race conditions. We recommend to use the ``transact`` method + described below. **Example request**: @@ -169,6 +174,7 @@ Endpoints { "id": 1, "secret": "HLBYVELFRC77NCQY", + "testmode": false, "currency": "EUR", "value": "14.00" } @@ -211,6 +217,7 @@ Endpoints "id": 1, "secret": "HLBYVELFRC77NCQY", "currency": "EUR", + "testmode": false, "value": "15.37" } diff --git a/src/pretix/api/serializers/organizer.py b/src/pretix/api/serializers/organizer.py index a7980d5f4c..622179d601 100644 --- a/src/pretix/api/serializers/organizer.py +++ b/src/pretix/api/serializers/organizer.py @@ -46,4 +46,4 @@ class GiftCardSerializer(I18nAwareModelSerializer): class Meta: model = GiftCard - fields = ('id', 'secret', 'issuance', 'value', 'currency') + fields = ('id', 'secret', 'issuance', 'value', 'currency', 'testmode') diff --git a/src/pretix/api/views/organizer.py b/src/pretix/api/views/organizer.py index f3a5508bf6..4f3f676e44 100644 --- a/src/pretix/api/views/organizer.py +++ b/src/pretix/api/views/organizer.py @@ -117,7 +117,8 @@ class GiftCardViewSet(viewsets.ModelViewSet): GiftCard.objects.select_for_update().get(pk=self.get_object().pk) old_value = serializer.instance.value value = serializer.validated_data.pop('value') - inst = serializer.save(secret=serializer.instance.secret, currency=serializer.instance.currency) + inst = serializer.save(secret=serializer.instance.secret, currency=serializer.instance.currency, + testmode=serializer.instance.testmode) diff = value - old_value inst.transactions.create(value=diff) inst.log_action( diff --git a/src/pretix/base/migrations/0138_auto_20191017_1151.py b/src/pretix/base/migrations/0138_auto_20191017_1151.py index dfc79d5128..09c365faf5 100644 --- a/src/pretix/base/migrations/0138_auto_20191017_1151.py +++ b/src/pretix/base/migrations/0138_auto_20191017_1151.py @@ -32,6 +32,7 @@ class Migration(migrations.Migration): ('issuer', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='issued_gift_cards', to='pretixbase.Organizer')), + ('testmode', django.db.models.BooleanField(default=False)), ], options={ 'unique_together': {('secret', 'issuer')}, diff --git a/src/pretix/base/models/giftcards.py b/src/pretix/base/models/giftcards.py index d5b6702da8..424167a720 100644 --- a/src/pretix/base/models/giftcards.py +++ b/src/pretix/base/models/giftcards.py @@ -52,6 +52,10 @@ class GiftCard(LoggedModel): db_index=True, verbose_name=_('Gift card code'), ) + testmode = models.BooleanField( + verbose_name=_('Test mode card'), + default=False + ) CURRENCY_CHOICES = [(c.alpha_3, c.alpha_3 + " - " + c.name) for c in settings.CURRENCIES] currency = models.CharField(max_length=10, choices=CURRENCY_CHOICES) diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py index 617cf815bb..1b147182ad 100644 --- a/src/pretix/base/models/orders.py +++ b/src/pretix/base/models/orders.py @@ -201,7 +201,7 @@ class Order(LockModel, LoggedModel): return self.full_code def gracefully_delete(self, user=None, auth=None): - from . import Voucher + from . import Voucher, GiftCard, GiftCardTransaction if not self.testmode: raise TypeError("Only test mode orders can be deleted.") @@ -217,6 +217,10 @@ class Order(LockModel, LoggedModel): if position.voucher: Voucher.objects.filter(pk=position.voucher.pk).update(redeemed=Greatest(0, F('redeemed') - 1)) + GiftCardTransaction.objects.filter(payment__in=self.payments.all()).update(payment=None) + GiftCardTransaction.objects.filter(refund__in=self.refunds.all()).update(refund=None) + GiftCardTransaction.objects.filter(order=self).update(order=None) + GiftCard.objects.filter(issued_in__in=self.positions.all()).update(issued_in=None) OrderPosition.all.filter(order=self, addon_to__isnull=False).delete() OrderPosition.all.filter(order=self).delete() OrderFee.all.filter(order=self).delete() diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py index 3a2f41d635..fbdbdc4d11 100644 --- a/src/pretix/base/payment.py +++ b/src/pretix/base/payment.py @@ -906,6 +906,10 @@ class GiftCardPayment(BasePaymentProvider): del f['_invoice_text'] return f + @property + def test_mode_message(self) -> str: + return _("In test mode, only test cards will work.") + def is_allowed(self, request: HttpRequest, total: Decimal=None) -> bool: return super().is_allowed(request, total) and self.event.organizer.has_gift_cards @@ -958,6 +962,12 @@ class GiftCardPayment(BasePaymentProvider): if gc.currency != self.event.currency: messages.error(request, _("This gift card does not support this currency.")) return + if gc.testmode and not self.event.testmode: + messages.error(request, _("This gift card can only be used in test mode.")) + return + if not gc.testmode and self.event.testmode: + messages.error(request, _("Only test gifts cards can be used in test mode.")) + return if gc.value <= Decimal("0.00"): messages.error(request, _("All credit on this gift card has been used.")) return @@ -997,6 +1007,12 @@ class GiftCardPayment(BasePaymentProvider): if gc.currency != self.event.currency: messages.error(request, _("This gift card does not support this currency.")) return + if gc.testmode and not self.event.testmode: + messages.error(request, _("This gift card can only be used in test mode.")) + return + if not gc.testmode and self.event.testmode: + messages.error(request, _("Only test gift cards can be used in test mode.")) + return if gc.value <= Decimal("0.00"): messages.error(request, _("All credit on this gift card has been used.")) return @@ -1027,6 +1043,8 @@ class GiftCardPayment(BasePaymentProvider): raise PaymentException(_("This gift card does not support this currency.")) if not gc.accepted_by(self.event.organizer): raise PaymentException(_("This gift card is not accepted by this event organizer.")) + if gc.testmode != payment.order.testmode: + raise PaymentException(_("Only the gift card or only the order are created in test mode.")) if payment.amount > gc.value: raise PaymentException(_("This gift card was used in the meantime. Please try again")) trans = gc.transactions.create( diff --git a/src/pretix/base/services/cart.py b/src/pretix/base/services/cart.py index 6431f9eb67..a8146e5706 100644 --- a/src/pretix/base/services/cart.py +++ b/src/pretix/base/services/cart.py @@ -971,9 +971,13 @@ def get_fees(event, request, total, invoice_address, provider): cs = cart_session(request) if cs.get('gift_cards'): + gcs = cs['gift_cards'] gc_qs = event.organizer.accepted_gift_cards.filter(pk__in=cs.get('gift_cards'), currency=event.currency) summed = 0 for gc in gc_qs: + if gc.testmode != event.testmode: + gcs.remove(gc.pk) + continue fval = Decimal(gc.value) # TODO: don't require an extra query fval = min(fval, total - summed) if fval > 0: @@ -988,6 +992,7 @@ def get_fees(event, request, total, invoice_address, provider): tax_value=Decimal('0.00'), tax_rule=TaxRule.zero() )) + cs['gift_cards'] = gcs if provider and total != 0: provider = event.get_payment_providers().get(provider) diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index 6417749037..d8e433e9e2 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -600,6 +600,10 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], now_d for gc in gc_qs: if gc.currency != event.currency: raise OrderError(_("This gift card does not support this currency.")) + if gc.testmode and not event.testmode: + raise OrderError(_("This gift card can only be used in test mode.")) + if not gc.testmode and event.testmode: + raise OrderError(_("Only test gift cards can be used in test mode.")) if not gc.accepted_by(event.organizer): raise OrderError(_("This gift card is not accepted by this event organizer.")) checked_gift_cards.append(gc) @@ -1687,7 +1691,7 @@ def signal_listener_issue_giftcards(sender: Event, order: Order, **kwargs): for p in order.positions.all(): if p.item.issue_giftcard: gc = sender.organizer.issued_gift_cards.create( - currency=sender.currency, issued_in=p + currency=sender.currency, issued_in=p, testmode=order.testmode ) gc.transactions.create(value=p.price, order=order) any_giftcards = True diff --git a/src/pretix/control/forms/organizer.py b/src/pretix/control/forms/organizer.py index 503720f002..69427ca8c5 100644 --- a/src/pretix/control/forms/organizer.py +++ b/src/pretix/control/forms/organizer.py @@ -355,4 +355,4 @@ class GiftCardCreateForm(forms.ModelForm): class Meta: model = GiftCard - fields = ['secret', 'currency'] + fields = ['secret', 'currency', 'testmode'] diff --git a/src/pretix/control/templates/pretixcontrol/organizers/giftcard.html b/src/pretix/control/templates/pretixcontrol/organizers/giftcard.html index 21f58c02fd..34f7398e16 100644 --- a/src/pretix/control/templates/pretixcontrol/organizers/giftcard.html +++ b/src/pretix/control/templates/pretixcontrol/organizers/giftcard.html @@ -7,6 +7,9 @@ {% blocktrans trimmed with card=card.secret %} Gift card: {{ card }} {% endblocktrans %} + {% if card.testmode %} + {% trans "TEST MODE" %} + {% endif %}