manager: custom fields edition (#63285)

This commit is contained in:
Lauréline Guérin 2022-04-01 08:30:24 +02:00
parent 4cfeb33d63
commit 597d88cce7
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
6 changed files with 205 additions and 1 deletions

View File

@ -1837,6 +1837,22 @@ class EventsType(models.Model):
def base_slug(self):
return slugify(self.label)
def get_custom_fields(self):
custom_fields = []
if not isinstance(self.custom_fields, list):
return custom_fields
for values in self.custom_fields:
if not isinstance(values, dict):
continue
complete = True
for k in ['varname', 'label', 'field_type']:
if not values.get(k):
complete = False
break
if complete:
custom_fields.append(values)
return custom_fields
class BookingColor(models.Model):
COLOR_COUNT = 8

View File

@ -29,7 +29,7 @@ from django.contrib.auth.models import Group
from django.core.exceptions import FieldDoesNotExist
from django.db import transaction
from django.db.models import DurationField, ExpressionWrapper, F
from django.forms import ValidationError
from django.forms import ValidationError, formset_factory
from django.utils.encoding import force_text
from django.utils.formats import date_format
from django.utils.six import StringIO
@ -48,6 +48,7 @@ from chrono.agendas.models import (
Booking,
Desk,
Event,
EventsType,
MeetingType,
Person,
Resource,
@ -330,6 +331,40 @@ class EventForm(NewEventForm):
return self.instance
class EventsTypeForm(forms.ModelForm):
class Meta:
model = EventsType
fields = ['label', 'slug']
class CustomFieldForm(forms.Form):
varname = forms.SlugField(label=_('Field slug'), required=False)
label = forms.CharField(label=_('Field label'), required=False)
field_type = forms.ChoiceField(
label=_('Field type'),
choices=[
('', '-------'),
('text', _('Text')),
('textarea', _('Textarea')),
('bool', _('Boolean')),
],
required=False,
)
def clean(self):
cleaned_data = super().clean()
if cleaned_data.get('varname') and not cleaned_data.get('label'):
self.add_error('label', _('This field is required.'))
if cleaned_data.get('varname') and not cleaned_data.get('field_type'):
self.add_error('field', _('This field is required.'))
return cleaned_data
CustomFieldFormSet = formset_factory(CustomFieldForm)
class BookingCheckFilterSet(django_filters.FilterSet):
class Meta:
model = Booking

View File

@ -31,4 +31,21 @@ $(function() {
}
});
}
if ($('#add-custom-field-form').length) {
var property_forms = $('.custom-field-form');
var total_form = $('#id_form-TOTAL_FORMS');
var form_num = property_forms.length - 1;
$('#add-custom-field-form').on('click', function() {
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('#custom-field-forms tbody');
$('#id_form-' + form_num + '-varname').val('');
$('#id_form-' + form_num + '-label').val('');
$('#id_form-' + form_num + '-field_type').val('');
total_form.val(form_num + 1);
})
}
});

View File

@ -23,6 +23,32 @@
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
{% if object.pk %}
<h3>{% trans "Custom fields" %}</h3>
{{ formset.management_form }}
<table id="custom-field-forms">
<thead>
<tr>
{% for field in formset.0 %}
<th class="column-{{ field.name }}{% if field.required %} required{% endif %}">{{ field.label }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for sub_form in formset %}
<tr class='custom-field-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-custom-field-form" type="button">{% trans "Add another custom field" %}</button>
{% endif %}
<div class="buttons">
<button class="submit-button">{% trans "Save" %}</button>
<a class="cancel" href="{% url 'chrono-manager-events-type-list' %}">{% trans 'Cancel' %}</a>

View File

