Skip to content

Commit

Permalink
Refs #163 -- Form to change orders, not yet functional
Browse files Browse the repository at this point in the history
  • Loading branch information
raphaelm committed Aug 15, 2016
1 parent dadc446 commit bf6d4ee
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 9 deletions.
53 changes: 52 additions & 1 deletion src/pretix/control/forms/orders.py
@@ -1,8 +1,10 @@
from django import forms
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import ugettext_lazy as _

from pretix.base.forms import I18nModelForm
from pretix.base.models import Order
from pretix.base.models import Item, Order


class ExtendForm(I18nModelForm):
Expand Down Expand Up @@ -35,3 +37,52 @@ class Meta:
'class': 'helper-width-100',
}),
}


class OrderPositionChangeForm(forms.Form):
itemvar = forms.ChoiceField()
price = forms.DecimalField(
required=False,
max_digits=10, decimal_places=2,
label=_('New price')
)
operation = forms.ChoiceField(
required=False,
widget=forms.RadioSelect,
choices=(
('product', 'Change product'),
('price', 'Change price'),
('cancel', 'Remove product')
)
)

def __init__(self, *args, **kwargs):
instance = kwargs.pop('instance')
initial = kwargs.get('initial', {})
if instance:
try:
if instance.variation:
initial['itemvar'] = '%d-%d' % (instance.item.pk, instance.variation.pk)
elif instance.item:
initial['itemvar'] = str(instance.item.pk)
except Item.DoesNotExist:
pass

initial['price'] = instance.price

kwargs['initial'] = initial
super().__init__(*args, **kwargs)
choices = []
for i in instance.order.event.items.prefetch_related('variations').all():
variations = list(i.variations.all())
if variations:
choices.append((str(i.pk), _('{product} – Any variation').format(product=i.name)))
for v in variations:
choices.append(('%d-%d' % (i.pk, v.pk), '%s – %s' % (i.name, v.value)))
else:
choices.append((str(i.pk), i.name))
self.fields['itemvar'].choices = choices

def clean(self):
if self.cleaned_data.get('operation') == 'price' and not self.cleaned_data.get('price'):
raise ValidationError(_('You need to enter a price if you want to change the product price.'))
1 change: 0 additions & 1 deletion src/pretix/control/forms/vouchers.py
Expand Up @@ -3,7 +3,6 @@
from django import forms
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.forms import model_to_dict
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _

