pricing: variable configuration (#64903)

This commit is contained in:
Lauréline Guérin 2022-05-09 15:16:30 +02:00
parent 4976df546a
commit 1cd72eeebc
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
8 changed files with 188 additions and 0 deletions

View File

@ -49,6 +49,20 @@ $(function() {
})
}
$(document).on('click', '#add-pricing-variable-form', function() {
if (typeof property_forms === "undefined") {var property_forms = $('.pricing-variable-form');}
if (typeof total_forms === "undefined") {var total_form = $('#id_form-TOTAL_FORMS');}
if (typeof form_num === "undefined") {var form_num = property_forms.length - 1;}
var new_form = $(property_forms[0]).clone();
var form_regex = RegExp(`form-(\\d){1}-`,'g');
form_num++;
new_form.html(new_form.html().replace(form_regex, `form-${form_num}-`));
new_form.appendTo('#pricing-variable-forms tbody');
$('#id_form-' + form_num + '-key').val('');
$('#id_form-' + form_num + '-value').val('');
total_form.val(form_num + 1);
})
$('.sortable').sortable({
handle: '.handle',
items: '.sortable-item',

View File

@ -51,6 +51,16 @@ class CriteriaForm(NewCriteriaForm):
return slug
class PricingVariableForm(forms.Form):
key = forms.CharField(label=_('Variable name'), required=False)
value = forms.CharField(
label=_('Value template'), widget=forms.TextInput(attrs={'size': 60}), required=False
)
PricingVariableFormSet = forms.formset_factory(PricingVariableForm)
class PricingCriteriaCategoryAddForm(forms.Form):
category = forms.ModelChoiceField(
label=_('Criteria category to add'), queryset=CriteriaCategory.objects.none(), required=True

View File

@ -202,6 +202,9 @@ class Pricing(models.Model):
result[key] = template.render(context)
return result
def get_extra_variables_keys(self):
return sorted((self.extra_variables or {}).keys())
class PricingCriteriaCategory(models.Model):
pricing = models.ForeignKey(Pricing, on_delete=models.CASCADE)

View File

@ -21,6 +21,20 @@
{% endblock %}
{% block content %}
<div class="section">
<h3>{% trans "Variables" %}</h3>
<div>
{% if object.extra_variables %}
<p>
<label>{% trans 'Extra variables:' %}</label>
{% for key in object.get_extra_variables_keys %}<i>{{ key }}</i>{% if not forloop.last %}, {% endif %}{% endfor %}
</p>
{% endif %}
<a class="pk-button" rel="popup" href="{% url 'chrono-manager-pricing-variable-edit' pk=object.pk %}">{% trans 'Define variables' %}</a>
</div>
</div>
<div class="section">
<h3>{% trans "Criterias" %}</h3>
{% with criterias=object.criterias.all categories=object.categories.all %}

View File

@ -0,0 +1,44 @@
{% extends "chrono/pricing/manager_pricing_detail.html" %}
{% load i18n %}
{% block breadcrumb %}
{{ block.super }}
<a href="{% url 'chrono-manager-pricing-variable-edit' object.pk %}">{% trans "Variable definition" %}</a>
{% endblock %}
{% block appbar %}
<h2>{% trans "Variable definition" %}</h2>
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.management_form }}
<table id="pricing-variable-forms">
<thead>
<tr>
{% for field in form.0 %}
<th class="column-{{ field.name }}{% if field.required %} required{% endif %}">{{ field.label }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for sub_form in form %}
<tr class='pricing-variable-form'>
{% for field in sub_form %}
<td class="field-{{ field.name }}">
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
<button id="add-pricing-variable-form" type="button">{% trans "Add another" %}</button>
<div class="buttons">
<button class="submit-button">{% trans "Save" %}</button>
<a class="cancel" href="{% url 'chrono-manager-pricing-detail' object.pk %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -40,6 +40,11 @@ urlpatterns = [
views.pricing_delete,
name='chrono-manager-pricing-delete',
),
url(
r'^(?P<pk>\d+)/variable/$',
views.pricing_variable_edit,
name='chrono-manager-pricing-variable-edit',
),
url(
r'^(?P<pk>\d+)/category/add/$',
views.pricing_criteria_category_add,

View File

@ -16,6 +16,7 @@
import datetime
import json
from operator import itemgetter
from django.core.exceptions import PermissionDenied
from django.db.models import Prefetch
@ -29,6 +30,7 @@ from chrono.pricing.forms import (
NewCriteriaForm,
PricingCriteriaCategoryAddForm,
PricingCriteriaCategoryEditForm,
PricingVariableFormSet,
)
from chrono.pricing.models import Criteria, CriteriaCategory, Pricing, PricingCriteriaCategory
@ -136,6 +138,43 @@ class PricingDeleteView(DeleteView):
pricing_delete = PricingDeleteView.as_view()
class PricingVariableEdit(FormView):
template_name = 'chrono/pricing/manager_pricing_variable_form.html'
model = Pricing
form_class = PricingVariableFormSet
def dispatch(self, request, *args, **kwargs):
self.object = get_object_or_404(Pricing, pk=kwargs['pk'])
if not request.user.is_staff:
raise PermissionDenied()
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
kwargs['object'] = self.object
return super().get_context_data(**kwargs)
def get_initial(self):
return sorted(
({'key': k, 'value': v} for k, v in self.object.extra_variables.items()),
key=itemgetter('key'),
)
def form_valid(self, form):
self.object.extra_variables = {}
for sub_data in form.cleaned_data:
if not sub_data.get('key'):
continue
self.object.extra_variables[sub_data['key']] = sub_data['value']
self.object.save()
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self):
return reverse('chrono-manager-pricing-detail', args=[self.object.pk])
pricing_variable_edit = PricingVariableEdit.as_view()
class PricingCriteriaCategoryAddView(FormView):
template_name = 'chrono/pricing/manager_pricing_criteria_category_form.html'
model = Pricing

