mirror of
https://github.com/pretix/pretix.git
synced 2026-05-07 15:34:02 +00:00
Allow to adjust the order of products (closes #60)
This commit is contained in:
29
src/pretix/base/migrations/0003_auto_20150602_2232.py
Normal file
29
src/pretix/base/migrations/0003_auto_20150602_2232.py
Normal file
@@ -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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -860,10 +860,14 @@ class Item(Versionable):
|
|||||||
),
|
),
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
position = models.IntegerField(
|
||||||
|
default=0
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Product")
|
verbose_name = _("Product")
|
||||||
verbose_name_plural = _("Products")
|
verbose_name_plural = _("Products")
|
||||||
|
ordering = ("category__position", "category", "position")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.name)
|
return str(self.name)
|
||||||
|
|||||||
@@ -3,6 +3,13 @@
|
|||||||
{% block title %}{% trans "Products" %}{% endblock %}
|
{% block title %}{% trans "Products" %}{% endblock %}
|
||||||
{% block inside %}
|
{% block inside %}
|
||||||
<h1>{% trans "Products" %}</h1>
|
<h1>{% trans "Products" %}</h1>
|
||||||
|
<p>
|
||||||
|
{% 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 %}
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="{% url "control:event.items.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
<a href="{% url "control:event.items.add" organizer=request.event.organizer.slug event=request.event.slug %}"
|
||||||
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create new product" %}</a>
|
class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create new product" %}</a>
|
||||||
@@ -12,16 +19,24 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Product name" %}</th>
|
<th>{% trans "Product name" %}</th>
|
||||||
<th>{% trans "Category" %}</th>
|
<th>{% trans "Category" %}</th>
|
||||||
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for i in items %}
|
{% regroup items by category as cat_list %}
|
||||||
<tr>
|
{% for c in cat_list %}
|
||||||
<td><strong><a href="
|
{% for i in c.list %}
|
||||||
{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}">{{ i.name }}</a></strong></td>
|
<tr>
|
||||||
<td>{% if i.category %}{{ i.category.name }}{% endif %}</td>
|
<td><strong><a href="
|
||||||
</tr>
|
{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}">{{ i.name }}</a></strong></td>
|
||||||
{% endfor %}
|
<td>{% if i.category %}{{ i.category.name }}{% endif %}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{% url "control:event.items.up" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}" class="btn btn-default btn-sm {% if forloop.counter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-up"></i></a>
|
||||||
|
<a href="{% url "control:event.items.down" organizer=request.event.organizer.slug event=request.event.slug item=i.identity %}" class="btn btn-default btn-sm {% if forloop.revcounter0 == 0 %}disabled{% endif %}"><i class="fa fa-arrow-down"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% include "pretixcontrol/pagination.html" %}
|
{% include "pretixcontrol/pagination.html" %}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ urlpatterns = [
|
|||||||
name='event.item.variations'),
|
name='event.item.variations'),
|
||||||
url(r'^items/(?P<item>[0-9a-f-]+)/restrictions$', item.ItemRestrictions.as_view(),
|
url(r'^items/(?P<item>[0-9a-f-]+)/restrictions$', item.ItemRestrictions.as_view(),
|
||||||
name='event.item.restrictions'),
|
name='event.item.restrictions'),
|
||||||
|
url(r'^items/(?P<item>[0-9a-f-]+)/up$', item.item_move_up, name='event.items.up'),
|
||||||
|
url(r'^items/(?P<item>[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/$', item.CategoryList.as_view(), name='event.items.categories'),
|
||||||
url(r'^categories/(?P<category>[0-9a-f-]+)/delete$', item.CategoryDelete.as_view(),
|
url(r'^categories/(?P<category>[0-9a-f-]+)/delete$', item.CategoryDelete.as_view(),
|
||||||
name='event.items.categories.delete'),
|
name='event.items.categories.delete'),
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ from . import UpdateView, CreateView
|
|||||||
class ItemList(ListView):
|
class ItemList(ListView):
|
||||||
model = Item
|
model = Item
|
||||||
context_object_name = 'items'
|
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'
|
template_name = 'pretixcontrol/items/index.html'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
@@ -36,6 +38,49 @@ class ItemList(ListView):
|
|||||||
).prefetch_related("category")
|
).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 CategoryForm(VersionedModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -159,6 +204,7 @@ def category_move(request, category, up=True):
|
|||||||
if cat.position != i:
|
if cat.position != i:
|
||||||
cat.position = i
|
cat.position = i
|
||||||
cat.save() # TODO: Clone or document sloppiness?
|
cat.save() # TODO: Clone or document sloppiness?
|
||||||
|
messages.success(request, _('The order of categories as been updated.'))
|
||||||
|
|
||||||
|
|
||||||
@event_permission_required("can_change_items")
|
@event_permission_required("can_change_items")
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class EventIndex(EventViewMixin, CartDisplayMixin, TemplateView):
|
|||||||
'quotas', 'variations__quotas', 'quotas__event' # for .availability()
|
'quotas', 'variations__quotas', 'quotas__event' # for .availability()
|
||||||
).annotate(quotac=Count('quotas')).filter(
|
).annotate(quotac=Count('quotas')).filter(
|
||||||
quotac__gt=0
|
quotac__gt=0
|
||||||
).order_by('category__position', 'category_id', 'name')
|
).order_by('category__position', 'category_id', 'position', 'name')
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
item.available_variations = sorted(item.get_all_available_variations(),
|
item.available_variations = sorted(item.get_all_available_variations(),
|
||||||
|
|||||||
Reference in New Issue
Block a user