diff --git a/src/pretix/base/migrations/0003_auto_20150602_2232.py b/src/pretix/base/migrations/0003_auto_20150602_2232.py
new file mode 100644
index 0000000000..8a055357c0
--- /dev/null
+++ b/src/pretix/base/migrations/0003_auto_20150602_2232.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+import django.core.validators
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('pretixbase', '0002_auto_20150524_1148'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='item',
+ options={'verbose_name_plural': 'Products', 'ordering': ('category__position', 'category', 'position'), 'verbose_name': 'Product'},
+ ),
+ migrations.AddField(
+ model_name='item',
+ name='position',
+ field=models.IntegerField(default=0),
+ ),
+ migrations.AlterField(
+ model_name='organizer',
+ name='slug',
+ field=models.SlugField(help_text='Should be short, only contain lowercase letters and numbers, and must be unique among your events. This is being used in addresses and bank transfer references.', validators=[django.core.validators.RegexValidator(message='The slug may only contain letters, numbers, dots and dashes.', regex='^[a-zA-Z0-9.-]+$')], verbose_name='Slug'),
+ ),
+ ]
diff --git a/src/pretix/base/models.py b/src/pretix/base/models.py
index 48089bfe74..2c71a9e29d 100644
--- a/src/pretix/base/models.py
+++ b/src/pretix/base/models.py
@@ -860,10 +860,14 @@ class Item(Versionable):
),
default=False
)
+ position = models.IntegerField(
+ default=0
+ )
class Meta:
verbose_name = _("Product")
verbose_name_plural = _("Products")
+ ordering = ("category__position", "category", "position")
def __str__(self):
return str(self.name)
diff --git a/src/pretix/control/templates/pretixcontrol/items/index.html b/src/pretix/control/templates/pretixcontrol/items/index.html
index 6b9afc8bfd..85ad40cbad 100644
--- a/src/pretix/control/templates/pretixcontrol/items/index.html
+++ b/src/pretix/control/templates/pretixcontrol/items/index.html
@@ -3,6 +3,13 @@
{% block title %}{% trans "Products" %}{% endblock %}
{% block inside %}
{% trans "Products" %}
+
+ {% blocktrans trimmed %}
+ Below, you find a list of all available products. You can click on a product name to inspect and change
+ product details. You can also use the buttons on the right to change the order of products within a
+ give category.
+ {% endblocktrans %}
+
{% trans "Create new product" %}
@@ -12,16 +19,24 @@
| {% trans "Product name" %} |
{% trans "Category" %} |
+ |
- {% for i in items %}
-
- | {{ i.name }} |
- {% if i.category %}{{ i.category.name }}{% endif %} |
-
- {% endfor %}
+ {% regroup items by category as cat_list %}
+ {% for c in cat_list %}
+ {% for i in c.list %}
+
+ | {{ i.name }} |
+ {% if i.category %}{{ i.category.name }}{% endif %} |
+
+
+
+ |
+
+ {% endfor %}
+ {% endfor %}
{% include "pretixcontrol/pagination.html" %}
diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py
index 8662439d3f..633ef415dd 100644
--- a/src/pretix/control/urls.py
+++ b/src/pretix/control/urls.py
@@ -27,6 +27,8 @@ urlpatterns = [
name='event.item.variations'),
url(r'^items/(?P- [0-9a-f-]+)/restrictions$', item.ItemRestrictions.as_view(),
name='event.item.restrictions'),
+ url(r'^items/(?P
- [0-9a-f-]+)/up$', item.item_move_up, name='event.items.up'),
+ url(r'^items/(?P
- [0-9a-f-]+)/down$', item.item_move_down, name='event.items.down'),
url(r'^categories/$', item.CategoryList.as_view(), name='event.items.categories'),
url(r'^categories/(?P[0-9a-f-]+)/delete$', item.CategoryDelete.as_view(),
name='event.items.categories.delete'),
diff --git a/src/pretix/control/views/item.py b/src/pretix/control/views/item.py
index 9c744f8430..bb3a91ff1f 100644
--- a/src/pretix/control/views/item.py
+++ b/src/pretix/control/views/item.py
@@ -27,7 +27,9 @@ from . import UpdateView, CreateView
class ItemList(ListView):
model = Item
context_object_name = 'items'
- paginate_by = 30
+ # paginate_by = 30
+ # Pagination is disabled as it is very unlikely to be necessary
+ # here and could cause problems with the "reorder-within-category" feature
template_name = 'pretixcontrol/items/index.html'
def get_queryset(self):
@@ -36,6 +38,49 @@ class ItemList(ListView):
).prefetch_related("category")
+def item_move(request, item, up=True):
+ """
+ This is a helper function to avoid duplicating code in item_move_up and
+ item_move_down. It takes an item and a direction and then tries to bring
+ all items for this category in a new order.
+ """
+ try:
+ item = request.event.items.current.get(
+ identity=item
+ )
+ except Item.DoesNotExist:
+ raise Http404(_("The requested product does not exist."))
+ items = list(request.event.items.current.filter(category=item.category).order_by("position"))
+
+ index = items.index(item)
+ if index != 0 and up:
+ items[index - 1], items[index] = items[index], items[index - 1]
+ elif index != len(items) - 1 and not up:
+ items[index + 1], items[index] = items[index], items[index + 1]
+
+ for i, item in enumerate(items):
+ if item.position != i:
+ item.position = i
+ item.save() # TODO: Clone or document sloppiness?
+ messages.success(request, _('The order of items as been updated.'))
+
+
+@event_permission_required("can_change_items")
+def item_move_up(request, organizer, event, item):
+ item_move(request, item, up=True)
+ return redirect('control:event.items',
+ organizer=request.event.organizer.slug,
+ event=request.event.slug)
+
+
+@event_permission_required("can_change_items")
+def item_move_down(request, organizer, event, item):
+ item_move(request, item, up=False)
+ return redirect('control:event.items',
+ organizer=request.event.organizer.slug,
+ event=request.event.slug)
+
+
class CategoryForm(VersionedModelForm):
class Meta:
@@ -159,6 +204,7 @@ def category_move(request, category, up=True):
if cat.position != i:
cat.position = i
cat.save() # TODO: Clone or document sloppiness?
+ messages.success(request, _('The order of categories as been updated.'))
@event_permission_required("can_change_items")
diff --git a/src/pretix/presale/views/event.py b/src/pretix/presale/views/event.py
index 3d9b942316..7714258e27 100644
--- a/src/pretix/presale/views/event.py
+++ b/src/pretix/presale/views/event.py
@@ -35,7 +35,7 @@ class EventIndex(EventViewMixin, CartDisplayMixin, TemplateView):
'quotas', 'variations__quotas', 'quotas__event' # for .availability()
).annotate(quotac=Count('quotas')).filter(
quotac__gt=0
- ).order_by('category__position', 'category_id', 'name')
+ ).order_by('category__position', 'category_id', 'position', 'name')
for item in items:
item.available_variations = sorted(item.get_all_available_variations(),