forked from CGM_Public/pretix_original
Compare commits
10 Commits
fix-invoic
...
list-sorti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c599ab056 | ||
|
|
b2a0893e63 | ||
|
|
4945003f32 | ||
|
|
b7cd10308f | ||
|
|
58a9a25515 | ||
|
|
9e23db1660 | ||
|
|
2d044b0b02 | ||
|
|
93a9528d66 | ||
|
|
578666f8e8 | ||
|
|
46ec507442 |
@@ -122,6 +122,16 @@ class ItemCategory(LoggedModel):
|
||||
return _('{category} (Add-On products)').format(category=str(name))
|
||||
return str(name)
|
||||
|
||||
def get_category_type_display(self):
|
||||
if self.is_addon:
|
||||
return _('Add-On products')
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def category_type(self):
|
||||
return 'addon' if self.is_addon else 'normal'
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
if self.event:
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
<tr>
|
||||
<th>{% trans "Product categories" %}</th>
|
||||
<th class="action-col-2"></th>
|
||||
<th class="action-col-2"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-dnd-url="{% url "control:event.items.categories.reorder" organizer=request.event.organizer.slug event=request.event.slug %}">
|
||||
@@ -41,18 +40,16 @@
|
||||
<td>
|
||||
<strong><a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}">{{ c.internal_name|default:c.name }}</a></strong>
|
||||
</td>
|
||||
<td>
|
||||
<button formaction="{% url "control:event.items.categories.up" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 and not page_obj.has_previous %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button formaction="{% url "control:event.items.categories.down" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container"></span>
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<button title="{% trans "Move up" %}" formaction="{% url "control:event.items.categories.up" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 and not page_obj.has_previous %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button title="{% trans "Move down" %}" formaction="{% url "control:event.items.categories.down" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<a title="{% trans "Edit" %}" href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.categories.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ c.id }}"
|
||||
class="btn btn-sm btn-default" title="{% trans "Clone" %}" data-toggle="tooltip">
|
||||
<span class="fa fa-copy"></span>
|
||||
</a>
|
||||
<a href="{% url "control:event.items.categories.delete" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
<a title="{% trans "Delete" %}" href="{% url "control:event.items.categories.delete" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -56,8 +56,7 @@
|
||||
<th>{% trans "Internal name" %}</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th>{% trans "Products" %}</th>
|
||||
<th class="action-col-2"></th>
|
||||
<th colspan="2">{% trans "Products" %}</th>
|
||||
<th class="action-col-2"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -102,9 +101,10 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<td {% if d.benefit_same_products %}colspan="2"{% endif %}>
|
||||
{% if not d.benefit_same_products %}{% trans "Condition:" %}{% endif %}
|
||||
{% if d.condition_all_products %}
|
||||
<em>{% trans "All" %}</em>
|
||||
<ul><li><em>{% trans "All" %}</em></li></ul>
|
||||
{% else %}
|
||||
<ul>
|
||||
{% for item in d.condition_limit_products.all %}
|
||||
@@ -115,18 +115,28 @@
|
||||
</ul>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if not d.benefit_same_products %}
|
||||
<td>
|
||||
{% trans "Applies to:" %}
|
||||
<ul>
|
||||
{% for item in d.benefit_limit_products.all %}
|
||||
<li>
|
||||
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.id %}">{{ item }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td class="text-right flip">
|
||||
<button formaction="{% url "control:event.items.discounts.up" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-default btn-sm sortable-up"
|
||||
class="btn btn-default btn-sm sortable-up" title="{% trans "Move up" %}"
|
||||
{% if forloop.counter0 == 0 and not page_obj.has_previous %}
|
||||
disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button formaction="{% url "control:event.items.discounts.down" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-default btn-sm sortable-down"
|
||||
class="btn btn-default btn-sm sortable-down" title="{% trans "Move down" %}"
|
||||
{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}>
|
||||
<i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container"></span>
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<a href="{% url "control:event.items.discounts.edit" organizer=request.event.organizer.slug event=request.event.slug discount=d.id %}"
|
||||
class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.discounts.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ d.id }}"
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
{% extends "pretixcontrol/items/base.html" %}
|
||||
{% load i18n %}
|
||||
{% load money %}
|
||||
{% block title %}{% trans "Products" %}{% endblock %}
|
||||
{% block inside %}
|
||||
{% blocktrans asvar s_taxes %}taxes{% endblocktrans %}
|
||||
<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.
|
||||
product details. You can also use the buttons on the right to change the order of products or move
|
||||
products to a different category.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
{% if items|length == 0 %}
|
||||
@@ -29,7 +31,7 @@
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-condensed table-hover">
|
||||
<table class="table table-condensed table-hover table-items">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Product name" %}</th>
|
||||
@@ -37,16 +39,24 @@
|
||||
<th class="iconcol"></th>
|
||||
<th class="iconcol"></th>
|
||||
<th class="iconcol"></th>
|
||||
<th>{% trans "Category" %}</th>
|
||||
<th class="action-col-2"><span class="sr-only">Move</span></th>
|
||||
<th class="text-right flip">{% trans "Default price" %}</th>
|
||||
<th class="action-col-2"><span class="sr-only">Edit</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% regroup items by category as cat_list %}
|
||||
{% for c in cat_list %}
|
||||
<tbody data-dnd-url="{% url "control:event.items.reorder" organizer=request.event.organizer.slug event=request.event.slug %}">
|
||||
{% for i in c.list %}
|
||||
{% if forloop.counter0 == 0 and i.category %}<tr class="sortable-disabled"><th colspan="8" scope="colgroup" class="text-muted">{{ i.category }}</th></tr>{% endif %}
|
||||
|
||||
{% for c, items in cat_list %}
|
||||
{% if c %}
|
||||
<tbody>
|
||||
<tr class="sortable-disabled"><th colspan="9" scope="colgroup" class="text-muted">
|
||||
{{ c.name }}{% if c.category_type != "normal" %} <span class="font-normal">({{ c.get_category_type_display }})</span>{% endif %}
|
||||
<a href="{% url "control:event.items.categories.edit" organizer=request.event.organizer.slug event=request.event.slug category=c.id %}" title="{% trans "Edit" %}"><span class="fa fa-edit fa-fw"></span></a>
|
||||
</th></tr>
|
||||
</tbody>
|
||||
{% endif %}
|
||||
<tbody data-dnd-url="{% url "control:event.items.reorder" organizer=request.event.organizer.slug event=request.event.slug category=c.id|default:0 %}"
|
||||
data-dnd-group="items">
|
||||
{% for i in items %}
|
||||
{% if forloop.counter0 == 0 and i.category %}{% endif %}
|
||||
<tr data-dnd-id="{{ i.id }}" {% if not i.active %}class="row-muted"{% endif %}>
|
||||
<td><strong>
|
||||
{% if not i.active %}<strike>{% endif %}
|
||||
@@ -92,15 +102,15 @@
|
||||
</td>
|
||||
<td>
|
||||
{% if i.var_count %}
|
||||
<span class="fa fa-th-large fa-fw text-muted" data-toggle="tooltip" title="{% trans "Product with variations" %}"></span>
|
||||
<span class="fa fa-bars fa-fw text-muted" data-toggle="tooltip" title="{% trans "Product with variations" %}"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if i.category.is_addon %}
|
||||
<span class="fa fa-puzzle-piece fa-fw text-muted" data-toggle="tooltip"
|
||||
<span class="fa fa-plus-square fa-fw text-muted" data-toggle="tooltip"
|
||||
title="{% trans "Only available as an add-on product" %}"></span>
|
||||
{% elif i.require_bundling %}
|
||||
<span class="fa fa-puzzle-piece fa-fw text-muted" data-toggle="tooltip"
|
||||
<span class="fa fa-plus-square fa-fw text-muted" data-toggle="tooltip"
|
||||
title="{% trans "Only available as part of a bundle" %}"></span>
|
||||
{% elif i.hide_without_voucher %}
|
||||
<span class="fa fa-tags fa-fw text-muted" data-toggle="tooltip"
|
||||
@@ -110,16 +120,29 @@
|
||||
title="{% trans "Can only be bought using a voucher" %}"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{% if i.category %}{{ i.category.name }}{% endif %}</td>
|
||||
<td>
|
||||
<button formaction="{% url "control:event.items.up" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button formaction="{% url "control:event.items.down" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container"></span>
|
||||
<td class="text-right flip">
|
||||
{% if i.free_price %}
|
||||
<span class="fa fa-edit fa-fw text-muted" data-toggle="tooltip" title="{% trans "Free price input" %}">
|
||||
</span>
|
||||
{% endif %}
|
||||
{{ i.default_price|money:request.event.currency }}
|
||||
{% if i.original_price %}<strike class="text-muted">{{ i.original_price|money:request.event.currency }}</strike>{% endif %}
|
||||
{% if i.tax_rule and i.default_price %}
|
||||
<br/>
|
||||
<small class="text-muted">
|
||||
{% blocktrans trimmed with rate=i.tax_rule.rate|floatformat:-2 taxname=i.tax_rule.name|default:s_taxes %}
|
||||
incl. {{ rate }}% {{ taxname }}
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-right flip col-actions">
|
||||
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
|
||||
<button title="{% trans "Move up" %}" formaction="{% url "control:event.items.up" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button title="{% trans "Move down" %}" formaction="{% url "control:event.items.down" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
<a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-default btn-sm" title="{% trans "Edit" %}"><i class="fa fa-edit"></i></a>
|
||||
<a href="{% url "control:event.items.add" organizer=request.event.organizer.slug event=request.event.slug %}?copy_from={{ i.id }}" class="btn btn-default btn-sm" title="{% trans "Clone" %}" data-toggle="tooltip"><i class="fa fa-copy"></i></a>
|
||||
<a href="{% url "control:event.items.delete" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
|
||||
<a href="{% url "control:event.items.delete" organizer=request.event.organizer.slug event=request.event.slug item=i.id %}" class="btn btn-danger btn-sm" title="{% trans "Delete" %}"><i class="fa fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@@ -51,9 +51,9 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<button formaction="{% url "control:organizer.property.up" organizer=request.organizer.slug property=p.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 and not page_obj.has_previous %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button formaction="{% url "control:organizer.property.down" organizer=request.organizer.slug property=p.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container"></span>
|
||||
<button title="{% trans "Move up" %}" formaction="{% url "control:organizer.property.up" organizer=request.organizer.slug property=p.id %}" class="btn btn-default btn-sm sortable-up"{% if forloop.counter0 == 0 and not page_obj.has_previous %} disabled{% endif %}><i class="fa fa-arrow-up"></i></button>
|
||||
<button title="{% trans "Move down" %}" formaction="{% url "control:organizer.property.down" organizer=request.organizer.slug property=p.id %}" class="btn btn-default btn-sm sortable-down"{% if forloop.revcounter0 == 0 and not page_obj.has_next %} disabled{% endif %}><i class="fa fa-arrow-down"></i></button>
|
||||
<span class="dnd-container" title="{% trans "Click and drag this button to reorder. Double click to show buttons for reordering." %}"></span>
|
||||
</td>
|
||||
<td class="text-right flip">
|
||||
<a href="{% url "control:organizer.property.edit" organizer=request.organizer.slug property=p.id %}"
|
||||
|
||||
@@ -293,7 +293,7 @@ urlpatterns = [
|
||||
re_path(r'^items/(?P<item>\d+)/$', item.ItemUpdateGeneral.as_view(), name='event.item'),
|
||||
re_path(r'^items/(?P<item>\d+)/up$', item.item_move_up, name='event.items.up'),
|
||||
re_path(r'^items/(?P<item>\d+)/down$', item.item_move_down, name='event.items.down'),
|
||||
re_path(r'^items/reorder$', item.reorder_items, name='event.items.reorder'),
|
||||
re_path(r'^items/reorder/(?P<category>\d+)/$', item.reorder_items, name='event.items.reorder'),
|
||||
re_path(r'^items/(?P<item>\d+)/delete$', item.ItemDelete.as_view(), name='event.items.delete'),
|
||||
re_path(r'^items/typeahead/meta/$', typeahead.item_meta_values, name='event.items.meta.typeahead'),
|
||||
re_path(r'^items/select2$', typeahead.items_select2, name='event.items.select2'),
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
import json
|
||||
from collections import OrderedDict, namedtuple
|
||||
from itertools import groupby
|
||||
from json.decoder import JSONDecodeError
|
||||
|
||||
from django.contrib import messages
|
||||
@@ -113,6 +114,8 @@ class ItemList(ListView):
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
ctx['sales_channels'] = get_all_sales_channels()
|
||||
items_by_category = {cat: list(items) for cat, items in groupby(ctx['items'], lambda item: item.category)}
|
||||
ctx['cat_list'] = [(cat, items_by_category.get(cat, [])) for cat in [None, *self.request.event.categories.all()]]
|
||||
return ctx
|
||||
|
||||
|
||||
@@ -169,7 +172,7 @@ def item_move_down(request, organizer, event, item):
|
||||
@transaction.atomic
|
||||
@event_permission_required("can_change_items")
|
||||
@require_http_methods(["POST"])
|
||||
def reorder_items(request, organizer, event):
|
||||
def reorder_items(request, organizer, event, category):
|
||||
try:
|
||||
ids = json.loads(request.body.decode('utf-8'))['ids']
|
||||
except (JSONDecodeError, KeyError, ValueError):
|
||||
@@ -180,23 +183,21 @@ def reorder_items(request, organizer, event):
|
||||
if len(input_items) != len(ids):
|
||||
raise Http404(_("Some of the provided object ids are invalid."))
|
||||
|
||||
item_categories = {i.category_id for i in input_items}
|
||||
if len(item_categories) > 1:
|
||||
raise Http404(_("You cannot reorder items spanning different categories."))
|
||||
|
||||
# get first and only category
|
||||
item_category = next(iter(item_categories))
|
||||
if len(input_items) != request.event.items.filter(category=item_category).count():
|
||||
raise Http404(_("Not all objects have been selected."))
|
||||
if int(category):
|
||||
target_category = request.event.categories.get(id=category)
|
||||
else:
|
||||
target_category = None
|
||||
|
||||
for i in input_items:
|
||||
pos = ids.index(str(i.pk))
|
||||
if pos != i.position: # Save unneccessary UPDATE queries
|
||||
if pos != i.position or target_category != i.category: # Save unneccessary UPDATE queries
|
||||
i.position = pos
|
||||
i.save(update_fields=['position'])
|
||||
i.category = target_category
|
||||
i.save(update_fields=['position', 'category_id'])
|
||||
i.log_action(
|
||||
'pretix.event.item.reordered', user=request.user, data={
|
||||
'position': i,
|
||||
'category': target_category and target_category.pk,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,48 +1,78 @@
|
||||
/*global $, Sortable*/
|
||||
$(function () {
|
||||
$("[data-dnd-url]").each(function(){
|
||||
var container = $(this),
|
||||
const allContainers = $("[data-dnd-url]");
|
||||
function updateAllSortButtonStates() {
|
||||
allContainers.each(function() { updateSortButtonState($(this)); });
|
||||
}
|
||||
function updateSortButtonState(container) {
|
||||
var disabledUp = container.find(".sortable-up:disabled"),
|
||||
firstUp = container.find(">tr[data-dnd-id] .sortable-up").first();
|
||||
if (disabledUp.length && disabledUp.get(0) !== firstUp.get(0)) {
|
||||
disabledUp.prop("disabled", false);
|
||||
firstUp.prop("disabled", true);
|
||||
}
|
||||
|
||||
var disabledDown = container.find(".sortable-down:disabled"),
|
||||
lastDown = container.find(">tr[data-dnd-id] .sortable-down").last();
|
||||
if (disabledDown.length && disabledDown.get(0) !== lastDown.get(0)) {
|
||||
disabledDown.prop("disabled", false);
|
||||
lastDown.prop("disabled", true);
|
||||
}
|
||||
}
|
||||
|
||||
let didSort = false, lastClick = 0;
|
||||
allContainers.each(function(){
|
||||
const container = $(this),
|
||||
url = container.data("dnd-url"),
|
||||
handle = $('<span class="btn btn-default btn-sm dnd-sort-handle"><i class="fa fa-arrows"></i></span>');
|
||||
|
||||
container.find(".dnd-container").append(handle);
|
||||
if (container.find("[data-dnd-id]").length < 2) {
|
||||
if (!sessionStorage.dndShowMoveButtons) {
|
||||
container.find(".sortable-up, .sortable-down").addClass("sr-only").on("click", function () {
|
||||
sessionStorage.dndShowMoveButtons = 'true';
|
||||
});
|
||||
}
|
||||
if (container.find("[data-dnd-id]").length < 2 && !container.data("dnd-group")) {
|
||||
handle.addClass("disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
function maybeShowSortButtons() {
|
||||
if (Date.now() - lastClick < 3000) {
|
||||
$("[data-dnd-url] .sortable-up, [data-dnd-url] .sortable-down").removeClass("sr-only");
|
||||
updateAllSortButtonStates();
|
||||
}
|
||||
lastClick = Date.now();
|
||||
}
|
||||
container.find(".dnd-sort-handle").on("mouseup", maybeShowSortButtons);
|
||||
const group = container.data("dnd-group");
|
||||
const containers = group ? container.parent().find('[data-dnd-group="' + group + '"]') : container;
|
||||
Sortable.create(container.get(0), {
|
||||
filter: ".sortable-disabled",
|
||||
handle: ".dnd-sort-handle",
|
||||
group: group,
|
||||
onMove: function (evt) {
|
||||
return evt.related.className.indexOf('sortable-disabled') === -1;
|
||||
},
|
||||
onStart: function (evt) {
|
||||
container.addClass("sortable-dragarea");
|
||||
containers.addClass("sortable-dragarea");
|
||||
container.parent().addClass("sortable-sorting");
|
||||
didSort = false;
|
||||
},
|
||||
onEnd: function (evt) {
|
||||
container.removeClass("sortable-dragarea");
|
||||
containers.removeClass("sortable-dragarea");
|
||||
container.parent().removeClass("sortable-sorting");
|
||||
|
||||
var disabledUp = container.find(".sortable-up:disabled"),
|
||||
firstUp = container.find(">tr[data-dnd-id] .sortable-up").first();
|
||||
if (disabledUp.length && disabledUp.get(0) !== firstUp.get(0)) {
|
||||
disabledUp.prop("disabled", false);
|
||||
firstUp.prop("disabled", true);
|
||||
}
|
||||
|
||||
var disabledDown = container.find(".sortable-down:disabled"),
|
||||
lastDown = container.find(">tr[data-dnd-id] .sortable-down").last();
|
||||
if (disabledDown.length && disabledDown.get(0) !== lastDown.get(0)) {
|
||||
disabledDown.prop("disabled", false);
|
||||
lastDown.prop("disabled", true);
|
||||
if (!didSort) {
|
||||
maybeShowSortButtons();
|
||||
} else {
|
||||
$("[data-dnd-url] .sortable-up, [data-dnd-url] .sortable-down").addClass("sr-only");
|
||||
delete sessionStorage.dndShowMoveButtons;
|
||||
}
|
||||
},
|
||||
onSort: function (evt){
|
||||
var container = $(evt.to),
|
||||
ids = container.find("[data-dnd-id]").toArray().map(function (e) { return e.dataset.dndId; });
|
||||
if (evt.target !== evt.to) return;
|
||||
didSort = true;
|
||||
|
||||
const ids = container.find("[data-dnd-id]").toArray().map(function (e) { return e.dataset.dndId; });
|
||||
$.ajax(
|
||||
{
|
||||
'type': 'POST',
|
||||
|
||||
@@ -841,9 +841,21 @@ h1 .label {
|
||||
tbody[data-dnd-url] {
|
||||
transition: opacity 1s;
|
||||
}
|
||||
.table-items tbody + tbody {
|
||||
border-top-width: 1px;
|
||||
}
|
||||
.table-items tbody[data-dnd-url]:after {
|
||||
content:'';
|
||||
display: table-row;
|
||||
height: 0.5em;
|
||||
}
|
||||
.sortable-sorting tbody:not(.sortable-dragarea) {
|
||||
opacity: .4;
|
||||
}
|
||||
.font-normal {
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
}
|
||||
tbody th {
|
||||
background: $table-bg-hover;
|
||||
}
|
||||
|
||||
@@ -402,6 +402,24 @@ class ItemsTest(ItemFormTest):
|
||||
self.item2.refresh_from_db()
|
||||
assert self.item1.position < self.item2.position
|
||||
|
||||
def test_reorder(self):
|
||||
self.client.post('/control/event/%s/%s/items/reorder/0/' % (self.orga1.slug, self.event1.slug), {
|
||||
'ids': [str(self.item2.id), str(self.item1.id)],
|
||||
}, content_type='application/json')
|
||||
self.item1.refresh_from_db()
|
||||
self.item2.refresh_from_db()
|
||||
assert self.item1.position > self.item2.position
|
||||
assert self.item1.category is None
|
||||
assert self.item2.category is None
|
||||
self.client.post('/control/event/%s/%s/items/reorder/%s/' % (self.orga1.slug, self.event1.slug, self.addoncat.id), {
|
||||
'ids': [str(self.item1.id), str(self.item2.id)],
|
||||
}, content_type='application/json')
|
||||
self.item1.refresh_from_db()
|
||||
self.item2.refresh_from_db()
|
||||
assert self.item1.position < self.item2.position
|
||||
assert self.item1.category.id == self.addoncat.id
|
||||
assert self.item2.category.id == self.addoncat.id
|
||||
|
||||
def test_create(self):
|
||||
self.client.post('/control/event/%s/%s/items/add' % (self.orga1.slug, self.event1.slug), {
|
||||
'name_0': 'T-Shirt',
|
||||
|
||||
@@ -323,7 +323,7 @@ event_permission_urls = [
|
||||
("can_change_items", "items/add", 200, HTTP_GET),
|
||||
("can_change_items", "items/1/up", 404, HTTP_POST),
|
||||
("can_change_items", "items/1/down", 404, HTTP_POST),
|
||||
("can_change_items", "items/reorder", 400, HTTP_POST),
|
||||
("can_change_items", "items/reorder/2/", 400, HTTP_POST),
|
||||
("can_change_items", "items/1/delete", 404, HTTP_GET),
|
||||
# ("can_change_items", "categories/", 200),
|
||||
# We don't have to create categories and similar objects
|
||||
|
||||
Reference in New Issue
Block a user