Provide support for manipulting order status

This commit is contained in:
Raphael Michel
2015-03-20 23:53:41 +01:00
parent 5664177bbb
commit a0b1a2e11b
8 changed files with 171 additions and 7 deletions

View File

@@ -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')]),
),
]

View File

@@ -1537,6 +1537,10 @@ class Order(Versionable):
verbose_name=_("Payment information"), verbose_name=_("Payment information"),
null=True, blank=True null=True, blank=True
) )
payment_manual = models.BooleanField(
verbose_name=_("Payment state was manually modified"),
default=False
)
total = models.DecimalField( total = models.DecimalField(
decimal_places=2, max_digits=10, decimal_places=2, max_digits=10,
verbose_name=_("Total amount") verbose_name=_("Total amount")
@@ -1590,7 +1594,7 @@ class Order(Versionable):
return True return True
return False # nothing there to modify 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, Mark this order as paid. This clones the order object, sets the payment provider,
info and date and returns the cloned order object. info and date and returns the cloned order object.
@@ -1604,9 +1608,11 @@ class Order(Versionable):
:type date: datetime :type date: datetime
""" """
order = self.clone() order = self.clone()
order.payment_provider = provider order.payment_provider = provider or order.payment_provider
order.payment_info = info order.payment_info = info or order.payment_info
order.payment_date = date or now() order.payment_date = date or now()
if manual is not None:
order.payment_manual = manual
order.status = Order.STATUS_PAID order.status = Order.STATUS_PAID
order.save() order.save()
return order return order

View File

@@ -47,3 +47,10 @@ nav.navbar {
} }
} }
} }
.btn-toolbar {
margin-bottom: 20px;
}
.container-fluid > .alert:first-child {
margin-top: 20px;
}

View File

@@ -74,6 +74,13 @@
</nav> </nav>
<div id="page-wrapper"> <div id="page-wrapper">
<div class="container-fluid"> <div class="container-fluid">
{% if messages %}
{% for message in messages %}
<div class="alert {{ message.tags }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% block content %} {% block content %}
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,32 @@
{% extends "pretixcontrol/event/base.html" %}
{% load i18n %}
{% block title %}
{% trans "Cancel order" %}
{% endblock %}
{% block content %}
<h1>
{% trans "Cancel order" %}
</h1>
<p>{% blocktrans trimmed %}
Do you really want to cancel this order? You cannot revert this action.
{% endblocktrans %}</p>
<form method="post" href="">
{% csrf_token %}
<input type="hidden" name="status" value="c" />
<div class="row checkout-button-row">
<div class="col-md-4">
<a class="btn btn-block btn-default btn-lg"
href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
{% trans "No, take me back" %}
</a>
</div>
<div class="col-md-4 col-md-offset-4">
<button class="btn btn-block btn-danger btn-lg" type="submit">
{% trans "Yes, cancel order" %}
</button>
</div>
<div class="clearfix"></div>
</div>
</form>
{% endblock %}

View File

@@ -12,6 +12,27 @@
{% endblocktrans %} {% endblocktrans %}
{% include "pretixcontrol/orders/fragment_order_status.html" with order=order class="pull-right" %} {% include "pretixcontrol/orders/fragment_order_status.html" with order=order class="pull-right" %}
</h1> </h1>
{% if order.status == 'n' or order.status == 'p' %}
<form action="{% url "control:event.order.transition" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}"
method="post">
{% csrf_token %}
<div class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group">
{% if order.status == 'n' %}
<button name="status" value="p" class="btn btn-default">{% trans "Mark as paid" %}</button>
<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" %}
</a>
{% elif order.status == 'p' %}
<button name="status" value="n" class="btn btn-default">{% trans "Mark as not paid" %}</button>
<a href="{% url "control:event.order.transition" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}?status=r" class="btn btn-default">
{% trans "Refund order" %}
</a>
{% endif %}
</div>
</div>
</form>
{% endif %}
<div class="panel panel-primary items"> <div class="panel panel-primary items">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title"> <h3 class="panel-title">
@@ -86,6 +107,11 @@
</h3> </h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
{% if order.payment_manual %}
<div class="alert alert-info">
{% trans "The payment state of this order was manually modified." %}
</div>
{% endif %}
{{ payment }} {{ payment }}
{% if order.status == 'n' %} {% if order.status == 'n' %}
<p>{% blocktrans trimmed with date=order.expires %} <p>{% blocktrans trimmed with date=order.expires %}

View File

@@ -46,6 +46,8 @@ urlpatterns = [
url(r'^quotas/(?P<quota>[0-9a-f-]+)/delete$', item.QuotaDelete.as_view(), url(r'^quotas/(?P<quota>[0-9a-f-]+)/delete$', item.QuotaDelete.as_view(),
name='event.items.quotas.delete'), name='event.items.quotas.delete'),
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(),
name='event.order.transition'),
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

@@ -1,5 +1,10 @@
from itertools import groupby from itertools import groupby
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.db.models import Q 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.utils.functional import cached_property
from django.views.generic import ListView, DetailView from django.views.generic import ListView, DetailView
@@ -21,11 +26,9 @@ class OrderList(EventPermissionRequiredMixin, ListView):
).select_related("user") ).select_related("user")
class OrderDetail(EventPermissionRequiredMixin, DetailView): class OrderView(DetailView):
model = Order
context_object_name = 'order' context_object_name = 'order'
template_name = 'pretixcontrol/order/index.html' model = Order
permission = 'can_view_orders'
def get_object(self, queryset=None): def get_object(self, queryset=None):
return Order.objects.current.get( return Order.objects.current.get(
@@ -33,6 +36,15 @@ class OrderDetail(EventPermissionRequiredMixin, DetailView):
code=self.kwargs['code'].upper() 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 @cached_property
def payment_provider(self): def payment_provider(self):
responses = register_payment_providers.send(self.request.event) responses = register_payment_providers.send(self.request.event)
@@ -85,3 +97,51 @@ class OrderDetail(EventPermissionRequiredMixin, DetailView):
'total': self.object.total, 'total': self.object.total,
'payment_fee': self.object.payment_fee, '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)