Refs #118 -- Asynchronous order cancelling

This commit is contained in:
Raphael Michel
2016-03-20 10:40:18 +01:00
parent 0044078ec5
commit 0355d22114
4 changed files with 73 additions and 23 deletions

View File

@@ -94,39 +94,51 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
@transaction.atomic @transaction.atomic
def mark_order_refunded(order: Order, user: User=None): def mark_order_refunded(order, user=None):
""" """
Mark this order as refunded. This sets the payment status and returns the order object. Mark this order as refunded. This sets the payment status and returns the order object.
:param order: The order to change :param order: The order to change
:param user: The user that performed the change :param user: The user that performed the change
""" """
order.status = Order.STATUS_REFUNDED if isinstance(order, int):
order.save() order = Order.objects.get(pk=order)
order.log_action('pretix.event.order.refunded', user=user) if isinstance(user, int):
user = User.objects.get(pk=user)
with order.event.lock():
order.status = Order.STATUS_REFUNDED
order.save()
order.log_action('pretix.event.order.refunded', user=user)
i = order.invoices.filter(is_cancellation=False).last() i = order.invoices.filter(is_cancellation=False).last()
if i: if i:
generate_cancellation(i) generate_cancellation(i)
return order return order
@transaction.atomic @transaction.atomic
def cancel_order(order: Order, user: User=None): def cancel_order(order, user=None):
""" """
Mark this order as canceled Mark this order as canceled
:param order: The order to change :param order: The order to change
:param user: The user that performed the change :param user: The user that performed the change
""" """
order.status = Order.STATUS_CANCELLED if isinstance(order, int):
order.save() order = Order.objects.get(pk=order)
order.log_action('pretix.event.order.cancelled', user=user) 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):
raise OrderError(_('You cannot cancel this order'))
order.status = Order.STATUS_CANCELLED
order.save()
order.log_action('pretix.event.order.cancelled', user=user)
i = order.invoices.filter(is_cancellation=False).last() i = order.invoices.filter(is_cancellation=False).last()
if i: if i:
generate_cancellation(i) generate_cancellation(i)
return order return order
class OrderError(Exception): class OrderError(Exception):
@@ -310,4 +322,12 @@ if settings.HAS_CELERY:
except EventLock.LockTimeoutException: except EventLock.LockTimeoutException:
self.retry(exc=OrderError(error_messages['busy'])) self.retry(exc=OrderError(error_messages['busy']))
@app.task(bind=True, max_retries=5, default_retry_delay=2)
def cancel_order_task(self, order: int, user: int=None):
try:
return cancel_order(order, user)
except EventLock.LockTimeoutException:
self.retry(exc=OrderError(error_messages['busy']))
perform_order.task = perform_order_task perform_order.task = perform_order_task
cancel_order.task = cancel_order_task

View File

@@ -12,7 +12,7 @@
Do you really want to cancel this order? You cannot revert this action. Do you really want to cancel this order? You cannot revert this action.
{% endblocktrans %}</p> {% endblocktrans %}</p>
<form method="post" href=""> <form method="post" action="{% eventurl request.event "presale:event.order.cancel.do" secret=order.secret order=order.code %}" data-asynctask>
{% csrf_token %} {% csrf_token %}
<div class="row checkout-button-row"> <div class="row checkout-button-row">
<div class="col-md-4"> <div class="col-md-4">

View File

@@ -21,6 +21,9 @@ event_patterns = [
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/cancel$', url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/cancel$',
pretix.presale.views.order.OrderCancel.as_view(), pretix.presale.views.order.OrderCancel.as_view(),
name='event.order.cancel'), name='event.order.cancel'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/cancel/do$',
pretix.presale.views.order.OrderCancelDo.as_view(),
name='event.order.cancel.do'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/modify$', url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/modify$',
pretix.presale.views.order.OrderModify.as_view(), pretix.presale.views.order.OrderModify.as_view(),
name='event.order.modify'), name='event.order.modify'),

View File

@@ -6,7 +6,7 @@ from django.http import Http404
from django.shortcuts import redirect from django.shortcuts import redirect
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _, gettext
from django.views.generic import TemplateView, View from django.views.generic import TemplateView, View
from pretix.base.models import ( from pretix.base.models import (
@@ -14,7 +14,7 @@ from pretix.base.models import (
) )
from pretix.base.models.orders import InvoiceAddress from pretix.base.models.orders import InvoiceAddress
from pretix.base.services.invoices import invoice_pdf from pretix.base.services.invoices import invoice_pdf
from pretix.base.services.orders import cancel_order from pretix.base.services.orders import cancel_order, OrderError
from pretix.base.services.tickets import generate from pretix.base.services.tickets import generate
from pretix.base.signals import ( from pretix.base.signals import (
register_payment_providers, register_ticket_outputs, register_payment_providers, register_ticket_outputs,
@@ -22,6 +22,7 @@ from pretix.base.signals import (
from pretix.multidomain.urlreverse import eventreverse from pretix.multidomain.urlreverse import eventreverse
from pretix.presale.forms.checkout import InvoiceAddressForm from pretix.presale.forms.checkout import InvoiceAddressForm
from pretix.presale.views import CartMixin, EventViewMixin from pretix.presale.views import CartMixin, EventViewMixin
from pretix.presale.views.async import AsyncAction
from pretix.presale.views.questions import QuestionsViewMixin from pretix.presale.views.questions import QuestionsViewMixin
@@ -270,10 +271,6 @@ class OrderCancel(EventViewMixin, OrderDetailMixin, TemplateView):
return redirect(self.get_order_url()) return redirect(self.get_order_url())
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
cancel_order(self.order)
return redirect(self.get_order_url())
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
@@ -283,6 +280,36 @@ class OrderCancel(EventViewMixin, OrderDetailMixin, TemplateView):
return ctx return ctx
class OrderCancelDo(EventViewMixin, OrderDetailMixin, AsyncAction, View):
task = cancel_order
def get_success_url(self, value):
return self.get_order_url()
def get_error_url(self):
return self.get_order_url()
def post(self, request, *args, **kwargs):
if not self.order:
raise Http404(_('Unknown order code or not authorized to access this order.'))
return self.do(self.order)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['order'] = self.order
return ctx
def get_success_message(self, value):
return _('The order has been cancelled.')
def get_error_message(self, exception):
if isinstance(exception, dict) and exception['exc_type'] == 'OrderError':
return gettext(exception['exc_message'])
elif isinstance(exception, OrderError):
return str(exception)
return super().get_error_message(exception)
class OrderDownload(EventViewMixin, OrderDetailMixin, View): class OrderDownload(EventViewMixin, OrderDetailMixin, View):
@cached_property @cached_property
def output(self): def output(self):