@ -105,6 +105,7 @@ from .forms import (
BookingAbsenceReasonForm,
BookingCancelForm,
BookingCheckFilterSet,
CustomFieldFormSet,
DeskExceptionsImportForm,
DeskForm,
EventCancelForm,
@ -850,6 +851,37 @@ class EventsTypeEditView(UpdateView):
def get_success_url(self):
return reverse('chrono-manager-events-type-list')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
data = None
if self.request.method == 'POST':
data = self.request.POST
context['formset'] = CustomFieldFormSet(
data=data,
initial=self.get_object().get_custom_fields(),
)
return context
def form_valid(self, form):
self.object = form.save(commit=False) # save object and update cache only once
return HttpResponseRedirect(self.get_success_url())
def post(self, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
formset = CustomFieldFormSet(data=self.request.POST)
if form.is_valid() and formset.is_valid():
response = self.form_valid(form)
self.object.custom_fields = []
for sub_data in formset.cleaned_data:
if not sub_data.get('varname'):
continue
self.object.custom_fields.append(sub_data)
self.object.save()
return response
else:
return self.form_invalid(form)
events_type_edit = EventsTypeEditView.as_view()

View File

@ -28,6 +28,7 @@ def test_add_events_type(app, admin_user):
assert resp.location.endswith('/manage/events-types/')
assert events_type.label == 'Foo bar'
assert events_type.slug == 'foo-bar'
assert events_type.custom_fields == []
def test_add_events_type_as_manager(app, manager_user):
@ -56,6 +57,83 @@ def test_edit_events_type(app, admin_user):
events_type.refresh_from_db()
assert events_type.label == 'Foo bar baz'
assert events_type.slug == 'baz2'
assert events_type.custom_fields == []
def test_edit_events_type_custom_fields(app, admin_user):
events_type = EventsType.objects.create(label='Foo bar')
app = login(app)
resp = app.get('/manage/events-type/%s/edit/' % events_type.pk)
resp.form['form-0-varname'] = 'foo'
resp.form['form-0-label'] = 'Foo'
resp.form['form-0-field_type'] = 'text'
resp = resp.form.submit()
assert resp.status_code == 302
events_type.refresh_from_db()
assert events_type.custom_fields == [
{'varname': 'foo', 'label': 'Foo', 'field_type': 'text'},
]
resp = app.get('/manage/events-type/%s/edit/' % events_type.pk)
assert resp.form['form-TOTAL_FORMS'].value == '2'
assert resp.form['form-0-varname'].value == 'foo'
assert resp.form['form-0-label'].value == 'Foo'
assert resp.form['form-0-field_type'].value == 'text'
assert resp.form['form-1-varname'].value == ''
assert resp.form['form-1-label'].value == ''
assert resp.form['form-1-field_type'].value == ''
resp.form['form-0-label'] = 'Foo-bis'
resp.form['form-1-varname'] = 'blah'
resp.form['form-1-label'] = 'Blah'
resp.form['form-1-field_type'] = 'bool'
resp = resp.form.submit()
assert resp.status_code == 302
events_type.refresh_from_db()
assert events_type.custom_fields == [
{'varname': 'foo', 'label': 'Foo-bis', 'field_type': 'text'},
{'varname': 'blah', 'label': 'Blah', 'field_type': 'bool'},
]
resp = app.get('/manage/events-type/%s/edit/' % events_type.pk)
assert resp.form['form-TOTAL_FORMS'].value == '3'
assert resp.form['form-0-varname'].value == 'foo'
assert resp.form['form-0-label'].value == 'Foo-bis'
assert resp.form['form-0-field_type'].value == 'text'
assert resp.form['form-1-varname'].value == 'blah'
assert resp.form['form-1-label'].value == 'Blah'
assert resp.form['form-1-field_type'].value == 'bool'
assert resp.form['form-2-varname'].value == ''
assert resp.form['form-2-label'].value == ''
assert resp.form['form-2-field_type'].value == ''
resp.form['form-0-varname'] = 'foo'
resp.form['form-0-label'] = 'Foo'
resp.form['form-1-varname'] = ''
resp = resp.form.submit()
assert resp.status_code == 302
events_type.refresh_from_db()
assert events_type.custom_fields == [
{'varname': 'foo', 'label': 'Foo', 'field_type': 'text'},
]
# custom_fields contains anything (bad format or incomplete)
bad_values = [
{'foo': 'bar'},
['foo', 'bar'],
[['foo', 'bar']],
[{'foo': 'bar'}],
[{'varname': '', 'label': 'Foo', 'field_type': 'text'}],
[{'varname': 'foo', 'label': '', 'field_type': 'text'}],
[{'varname': 'foo', 'label': 'Foo', 'field_type': ''}],
]
for bad_value in bad_values:
events_type.custom_fields = bad_value
events_type.save()
resp = app.get('/manage/events-type/%s/edit/' % events_type.pk)
assert resp.form['form-TOTAL_FORMS'].value == '1'
assert resp.form['form-0-varname'].value == ''
assert resp.form['form-0-label'].value == ''
assert resp.form['form-0-field_type'].value == ''
def test_edit_events_type_as_manager(app, manager_user):