diff --git a/doc/api/resources/orders.rst b/doc/api/resources/orders.rst
index 107e8811b9..4aaf3e18b5 100644
--- a/doc/api/resources/orders.rst
+++ b/doc/api/resources/orders.rst
@@ -769,6 +769,8 @@ Creating orders
* does not support file upload questions
+ * does not support redeeming gift cards
+
You can supply the following fields of the resource:
* ``code`` (optional)
diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py
index 6f17b19faa..6895195187 100644
--- a/src/pretix/base/payment.py
+++ b/src/pretix/base/payment.py
@@ -1044,7 +1044,9 @@ class GiftCardPayment(BasePaymentProvider):
messages.error(request, _("This gift card can not be redeemed since its code is not unique. Please contact the organizer of this event."))
def execute_payment(self, request: HttpRequest, payment: OrderPayment) -> str:
- for p in payment.order.positions.all():
+ # This method will only be called when retrying payments, e.g. after a payment_prepare call. It is not called
+ # during the order creation phase because this payment provider is a special case.
+ for p in payment.order.positions.all(): # noqa - just a safeguard
if p.item.issue_giftcard:
raise PaymentException(_("You cannot pay with gift cards when buying a gift card."))
@@ -1053,13 +1055,11 @@ class GiftCardPayment(BasePaymentProvider):
raise PaymentException("Invalid state, should never occur.")
with transaction.atomic():
gc = GiftCard.objects.select_for_update().get(pk=gcpk)
- if gc.currency != self.event.currency:
+ if gc.currency != self.event.currency: # noqa - just a safeguard
raise PaymentException(_("This gift card does not support this currency."))
- if not gc.accepted_by(self.event.organizer):
+ if not gc.accepted_by(self.event.organizer): # noqa - just a safeguard
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:
+ if payment.amount > gc.value: # noqa - just a safeguard
raise PaymentException(_("This gift card was used in the meantime. Please try again"))
trans = gc.transactions.create(
value=-1 * payment.amount,
diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py
index 49169b0124..76471e1464 100644
--- a/src/pretix/base/services/orders.py
+++ b/src/pretix/base/services/orders.py
@@ -569,14 +569,12 @@ def _get_fees(positions: List[CartPosition], payment_provider: BasePaymentProvid
fees += resp
total += sum(f.value for f in fees)
- summed = 0
gift_card_values = {}
for gc in gift_cards:
fval = Decimal(gc.value) # TODO: don't require an extra query
- fval = min(fval, total - summed)
+ fval = min(fval, total)
if fval > 0:
total -= fval
- summed += fval
gift_card_values[gc] = fval
if payment_provider:
@@ -967,6 +965,7 @@ class OrderChangeManager:
'seat_subevent_mismatch': _('You selected seat "{seat}" for a date that does not match the selected ticket date. Please choose a seat again.'),
'seat_required': _('The selected product requires you to select a seat.'),
'seat_forbidden': _('The selected product does not allow to select a seat.'),
+ 'gift_card_change': _('You cannot change the price of a position that has been used to issue a gift card.'),
}
ItemOperation = namedtuple('ItemOperation', ('position', 'item', 'variation'))
SubeventOperation = namedtuple('SubeventOperation', ('position', 'subevent'))
@@ -1034,6 +1033,9 @@ class OrderChangeManager:
def change_price(self, position: OrderPosition, price: Decimal):
price = position.item.tax(price, base_price_is='gross')
+ if position.issued_gift_cards.exists():
+ raise OrderError(self.error_messages['gift_card_change'])
+
self._totaldiff += price.gross - position.price
if self.order.event.settings.invoice_include_free or price.gross != Decimal('0.00') or position.price != Decimal('0.00'):
@@ -1710,13 +1712,14 @@ def signal_listener_issue_giftcards(sender: Event, order: Order, **kwargs):
issued = Decimal('0.00')
for gc in p.issued_gift_cards.all():
issued += gc.transactions.first().value
- gc = sender.organizer.issued_gift_cards.create(
- currency=sender.currency, issued_in=p, testmode=order.testmode
- )
- gc.transactions.create(value=p.price - issued, order=order)
- any_giftcards = True
- p.secret = gc.secret
- p.save(update_fields=['secret'])
+ if p.price - issued > 0:
+ gc = sender.organizer.issued_gift_cards.create(
+ currency=sender.currency, issued_in=p, testmode=order.testmode
+ )
+ gc.transactions.create(value=p.price - issued, order=order)
+ any_giftcards = True
+ p.secret = gc.secret
+ p.save(update_fields=['secret'])
if any_giftcards:
tickets.invalidate_cache.apply_async(kwargs={'event': sender.pk, 'order': order.pk})
diff --git a/src/pretix/control/templates/pretixcontrol/organizers/giftcards.html b/src/pretix/control/templates/pretixcontrol/organizers/giftcards.html
index 3789ed9516..7445310505 100644
--- a/src/pretix/control/templates/pretixcontrol/organizers/giftcards.html
+++ b/src/pretix/control/templates/pretixcontrol/organizers/giftcards.html
@@ -52,8 +52,7 @@