Allow to change payment term (closes #10)

This commit is contained in:
Raphael Michel
2015-04-14 18:00:00 +02:00
parent df524f31d5
commit 42ecd9c5f9
4 changed files with 91 additions and 17 deletions

View File

@@ -1568,22 +1568,27 @@ class Order(Versionable):
order.save() order.save()
return order return order
def _can_be_paid(self): def _can_be_paid(self, keep_locked=False):
error_messages = { error_messages = {
'unavailable': _('Some of the ordered products were no longer available.'),
'busy': _('We were not able to process the request completely as the '
'server was too busy. Please try again.'),
'late': _("The payment is too late to be accepted."), 'late': _("The payment is too late to be accepted."),
} }
if self.event.settings.get('payment_term_last') \ if self.event.settings.get('payment_term_last') \
and now() > self.event.settings.get('payment_term_last'): and now() > self.event.settings.get('payment_term_last'):
return error_messages['late'] return error_messages['late'], None
if now() < self.expires: if now() < self.expires:
return True return True, None
if not self.event.settings.get('payment_term_accept_late'): if not self.event.settings.get('payment_term_accept_late'):
return error_messages['late'] return error_messages['late'], None
return self._is_still_available(keep_locked)
def _is_still_available(self, keep_locked=False):
error_messages = {
'unavailable': _('Some of the ordered products were no longer available.'),
'busy': _('We were not able to process the request completely as the '
'server was too busy. Please try again.'),
}
positions = list(self.positions.all().select_related( positions = list(self.positions.all().select_related(
'item', 'variation' 'item', 'variation'
).prefetch_related( ).prefetch_related(
@@ -1591,6 +1596,7 @@ class Order(Versionable):
'item__questions', 'answers' 'item__questions', 'answers'
)) ))
quotas_locked = set() quotas_locked = set()
release = True
try: try:
for i, op in enumerate(positions): for i, op in enumerate(positions):
@@ -1613,16 +1619,19 @@ class Order(Versionable):
# This quota is sold out/currently unavailable, so do not sell this at all # This quota is sold out/currently unavailable, so do not sell this at all
raise Quota.QuotaExceededException(error_messages['unavailable']) raise Quota.QuotaExceededException(error_messages['unavailable'])
except Quota.QuotaExceededException as e: except Quota.QuotaExceededException as e:
return str(e) return str(e), None
except Quota.LockTimeoutException: except Quota.LockTimeoutException:
# Is raised when there are too many threads asking for quota locks and we were # Is raised when there are too many threads asking for quota locks and we were
# unaible to get one # unaible to get one
return error_messages['busy'] return error_messages['busy'], None
else:
release = False
finally: finally:
# Release the locks. This is important ;) # Release the locks. This is important ;)
for quota in quotas_locked: if release or not keep_locked:
quota.release() for quota in quotas_locked:
return True quota.release()
return True, quotas_locked
def mark_paid(self, provider=None, info=None, date=None, manual=None, force=False): def mark_paid(self, provider=None, info=None, date=None, manual=None, force=False):
""" """
@@ -1641,7 +1650,7 @@ class Order(Versionable):
:type force: boolean :type force: boolean
:raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False`` :raises Quota.QuotaExceededException: if the quota is exceeded and ``force`` is ``False``
""" """
can_be_paid = self._can_be_paid() can_be_paid, quotas_locked = self._can_be_paid(keep_locked=True)
if not force and can_be_paid is not True: if not force and can_be_paid is not True:
raise Quota.QuotaExceededException(can_be_paid) raise Quota.QuotaExceededException(can_be_paid)
order = self.clone() order = self.clone()
@@ -1653,6 +1662,10 @@ class Order(Versionable):
order.status = Order.STATUS_PAID order.status = Order.STATUS_PAID
order.save() order.save()
if quotas_locked:
for quota in quotas_locked:
quota.release()
from pretix.base.mail import mail from pretix.base.mail import mail
mail( mail(
order.user, _('Payment received for your order: %(code)s') % {'code': order.code}, order.user, _('Payment received for your order: %(code)s') % {'code': order.code},

View File

@@ -20,6 +20,9 @@
<div class="btn-group" role="group"> <div class="btn-group" role="group">
{% if order.status == 'n' %} {% if order.status == 'n' %}
<button name="status" value="p" class="btn btn-default">{% trans "Mark as paid" %}</button> <button name="status" value="p" class="btn btn-default">{% trans "Mark as paid" %}</button>
<a href="{% url "control:event.order.extend" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}" class="btn btn-default">
{% trans "Extend payment term" %}
</a>
<a href="{% url "control:event.order.transition" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}?status=c" class="btn btn-default"> <a href="{% url "control:event.order.transition" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}?status=c" class="btn btn-default">
{% trans "Cancel order" %} {% trans "Cancel order" %}
</a> </a>

View File

@@ -48,6 +48,8 @@ urlpatterns = [
url(r'^quotas/add$', item.QuotaCreate.as_view(), name='event.items.quotas.add'), url(r'^quotas/add$', item.QuotaCreate.as_view(), name='event.items.quotas.add'),
url(r'^orders/(?P<code>[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(), url(r'^orders/(?P<code>[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(),
name='event.order.transition'), name='event.order.transition'),
url(r'^orders/(?P<code>[0-9A-Z]+)/extend$', orders.OrderExtend.as_view(),
name='event.order.extend'),
url(r'^orders/(?P<code>[0-9A-Z]+)/$', orders.OrderDetail.as_view(), name='event.order'), url(r'^orders/(?P<code>[0-9A-Z]+)/$', orders.OrderDetail.as_view(), name='event.order'),
url(r'^orders/$', orders.OrderList.as_view(), name='event.orders'), url(r'^orders/$', orders.OrderList.as_view(), name='event.orders'),
])), ])),

