mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
Allow to change payment term (closes #10)
This commit is contained in:
@@ -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},
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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'),
|
||||||
])),
|
])),
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user