View File

@ -111,6 +111,65 @@ def test_delete_pricing_as_manager(app, manager_user):
app.get('/manage/pricing/%s/delete/' % pricing.pk, status=403)
def test_pricing_edit_extra_variables(app, admin_user):
pricing = Pricing.objects.create(label='Model')
assert pricing.extra_variables == {}
app = login(app)
resp = app.get('/manage/pricing/%s/' % pricing.id)
assert '<label>Extra variables:</label>' not in resp.text
resp = resp.click(href='/manage/pricing/%s/variable/' % pricing.id)
resp.form['form-0-key'] = 'foo'
resp.form['form-0-value'] = 'bar'
resp = resp.form.submit().follow()
pricing.refresh_from_db()
assert pricing.extra_variables == {'foo': 'bar'}
assert '<label>Extra variables:</label>' in resp.text
assert '<i>foo</i>' in resp
resp = resp.click(href='/manage/pricing/%s/variable/' % pricing.id)
assert resp.form['form-TOTAL_FORMS'].value == '2'
assert resp.form['form-0-key'].value == 'foo'
assert resp.form['form-0-value'].value == 'bar'
assert resp.form['form-1-key'].value == ''
assert resp.form['form-1-value'].value == ''
resp.form['form-0-value'] = 'bar-bis'
resp.form['form-1-key'] = 'blah'
resp.form['form-1-value'] = 'baz'
resp = resp.form.submit().follow()
pricing.refresh_from_db()
assert pricing.extra_variables == {
'foo': 'bar-bis',
'blah': 'baz',
}
assert '<i>blah</i>, <i>foo</i>' in resp
resp = resp.click(href='/manage/pricing/%s/variable/' % pricing.id)
assert resp.form['form-TOTAL_FORMS'].value == '3'
assert resp.form['form-0-key'].value == 'blah'
assert resp.form['form-0-value'].value == 'baz'
assert resp.form['form-1-key'].value == 'foo'
assert resp.form['form-1-value'].value == 'bar-bis'
assert resp.form['form-2-key'].value == ''
assert resp.form['form-2-value'].value == ''
resp.form['form-1-key'] = 'foo'
resp.form['form-1-value'] = 'bar'
resp.form['form-0-key'] = ''
resp = resp.form.submit().follow()
pricing.refresh_from_db()
assert pricing.extra_variables == {
'foo': 'bar',
}
assert '<i>foo</i>' in resp
def test_pricing_edit_extra_variables_as_manager(app, manager_user):
pricing = Pricing.objects.create(label='Model')
app = login(app, username='manager', password='manager')
app.get('/manage/pricing/%s/variable/' % pricing.pk, status=403)
def test_pricing_add_category(app, admin_user):
pricing = Pricing.objects.create(label='Model')
category1 = CriteriaCategory.objects.create(label='Cat 1')