diff --git a/src/pretix/base/models/vouchers.py b/src/pretix/base/models/vouchers.py index 104d2088a7..8c5a72760d 100644 --- a/src/pretix/base/models/vouchers.py +++ b/src/pretix/base/models/vouchers.py @@ -8,7 +8,7 @@ from django.utils.translation import ugettext_lazy as _ from .base import LoggedModel from .event import Event from .items import Item, ItemVariation, Quota -from .orders import CartPosition, OrderPosition +from .orders import CartPosition, Order, OrderPosition def generate_code(): diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py index 935d4023d3..ea6a5a3869 100644 --- a/src/pretix/base/services/orders.py +++ b/src/pretix/base/services/orders.py @@ -142,7 +142,7 @@ def cancel_order(order, user=None): if isinstance(user, int): user = User.objects.get(pk=user) with order.event.lock(): - if order.status not in (Order.STATUS_PENDING, Order.STATUS_EXPIRED): + if order.status != Order.STATUS_PENDING: raise OrderError(_('You cannot cancel this order.')) order.status = Order.STATUS_CANCELLED order.save() diff --git a/src/pretix/presale/views/order.py b/src/pretix/presale/views/order.py index ebe5ff7c3c..b26c534575 100644 --- a/src/pretix/presale/views/order.py +++ b/src/pretix/presale/views/order.py @@ -295,7 +295,7 @@ class OrderCancel(EventViewMixin, OrderDetailMixin, TemplateView): self.kwargs = kwargs if not self.order: raise Http404(_('Unknown order code or not authorized to access this order.')) - if self.order.status not in (Order.STATUS_PENDING, Order.STATUS_EXPIRED) or not self.order.can_user_cancel: + if self.order.status != Order.STATUS_PENDING or not self.order.can_user_cancel: messages.error(request, _('You cannot cancel this order.')) return redirect(self.get_order_url()) return super().dispatch(request, *args, **kwargs) diff --git a/src/tests/base/test_models.py b/src/tests/base/test_models.py index 4fe5f2a396..518b59aaf6 100644 --- a/src/tests/base/test_models.py +++ b/src/tests/base/test_models.py @@ -10,7 +10,9 @@ from pretix.base.models import ( CachedFile, CartPosition, Event, Item, ItemCategory, ItemVariation, Order, OrderPosition, Organizer, Question, Quota, User, Voucher, ) -from pretix.base.services.orders import mark_order_paid +from pretix.base.services.orders import ( + OrderError, cancel_order, mark_order_paid, perform_order, +) class UserTestCase(TestCase): @@ -278,6 +280,26 @@ class QuotaTestCase(BaseQuotaTestCase): self.assertEqual(self.quota.count_in_cart(), 1) self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1)) + def test_voucher_reuse(self): + self.quota.items.add(self.item1) + v = Voucher.objects.create(quota=self.quota, event=self.event, valid_until=now() + timedelta(days=5)) + + # use a voucher normally + cart = CartPosition.objects.create(event=self.event, item=self.item1, price=self.item1.default_price, + expires=now() + timedelta(days=3), voucher=v) + order = perform_order(event=self.event.id, payment_provider='free', positions=[cart.id]) + + # assert that the voucher cannot be reused + cart = CartPosition.objects.create(event=self.event, item=self.item1, price=self.item1.default_price, + expires=now() + timedelta(days=3), voucher=v) + self.assertRaises(OrderError, perform_order, event=self.event.id, payment_provider='free', positions=[cart.id]) + + # assert that the voucher can be re-used after cancelling the successful order + cancel_order(order) + cart = CartPosition.objects.create(event=self.event, item=self.item1, price=self.item1.default_price, + expires=now() + timedelta(days=3), voucher=v) + perform_order(event=self.event.id, payment_provider='free', positions=[cart.id]) + class OrderTestCase(BaseQuotaTestCase): def setUp(self):