Expand Down
100 changes: 100 additions & 0 deletions src/pretix/control/templates/pretixcontrol/order/change.html
@@ -0,0 +1,100 @@
{% extends "pretixcontrol/event/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}
{% blocktrans trimmed with code=order.code %}
Change order: {{ code }}
{% endblocktrans %}
{% endblock %}
{% block content %}
<h1>
{% blocktrans trimmed with code=order.code %}
Change order: {{ code }}
{% endblocktrans %}
</h1>
<p>
{% blocktrans trimmed %}
You can use this tool to change the ordered products or to partially cancel the order. Please keep
in mind that changing an order can have several implications, e.g. the payment method fee might change or
additional questions can be added to the order that need to be answered by the user.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed %}
The user will receive a notification about the change but in the case of new required questions, the user
will not be forced to answer them. You cannot use this form to add something to the order, please create
a second order instead.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed %}
If an invoice is attached to the order, a cancellation will be created together with a new invoice.
{% endblocktrans %}
</p>
<div class="alert alert-warning">
{% blocktrans trimmed %}
Please use this tool carefully. Changes you make here are not reversible. In most cases it is easier to
cancel the order completely and create a new one.
{% endblocktrans %}
</div>
<form method="post" class="form-horizontal" href="">
{% csrf_token %}
{% for position in positions %}
<div class="panel panel-default items">
<div class="panel-heading">
<h3 class="panel-title">
<strong>{{ position.item.name }}</strong>
{% if position.variation %}
– {{ position.variation }}
{% endif %}
</h3>
</div>
<div class="panel-body">
<div class="form-inline form-order-change">
{% bootstrap_form_errors position.form %}
<div class="radio">
<label>
<input name="{{ position.form.prefix }}-operation" type="radio" value=""
{% if not position.form.operation.value %}checked="checked"{% endif %}>
{% trans "Keep unchanged" %}
</label>
</div>
<div class="radio">
<label>
<input name="{{ position.form.prefix }}-operation" type="radio" value="product"
{% if position.form.operation.value == "product" %}checked="checked"{% endif %}>
{% trans "Change product to" %}
{% bootstrap_field position.form.itemvar layout='inline' %}
</label>
</div>
<div class="radio">
<label>
<input name="{{ position.form.prefix }}-operation" type="radio" value="price"
{% if position.form.operation.value == "price" %}checked="checked"{% endif %}>
{% trans "Change price to" %}
{% bootstrap_field position.form.price layout='inline' %}
</label>
</div>
<div class="radio">
<label>
<input name="{{ position.form.prefix }}-operation" type="radio" value="cancel"
{% if position.form.operation.value == "cancel" %}checked="checked"{% endif %}>
{% trans "Remove from order" %}
</label>
</div>
</div>
</div>
</div>
{% endfor %}
<div class="form-group submit-group">
<a class="btn btn-default btn-lg"
href="{% url "control:event.order" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
{% trans "Cancel" %}
</a>
<button class="btn btn-danger btn-save btn-lg" type="submit">
{% trans "Save" %}
</button>
<div class="clearfix"></div>
</div>
</form>
{% endblock %}
8 changes: 8 additions & 0 deletions src/pretix/control/templates/pretixcontrol/order/index.html
Expand Up @@ -146,6 +146,14 @@ <h3 class="panel-title">
</div>
<div class="panel panel-default items">
<div class="panel-heading">
<div class="pull-right">
{% if order.status == "n" and request.eventperm.can_change_orders %}
<a href="{% url "control:event.order.change" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">
<span class="fa fa-edit"></span>
{% trans "Change products" %}
</a>
{% endif %}
</div>
<h3 class="panel-title">
{% trans "Ordered items" %}
</h3>
Expand Down
2 changes: 2 additions & 0 deletions src/pretix/control/urls.py
Expand Up @@ -79,6 +79,8 @@
name='event.order.extend'),
url(r'^orders/(?P<code>[0-9A-Z]+)/comment$', orders.OrderComment.as_view(),
name='event.order.comment'),
url(r'^orders/(?P<code>[0-9A-Z]+)/change$', orders.OrderChange.as_view(),
name='event.order.change'),
url(r'^orders/(?P<code>[0-9A-Z]+)/$', orders.OrderDetail.as_view(), name='event.order'),
url(r'^orders/(?P<code>[0-9A-Z]+)/download/(?P<output>[^/]+)$', orders.OrderDownload.as_view(),
name='event.order.download'),
Expand Down
50 changes: 43 additions & 7 deletions src/pretix/control/views/orders.py
Expand Up @@ -28,7 +28,9 @@
register_data_exporters, register_payment_providers,
register_ticket_outputs,
)
from pretix.control.forms.orders import CommentForm, ExporterForm, ExtendForm
from pretix.control.forms.orders import (
CommentForm, ExporterForm, ExtendForm, OrderPositionChangeForm,
)
from pretix.control.permissions import EventPermissionRequiredMixin
from pretix.multidomain.urlreverse import build_absolute_uri

Expand Down Expand Up @@ -74,6 +76,12 @@ def get_object(self, queryset=None):
code=self.kwargs['code'].upper()
)

def _redirect_back(self):
return redirect('control:event.order',
event=self.request.event.slug,
organizer=self.request.event.organizer.slug,
code=self.order.code)

@cached_property
def order(self):
return self.get_object()
Expand Down Expand Up @@ -431,12 +439,6 @@ def post(self, *args, **kwargs):
else:
return self.get(*args, **kwargs)

def _redirect_back(self):
return redirect('control:event.order',
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.'))
Expand All @@ -452,6 +454,40 @@ def form(self):
data=self.request.POST if self.request.method == "POST" else None)


class OrderChange(OrderView):
permission = 'can_change_orders'
template_name = 'pretixcontrol/order/change.html'

def dispatch(self, request, *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 super().dispatch(request, *args, **kwargs)

@cached_property
def positions(self):
positions = list(self.order.positions.all())
for p in positions:
p.form = OrderPositionChangeForm(prefix='op-{}'.format(p.pk), instance=p,
data=self.request.POST if self.request.method == "POST" else None)
return positions

def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['positions'] = self.positions
return ctx

def post(self, *args, **kwargs):
# check quotas
# custom prices
# vouchers
# recalculate total and payment fee
# regenerate invoice
# mail user
# cannot change free order to non-free (could never be paid)
return self.get(*args, **kwargs)


class OverView(EventPermissionRequiredMixin, TemplateView):
template_name = 'pretixcontrol/orders/overview.html'
permission = 'can_view_orders'
Expand Down
6 changes: 6 additions & 0 deletions src/static/pretixcontrol/scss/main.scss
Expand Up @@ -123,3 +123,9 @@ h1 .btn-sm {
.progress-bar-#{$i} { width: 1% * $i; }
}

.form-order-change .radio {
display: block;
}
.form-order-change .form-group {
margin: 0;
}

0 comments on commit bf6d4ee

Please sign in to comment.