diff --git a/src/pretix/base/migrations/0022_auto_20150320_2239.py b/src/pretix/base/migrations/0022_auto_20150320_2239.py new file mode 100644 index 0000000000..2acc92a0a7 --- /dev/null +++ b/src/pretix/base/migrations/0022_auto_20150320_2239.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('pretixbase', '0021_auto_20150320_1622'), + ] + + operations = [ + migrations.AddField( + model_name='order', + name='payment_manual', + field=models.BooleanField(verbose_name='Payment state was manually modified', default=False), + ), + migrations.AlterField( + model_name='order', + name='status', + field=models.CharField(verbose_name='Status', max_length=3, choices=[('n', 'pending'), ('p', 'paid'), ('e', 'expired'), ('c', 'cancelled'), ('r', 'refunded')]), + ), + ] diff --git a/src/pretix/base/models.py b/src/pretix/base/models.py index 9117f49847..5de71c9c51 100644 --- a/src/pretix/base/models.py +++ b/src/pretix/base/models.py @@ -1537,6 +1537,10 @@ class Order(Versionable): verbose_name=_("Payment information"), null=True, blank=True ) + payment_manual = models.BooleanField( + verbose_name=_("Payment state was manually modified"), + default=False + ) total = models.DecimalField( decimal_places=2, max_digits=10, verbose_name=_("Total amount") @@ -1590,7 +1594,7 @@ class Order(Versionable): return True return False # nothing there to modify - def mark_paid(self, provider, info, date=None): + def mark_paid(self, provider=None, info=None, date=None, manual=None): """ Mark this order as paid. This clones the order object, sets the payment provider, info and date and returns the cloned order object. @@ -1604,9 +1608,11 @@ class Order(Versionable): :type date: datetime """ order = self.clone() - order.payment_provider = provider - order.payment_info = info + order.payment_provider = provider or order.payment_provider + order.payment_info = info or order.payment_info order.payment_date = date or now() + if manual is not None: + order.payment_manual = manual order.status = Order.STATUS_PAID order.save() return order diff --git a/src/pretix/control/static/pretixcontrol/less/main.less b/src/pretix/control/static/pretixcontrol/less/main.less index 52417816f9..4a8d618bd7 100644 --- a/src/pretix/control/static/pretixcontrol/less/main.less +++ b/src/pretix/control/static/pretixcontrol/less/main.less @@ -47,3 +47,10 @@ nav.navbar { } } } + +.btn-toolbar { + margin-bottom: 20px; +} +.container-fluid > .alert:first-child { + margin-top: 20px; +} \ No newline at end of file diff --git a/src/pretix/control/templates/pretixcontrol/base.html b/src/pretix/control/templates/pretixcontrol/base.html index 4c9f876a21..8884592f22 100644 --- a/src/pretix/control/templates/pretixcontrol/base.html +++ b/src/pretix/control/templates/pretixcontrol/base.html @@ -74,6 +74,13 @@
{% blocktrans trimmed %} + Do you really want to cancel this order? You cannot revert this action. + {% endblocktrans %}
+ + +{% endblock %} diff --git a/src/pretix/control/templates/pretixcontrol/order/index.html b/src/pretix/control/templates/pretixcontrol/order/index.html index ffaead64bc..f6a2841fdc 100644 --- a/src/pretix/control/templates/pretixcontrol/order/index.html +++ b/src/pretix/control/templates/pretixcontrol/order/index.html @@ -12,6 +12,27 @@ {% endblocktrans %} {% include "pretixcontrol/orders/fragment_order_status.html" with order=order class="pull-right" %} + {% if order.status == 'n' or order.status == 'p' %} + + {% endif %}{% blocktrans trimmed with date=order.expires %}
diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py
index c12abb346f..35bc0eff80 100644
--- a/src/pretix/control/urls.py
+++ b/src/pretix/control/urls.py
@@ -46,6 +46,8 @@ urlpatterns = [
url(r'^quotas/(?P[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(),
+ name='event.order.transition'),
url(r'^orders/(?P[0-9A-Z]+)/$', orders.OrderDetail.as_view(), name='event.order'),
url(r'^orders/$', orders.OrderList.as_view(), name='event.orders'),
])),
diff --git a/src/pretix/control/views/orders.py b/src/pretix/control/views/orders.py
index c04b37d39e..b6afbd8ae5 100644
--- a/src/pretix/control/views/orders.py
+++ b/src/pretix/control/views/orders.py
@@ -1,5 +1,10 @@
from itertools import groupby
+from django.contrib import messages
+from django.core.urlresolvers import reverse
from django.db.models import Q
+from django.utils.translation import ugettext_lazy as _
+from django.http import HttpResponse
+from django.shortcuts import redirect, render
from django.utils.functional import cached_property
from django.views.generic import ListView, DetailView
@@ -21,11 +26,9 @@ class OrderList(EventPermissionRequiredMixin, ListView):
).select_related("user")
-class OrderDetail(EventPermissionRequiredMixin, DetailView):
- model = Order
+class OrderView(DetailView):
context_object_name = 'order'
- template_name = 'pretixcontrol/order/index.html'
- permission = 'can_view_orders'
+ model = Order
def get_object(self, queryset=None):
return Order.objects.current.get(
@@ -33,6 +36,15 @@ class OrderDetail(EventPermissionRequiredMixin, DetailView):
code=self.kwargs['code'].upper()
)
+ @cached_property
+ def order(self):
+ return self.get_object()
+
+
+class OrderDetail(EventPermissionRequiredMixin, OrderView):
+ template_name = 'pretixcontrol/order/index.html'
+ permission = 'can_view_orders'
+
@cached_property
def payment_provider(self):
responses = register_payment_providers.send(self.request.event)
@@ -85,3 +97,51 @@ class OrderDetail(EventPermissionRequiredMixin, DetailView):
'total': self.object.total,
'payment_fee': self.object.payment_fee,
}
+
+
+class OrderTransition(EventPermissionRequiredMixin, OrderView):
+ permission = 'can_view_orders'
+
+ def post(self, *args, **kwargs):
+ to = self.request.POST.get('status', '')
+ if self.order.status == 'n' and to == 'p':
+ self.order.mark_paid(manual=True)
+ messages.success(self.request, _('The order has been marked as paid.'))
+ elif self.order.status == 'n' and to == 'c':
+ order = self.order.clone()
+ order.status = Order.STATUS_CANCELLED
+ order.save()
+ messages.success(self.request, _('The order has been cancelled.'))
+ elif self.order.status == 'p' and to == 'n':
+ order = self.order.clone()
+ order.status = Order.STATUS_PENDING
+ order.payment_manual = True
+ order.save()
+ messages.success(self.request, _('The order has been marked as not paid.'))
+ 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):
+ to = self.request.GET.get('status', '')
+ if self.order.status == 'n' and to == 'c':
+ return render(self.request, 'pretixcontrol/order/cancel.html', {
+ 'order': self.order,
+ })
+ elif self.order.status == 'p' and to == 'r':
+ messages.error(self.request, _('Refunding orders is not yet implemented.'))
+ return redirect(reverse(
+ 'control:event.order',
+ kwargs={
+ 'event': self.request.event.slug,
+ 'organizer': self.request.event.organizer.slug,
+ 'code': self.order.code,
+ }
+ ))
+ else:
+ return HttpResponse(status=405)