View File

@@ -2,11 +2,13 @@ from itertools import groupby
from django.contrib import messages from django.contrib import messages
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db.models import Q from django.db.models import Q
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.views.generic import ListView, DetailView from django.views.generic import ListView, DetailView
from pretix.base.forms import VersionedModelForm
from pretix.base.models import Order, Quota from pretix.base.models import Order, Quota
from pretix.base.signals import register_payment_providers from pretix.base.signals import register_payment_providers
@@ -26,7 +28,7 @@ class OrderList(EventPermissionRequiredMixin, ListView):
).select_related("user") ).select_related("user")
class OrderView(DetailView): class OrderView(EventPermissionRequiredMixin, DetailView):
context_object_name = 'order' context_object_name = 'order'
model = Order model = Order
@@ -49,7 +51,13 @@ class OrderView(DetailView):
return provider return provider
class OrderDetail(EventPermissionRequiredMixin, OrderView): class ExtendForm(VersionedModelForm):
class Meta:
model = Order
fields = ['expires']
class OrderDetail(OrderView):
template_name = 'pretixcontrol/order/index.html' template_name = 'pretixcontrol/order/index.html'
permission = 'can_view_orders' permission = 'can_view_orders'
@@ -99,8 +107,8 @@ class OrderDetail(EventPermissionRequiredMixin, OrderView):
} }
class OrderTransition(EventPermissionRequiredMixin, OrderView): class OrderTransition(OrderView):
permission = 'can_view_orders' permission = 'can_change_orders'
def post(self, *args, **kwargs): def post(self, *args, **kwargs):
to = self.request.POST.get('status', '') to = self.request.POST.get('status', '')
@@ -148,3 +156,51 @@ class OrderTransition(EventPermissionRequiredMixin, OrderView):
}) })
else: else:
return HttpResponse(status=405) return HttpResponse(status=405)
class OrderExtend(OrderView):
permission = 'can_change_orders'
def post(self, *args, **kwargs):
if self.order.status != Order.STATUS_PENDING:
messages.error(self.request, _('This action is only allowed for pending orders.'))
return self._redirect_back()
oldvalue = self.order.expires
if self.form.is_valid():
if oldvalue > now():
self.form.save()
else:
is_available, _quotas_locked = self.order._is_still_available(keep_locked=False)
if is_available is True:
self.form.save()
messages.success(self.request, _('The payment term has been changed.'))
else:
messages.error(self.request, is_available)
return self._redirect_back()
else:
return self.get(*args, **kwargs)
def _redirect_back(self):
return redirect(reverse(
'control:event.order',
kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
'code': self.order.code,
}
))
def get(self, *args, **kwargs):
if self.order.status != Order.STATUS_PENDING:
messages.error(self.request, _('This action is only allowed for pending orders.'))
return self._redirect_back()
return render(self.request, 'pretixcontrol/order/extend.html', {
'order': self.order,
'form': self.form,
})
@cached_property
def form(self):
return ExtendForm(instance=self.order,
data=self.request.POST if self.request.method == "POST" else None)