pricing: categories can be reordered in a pricing model (#64903)
This commit is contained in:
parent
306ec0e5ce
commit
4976df546a
|
@ -537,18 +537,22 @@ div.paragraph {
|
|||
}
|
||||
}
|
||||
|
||||
.sortable {
|
||||
span.handle {
|
||||
cursor: move;
|
||||
display: inline-block;
|
||||
padding: 0.5ex;
|
||||
text-align: center;
|
||||
width: 2em;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
ul.objects-list.sortable {
|
||||
li {
|
||||
position: relative;
|
||||
span.handle {
|
||||
cursor: move;
|
||||
display: inline-block;
|
||||
padding: 0.5ex;
|
||||
text-align: center;
|
||||
width: 2em;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
& > a {
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
@ -49,12 +49,12 @@ $(function() {
|
|||
})
|
||||
}
|
||||
|
||||
$('ul.sortable').sortable({
|
||||
$('.sortable').sortable({
|
||||
handle: '.handle',
|
||||
items: 'li.sortable-item',
|
||||
items: '.sortable-item',
|
||||
update : function(event, ui) {
|
||||
var new_order = '';
|
||||
$(this).find('li.sortable-item').each(function(i, x) {
|
||||
$(this).find('.sortable-item').each(function(i, x) {
|
||||
var item_id = $(x).data('item-id');
|
||||
if (new_order) {
|
||||
new_order += ',';
|
||||
|
|
|
@ -23,11 +23,16 @@
|
|||
{% block content %}
|
||||
<div class="section">
|
||||
<h3>{% trans "Criterias" %}</h3>
|
||||
{% with criterias=object.criterias.all categories=object.categories.all %}
|
||||
{% if categories %}
|
||||
<div>
|
||||
{% with criterias=object.criterias.all %}
|
||||
{% for category in object.categories.all %}
|
||||
<div class="paragraph">
|
||||
<h4>{% trans "Category:" %} {{ category }}</h4>
|
||||
{% blocktrans %}Use drag and drop with the ⣿ handles to reorder categories.{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="sortable" data-order-url="{% url 'chrono-manager-pricing-criteria-category-order' object.pk %}">
|
||||
{% for category in categories %}
|
||||
<div class="paragraph sortable-item" data-item-id="{{ category.pk }}">
|
||||
<h4><span class="handle">⣿</span>{% trans "Category:" %} {{ category }}</h4>
|
||||
<p>{% trans "Selected criterias:" %}</p>
|
||||
<ul>
|
||||
{% for criteria in criterias %}
|
||||
|
@ -42,12 +47,12 @@
|
|||
</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
{% if object.categories.count < 3 %}
|
||||
<p>
|
||||
<a rel="popup" class="pk-button" href="{% url 'chrono-manager-pricing-criteria-category-add' pk=object.pk %}">{% trans "Add a category" %} <span class="extra-info">({% trans "max 3 categories" %})</span></a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -55,6 +55,11 @@ urlpatterns = [
|
|||
views.pricing_criteria_category_delete,
|
||||
name='chrono-manager-pricing-criteria-category-delete',
|
||||
),
|
||||
url(
|
||||
r'^(?P<pk>\d+)/order/$',
|
||||
views.pricing_criteria_category_order,
|
||||
name='chrono-manager-pricing-criteria-category-order',
|
||||
),
|
||||
url(r'^criterias/$', views.criteria_list, name='chrono-manager-pricing-criteria-list'),
|
||||
url(
|
||||
r'^criteria/category/add/$',
|
||||
|
|
|
@ -234,6 +234,33 @@ class PricingCriteriaCategoryDeleteView(DeleteView):
|
|||
pricing_criteria_category_delete = PricingCriteriaCategoryDeleteView.as_view()
|
||||
|
||||
|
||||
class PricingCriteriaCategoryOrder(DetailView):
|
||||
model = Pricing
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_staff:
|
||||
raise PermissionDenied()
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if 'new-order' not in request.GET:
|
||||
return HttpResponseBadRequest('missing new-order parameter')
|
||||
pricing = self.get_object()
|
||||
try:
|
||||
new_order = [int(x) for x in request.GET['new-order'].split(',')]
|
||||
except ValueError:
|
||||
return HttpResponseBadRequest('incorrect new-order parameter')
|
||||
categories = pricing.categories.all()
|
||||
if set(new_order) != {x.pk for x in categories} or len(new_order) != len(categories):
|
||||
return HttpResponseBadRequest('incorrect new-order parameter')
|
||||
for i, c_id in enumerate(new_order):
|
||||
PricingCriteriaCategory.objects.filter(pricing=pricing, category=c_id).update(order=i + 1)
|
||||
return HttpResponse(status=204)
|
||||
|
||||
|
||||
pricing_criteria_category_order = PricingCriteriaCategoryOrder.as_view()
|
||||
|
||||
|
||||
class CriteriaCategoryAddView(CreateView):
|
||||
template_name = 'chrono/pricing/manager_criteria_category_form.html'
|
||||
model = CriteriaCategory
|
||||
|
|
|
@ -259,6 +259,78 @@ def test_pricing_delete_category_as_manager(app, manager_user):
|
|||
app.get('/manage/pricing/%s/category/%s/delete/' % (pricing.pk, category.pk), status=403)
|
||||
|
||||
|
||||
def test_pricing_reorder_categories(app, admin_user):
|
||||
category1 = CriteriaCategory.objects.create(label='Cat 1')
|
||||
category2 = CriteriaCategory.objects.create(label='Cat 2')
|
||||
category3 = CriteriaCategory.objects.create(label='Cat 3')
|
||||
category4 = CriteriaCategory.objects.create(label='Cat 4')
|
||||
pricing = Pricing.objects.create(label='Model')
|
||||
pricing.categories.add(category1, through_defaults={'order': 1})
|
||||
pricing.categories.add(category2, through_defaults={'order': 2})
|
||||
pricing.categories.add(category3, through_defaults={'order': 3})
|
||||
assert list(
|
||||
PricingCriteriaCategory.objects.filter(pricing=pricing).values_list('category', flat=True)
|
||||
) == [category1.pk, category2.pk, category3.pk]
|
||||
assert list(PricingCriteriaCategory.objects.filter(pricing=pricing).values_list('order', flat=True)) == [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
app = login(app)
|
||||
# missing get params
|
||||
app.get('/manage/pricing/%s/order/' % (pricing.pk), status=400)
|
||||
|
||||
# bad new-order param
|
||||
bad_params = [
|
||||
# missing category3 in order
|
||||
','.join(str(x) for x in [category1.pk, category2.pk]),
|
||||
# category1 mentionned twice
|
||||
','.join(str(x) for x in [category1.pk, category2.pk, category3.pk, category1.pk]),
|
||||
# category4 not in pricing categories
|
||||
','.join(str(x) for x in [category1.pk, category2.pk, category3.pk, category4.pk]),
|
||||
# not an id
|
||||
'foo,1,2,3',
|
||||
' 1 ,2,3',
|
||||
]
|
||||
for bad_param in bad_params:
|
||||
app.get(
|
||||
'/manage/pricing/%s/order/' % (pricing.pk),
|
||||
params={'new-order': bad_param},
|
||||
status=400,
|
||||
)
|
||||
# not changed
|
||||
assert list(
|
||||
PricingCriteriaCategory.objects.filter(pricing=pricing).values_list('category', flat=True)
|
||||
) == [category1.pk, category2.pk, category3.pk]
|
||||
assert list(PricingCriteriaCategory.objects.filter(pricing=pricing).values_list('order', flat=True)) == [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
# change order
|
||||
app.get(
|
||||
'/manage/pricing/%s/order/' % (pricing.pk),
|
||||
params={'new-order': ','.join(str(x) for x in [category3.pk, category1.pk, category2.pk])},
|
||||
)
|
||||
assert list(
|
||||
PricingCriteriaCategory.objects.filter(pricing=pricing).values_list('category', flat=True)
|
||||
) == [category3.pk, category1.pk, category2.pk]
|
||||
assert list(PricingCriteriaCategory.objects.filter(pricing=pricing).values_list('order', flat=True)) == [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
]
|
||||
|
||||
|
||||
def test_pricing_reorder_categories_as_manager(app, manager_user):
|
||||
pricing = Pricing.objects.create(label='Model')
|
||||
|
||||
app = login(app, username='manager', password='manager')
|
||||
app.get('/manage/pricing/%s/order/' % (pricing.pk), status=403)
|
||||
|
||||
|
||||
def test_list_criterias_as_manager(app, manager_user):
|
||||
app = login(app, username='manager', password='manager')
|
||||
app.get('/manage/pricing/criterias/', status=403)
|
||||
|
|
Loading…
Reference in New Issue