mirror of
https://github.com/pretix/pretix.git
synced 2026-08-27 13:24:41 +00:00
Various improvements to the subevent creation form (#1670)
This commit is contained in:
@@ -2,6 +2,7 @@ from datetime import timedelta
|
|||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.forms import formset_factory
|
from django.forms import formset_factory
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.dates import MONTHS, WEEKDAYS
|
from django.utils.dates import MONTHS, WEEKDAYS
|
||||||
@@ -59,20 +60,6 @@ class SubEventForm(I18nModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class SubEventBulkForm(SubEventForm):
|
class SubEventBulkForm(SubEventForm):
|
||||||
time_from = forms.TimeField(
|
|
||||||
label=_('Event start time'),
|
|
||||||
widget=forms.TimeInput(attrs={'class': 'timepickerfield'})
|
|
||||||
)
|
|
||||||
time_to = forms.TimeField(
|
|
||||||
label=_('Event end time'),
|
|
||||||
widget=forms.TimeInput(attrs={'class': 'timepickerfield'}),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
time_admission = forms.TimeField(
|
|
||||||
label=_('Admission time'),
|
|
||||||
widget=forms.TimeInput(attrs={'class': 'timepickerfield'}),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
rel_presale_start = RelativeDateTimeField(
|
rel_presale_start = RelativeDateTimeField(
|
||||||
label=_('Start of presale'),
|
label=_('Start of presale'),
|
||||||
help_text=_('Optional. No products will be sold before this date.'),
|
help_text=_('Optional. No products will be sold before this date.'),
|
||||||
@@ -376,3 +363,34 @@ RRuleFormSet = formset_factory(
|
|||||||
RRuleForm,
|
RRuleForm,
|
||||||
can_order=False, can_delete=True, extra=1
|
can_order=False, can_delete=True, extra=1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TimeForm(forms.Form):
|
||||||
|
time_from = forms.TimeField(
|
||||||
|
label=_('Event start time'),
|
||||||
|
widget=forms.TimeInput(attrs={'class': 'timepickerfield'}),
|
||||||
|
required=True
|
||||||
|
)
|
||||||
|
time_to = forms.TimeField(
|
||||||
|
label=_('Event end time'),
|
||||||
|
widget=forms.TimeInput(attrs={'class': 'timepickerfield'}),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
time_admission = forms.TimeField(
|
||||||
|
label=_('Admission time'),
|
||||||
|
widget=forms.TimeInput(attrs={'class': 'timepickerfield'}),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
d = super().clean()
|
||||||
|
if d.get('time_from') and d.get('time_to') and d['time_from'] > d['time_to']:
|
||||||
|
raise ValidationError({'time_to': _('The end of the event has to be later than its start.')})
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
TimeFormSet = formset_factory(
|
||||||
|
TimeForm,
|
||||||
|
min_num=1,
|
||||||
|
can_order=False, can_delete=True, extra=1, validate_min=True
|
||||||
|
)
|
||||||
|
|||||||
@@ -261,12 +261,87 @@
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<fieldset>
|
||||||
|
<legend>{% trans "Times" context "subevent" %}</legend>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-4"><strong>{% trans "Event start time" %}</strong></div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<strong>{% trans "Event end time" %}</strong><br>
|
||||||
|
<label><span class="optional">{% trans "Optional" %}</span></label>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
<strong>{% trans "Admission time" %}</strong><br>
|
||||||
|
<label><span class="optional">{% trans "Optional" %}</span></label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="formset" data-formset data-formset-prefix="{{ time_formset.prefix }}"
|
||||||
|
id="time-formset">
|
||||||
|
{{ time_formset.management_form }}
|
||||||
|
{% bootstrap_formset_errors time_formset %}
|
||||||
|
<div data-formset-body>
|
||||||
|
{% for f in time_formset %}
|
||||||
|
{% bootstrap_form_errors f %}
|
||||||
|
<div data-formset-form>
|
||||||
|
<div class="row form-inline">
|
||||||
|
<div class="sr-only">
|
||||||
|
{{ f.id }}
|
||||||
|
{% bootstrap_field f.DELETE form_group_class="" layout="inline" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
{% bootstrap_field f.time_from layout="inline" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
{% bootstrap_field f.time_to layout="inline" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
{% bootstrap_field f.time_admission layout="inline" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-1 text-right flip">
|
||||||
|
<button type="button" class="btn btn-danger btn-block"
|
||||||
|
data-formset-delete-button>
|
||||||
|
<i class="fa fa-trash"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<script type="form-template" data-formset-empty-form>
|
||||||
|
{% escapescript %}
|
||||||
|
<div data-formset-form>
|
||||||
|
<div class="row form-inline">
|
||||||
|
<div class="sr-only">
|
||||||
|
{{ time_formset.empty_form.id }}
|
||||||
|
{% bootstrap_field time_formset.empty_form.DELETE form_group_class="" layout="inline" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
{% bootstrap_field time_formset.empty_form.time_from layout="inline" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
{% bootstrap_field time_formset.empty_form.time_to layout="inline" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-3">
|
||||||
|
{% bootstrap_field time_formset.empty_form.time_admission layout="inline" %}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-1 text-right flip">
|
||||||
|
<button type="button" class="btn btn-danger btn-block"
|
||||||
|
data-formset-delete-button>
|
||||||
|
<i class="fa fa-trash"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endescapescript %}
|
||||||
|
</script>
|
||||||
|
<p>
|
||||||
|
<button type="button" class="btn btn-default" data-formset-add>
|
||||||
|
<i class="fa fa-plus"></i> {% trans "Add a new time slot" %}</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{% trans "General information" %}</legend>
|
<legend>{% trans "General information" %}</legend>
|
||||||
{% bootstrap_field form.name layout="control" %}
|
{% bootstrap_field form.name layout="control" %}
|
||||||
{% bootstrap_field form.active layout="control" %}
|
{% bootstrap_field form.active layout="control" %}
|
||||||
{% bootstrap_field form.time_from layout="control" %}
|
|
||||||
{% bootstrap_field form.time_to layout="control" %}
|
|
||||||
<div class="geodata-section">
|
<div class="geodata-section">
|
||||||
{% bootstrap_field form.location layout="control" %}
|
{% bootstrap_field form.location layout="control" %}
|
||||||
<div class="form-group geodata-group" data-tiles="{{ global_settings.leaflet_tiles|default_if_none:"" }}" data-attrib="{{ global_settings.leaflet_tiles_attribution }}" data-icon="{% static "leaflet/images/marker-icon.png" %}" data-shadow="{% static "leaflet/images/marker-shadow.png" %}">
|
<div class="form-group geodata-group" data-tiles="{{ global_settings.leaflet_tiles|default_if_none:"" }}" data-attrib="{{ global_settings.leaflet_tiles_attribution }}" data-icon="{% static "leaflet/images/marker-icon.png" %}" data-shadow="{% static "leaflet/images/marker-shadow.png" %}">
|
||||||
@@ -291,7 +366,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% bootstrap_field form.time_admission layout="control" %}
|
|
||||||
{% bootstrap_field form.frontpage_text layout="control" %}
|
{% bootstrap_field form.frontpage_text layout="control" %}
|
||||||
{% bootstrap_field form.is_public layout="control" %}
|
{% bootstrap_field form.is_public layout="control" %}
|
||||||
{% if meta_forms %}
|
{% if meta_forms %}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from pretix.control.forms.item import QuotaForm
|
|||||||
from pretix.control.forms.subevents import (
|
from pretix.control.forms.subevents import (
|
||||||
CheckinListFormSet, QuotaFormSet, RRuleFormSet, SubEventBulkForm,
|
CheckinListFormSet, QuotaFormSet, RRuleFormSet, SubEventBulkForm,
|
||||||
SubEventForm, SubEventItemForm, SubEventItemVariationForm,
|
SubEventForm, SubEventItemForm, SubEventItemVariationForm,
|
||||||
SubEventMetaValueForm,
|
SubEventMetaValueForm, TimeFormSet,
|
||||||
)
|
)
|
||||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||||
from pretix.control.signals import subevent_forms
|
from pretix.control.signals import subevent_forms
|
||||||
@@ -215,7 +215,7 @@ class SubEventEditorMixin(MetaDataEditorMixin):
|
|||||||
|
|
||||||
formsetclass = inlineformset_factory(
|
formsetclass = inlineformset_factory(
|
||||||
SubEvent, Quota,
|
SubEvent, Quota,
|
||||||
form=QuotaForm, formset=QuotaFormSet,
|
form=QuotaForm, formset=QuotaFormSet, min_num=1, validate_min=True,
|
||||||
can_order=False, can_delete=True, extra=extra,
|
can_order=False, can_delete=True, extra=extra,
|
||||||
)
|
)
|
||||||
if self.object:
|
if self.object:
|
||||||
@@ -451,12 +451,17 @@ class SubEventCreate(SubEventEditorMixin, EventPermissionRequiredMixin, CreateVi
|
|||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
kwargs['event'] = self.request.event
|
kwargs['event'] = self.request.event
|
||||||
|
initial = kwargs.get('initial', {})
|
||||||
if self.copy_from:
|
if self.copy_from:
|
||||||
i = modelcopy(self.copy_from)
|
i = modelcopy(self.copy_from)
|
||||||
i.pk = None
|
i.pk = None
|
||||||
kwargs['instance'] = i
|
kwargs['instance'] = i
|
||||||
else:
|
else:
|
||||||
kwargs['instance'] = SubEvent(event=self.request.event)
|
kwargs['instance'] = SubEvent(event=self.request.event)
|
||||||
|
initial['location'] = self.request.event.location
|
||||||
|
initial['geo_lat'] = self.request.event.geo_lat
|
||||||
|
initial['geo_lon'] = self.request.event.geo_lon
|
||||||
|
kwargs['initial'] = initial
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
@@ -579,7 +584,7 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Crea
|
|||||||
form_class = SubEventBulkForm
|
form_class = SubEventBulkForm
|
||||||
|
|
||||||
def is_valid(self, form):
|
def is_valid(self, form):
|
||||||
return self.rrule_formset.is_valid() and super().is_valid(form)
|
return self.rrule_formset.is_valid() and self.time_formset.is_valid() and super().is_valid(form)
|
||||||
|
|
||||||
def get_success_url(self) -> str:
|
def get_success_url(self) -> str:
|
||||||
return reverse('control:event.subevents', kwargs={
|
return reverse('control:event.subevents', kwargs={
|
||||||
@@ -594,9 +599,17 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Crea
|
|||||||
prefix='rruleformset'
|
prefix='rruleformset'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def time_formset(self):
|
||||||
|
return TimeFormSet(
|
||||||
|
data=self.request.POST if self.request.method == "POST" else None,
|
||||||
|
prefix='timeformset'
|
||||||
|
)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
ctx = super().get_context_data(**kwargs)
|
ctx = super().get_context_data(**kwargs)
|
||||||
ctx['rrule_formset'] = self.rrule_formset
|
ctx['rrule_formset'] = self.rrule_formset
|
||||||
|
ctx['time_formset'] = self.time_formset
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
@@ -621,7 +634,9 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Crea
|
|||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super().get_form_kwargs()
|
kwargs = super().get_form_kwargs()
|
||||||
initial = {}
|
initial = {
|
||||||
|
'active': True,
|
||||||
|
}
|
||||||
kwargs['event'] = self.request.event
|
kwargs['event'] = self.request.event
|
||||||
tz = self.request.event.timezone
|
tz = self.request.event.timezone
|
||||||
if self.copy_from:
|
if self.copy_from:
|
||||||
@@ -643,9 +658,20 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Crea
|
|||||||
)) if i.presale_end else None
|
)) if i.presale_end else None
|
||||||
else:
|
else:
|
||||||
kwargs['instance'] = SubEvent(event=self.request.event)
|
kwargs['instance'] = SubEvent(event=self.request.event)
|
||||||
|
initial['location'] = self.request.event.location
|
||||||
|
initial['geo_lat'] = self.request.event.geo_lat
|
||||||
|
initial['geo_lon'] = self.request.event.geo_lon
|
||||||
kwargs['initial'] = initial
|
kwargs['initial'] = initial
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
|
def get_times(self):
|
||||||
|
times = []
|
||||||
|
for f in self.time_formset:
|
||||||
|
if f in self.time_formset.deleted_forms:
|
||||||
|
continue
|
||||||
|
times.append(f.cleaned_data)
|
||||||
|
return times
|
||||||
|
|
||||||
def get_rrule_set(self):
|
def get_rrule_set(self):
|
||||||
s = rruleset()
|
s = rruleset()
|
||||||
for f in self.rrule_formset:
|
for f in self.rrule_formset:
|
||||||
@@ -695,90 +721,91 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Crea
|
|||||||
tz = self.request.event.timezone
|
tz = self.request.event.timezone
|
||||||
cnt = 0
|
cnt = 0
|
||||||
for rdate in self.get_rrule_set():
|
for rdate in self.get_rrule_set():
|
||||||
se = copy.copy(form.instance)
|
for t in self.get_times():
|
||||||
|
se = copy.copy(form.instance)
|
||||||
|
|
||||||
se.date_from = make_aware(datetime.combine(rdate, form.cleaned_data['time_from']), tz)
|
se.date_from = make_aware(datetime.combine(rdate, t['time_from']), tz)
|
||||||
se.date_to = (
|
se.date_to = (
|
||||||
make_aware(datetime.combine(rdate, form.cleaned_data['time_to']), tz)
|
make_aware(datetime.combine(rdate, t['time_to']), tz)
|
||||||
if form.cleaned_data.get('time_to')
|
if t.get('time_to')
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
se.date_admission = (
|
se.date_admission = (
|
||||||
make_aware(datetime.combine(rdate, form.cleaned_data['time_admission']), tz)
|
make_aware(datetime.combine(rdate, t['time_admission']), tz)
|
||||||
if form.cleaned_data.get('time_admission')
|
if t.get('time_admission')
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
se.presale_start = (
|
se.presale_start = (
|
||||||
form.cleaned_data['rel_presale_start'].datetime(se)
|
form.cleaned_data['rel_presale_start'].datetime(se)
|
||||||
if form.cleaned_data.get('rel_presale_start')
|
if form.cleaned_data.get('rel_presale_start')
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
se.presale_end = (
|
se.presale_end = (
|
||||||
form.cleaned_data['rel_presale_end'].datetime(se)
|
form.cleaned_data['rel_presale_end'].datetime(se)
|
||||||
if form.cleaned_data.get('rel_presale_end')
|
if form.cleaned_data.get('rel_presale_end')
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
se.save()
|
se.save()
|
||||||
data = dict(form.cleaned_data)
|
data = dict(form.cleaned_data)
|
||||||
for f in self.plugin_forms:
|
for f in self.plugin_forms:
|
||||||
data.update({
|
data.update({
|
||||||
k: (f.cleaned_data.get(k).name
|
k: (f.cleaned_data.get(k).name
|
||||||
if isinstance(f.cleaned_data.get(k), File)
|
if isinstance(f.cleaned_data.get(k), File)
|
||||||
else f.cleaned_data.get(k))
|
else f.cleaned_data.get(k))
|
||||||
for k in f.cleaned_data
|
for k in f.cleaned_data
|
||||||
})
|
})
|
||||||
se.log_action('pretix.subevent.added', data=data, user=self.request.user)
|
se.log_action('pretix.subevent.added', data=data, user=self.request.user)
|
||||||
|
|
||||||
for f in self.meta_forms:
|
for f in self.meta_forms:
|
||||||
if f.cleaned_data.get('value'):
|
if f.cleaned_data.get('value'):
|
||||||
|
i = copy.copy(f.instance)
|
||||||
|
i.subevent = se
|
||||||
|
i.save()
|
||||||
|
|
||||||
|
for f in self.formset.forms:
|
||||||
|
if self.formset._should_delete_form(f):
|
||||||
|
continue
|
||||||
|
i = copy.copy(f.instance)
|
||||||
|
i.subevent = se
|
||||||
|
i.event = se.event
|
||||||
|
i.save()
|
||||||
|
selected_items = set(list(self.request.event.items.filter(id__in=[
|
||||||
|
i.split('-')[0] for i in f.cleaned_data.get('itemvars', [])
|
||||||
|
])))
|
||||||
|
selected_variations = list(ItemVariation.objects.filter(item__event=self.request.event, id__in=[
|
||||||
|
i.split('-')[1] for i in f.cleaned_data.get('itemvars', []) if '-' in i
|
||||||
|
]))
|
||||||
|
i.items.add(*[_i for _i in selected_items])
|
||||||
|
i.variations.add(*[_i for _i in selected_variations])
|
||||||
|
|
||||||
|
change_data = {k: f.cleaned_data.get(k) for k in f.changed_data}
|
||||||
|
change_data['id'] = i.pk
|
||||||
|
i.log_action(action='pretix.event.quota.added', user=self.request.user, data=change_data)
|
||||||
|
se.log_action('pretix.subevent.quota.added', user=self.request.user, data=change_data)
|
||||||
|
|
||||||
|
for f in self.cl_formset.forms:
|
||||||
|
if self.cl_formset._should_delete_form(f):
|
||||||
|
continue
|
||||||
|
i = copy.copy(f.instance)
|
||||||
|
i.subevent = se
|
||||||
|
i.event = se.event
|
||||||
|
i.save()
|
||||||
|
i.limit_products.add(*f.cleaned_data.get('limit_products', []))
|
||||||
|
change_data = {k: f.cleaned_data.get(k) for k in f.changed_data}
|
||||||
|
change_data['id'] = i.pk
|
||||||
|
i.log_action(action='pretix.event.checkinlist.added', user=self.request.user, data=change_data)
|
||||||
|
|
||||||
|
for f in self.itemvar_forms:
|
||||||
i = copy.copy(f.instance)
|
i = copy.copy(f.instance)
|
||||||
i.subevent = se
|
i.subevent = se
|
||||||
i.save()
|
i.save()
|
||||||
|
|
||||||
for f in self.formset.forms:
|
for f in self.plugin_forms:
|
||||||
if self.formset._should_delete_form(f):
|
f.is_valid()
|
||||||
continue
|
f.subevent = se
|
||||||
i = copy.copy(f.instance)
|
f.save()
|
||||||
i.subevent = se
|
|
||||||
i.event = se.event
|
|
||||||
i.save()
|
|
||||||
selected_items = set(list(self.request.event.items.filter(id__in=[
|
|
||||||
i.split('-')[0] for i in f.cleaned_data.get('itemvars', [])
|
|
||||||
])))
|
|
||||||
selected_variations = list(ItemVariation.objects.filter(item__event=self.request.event, id__in=[
|
|
||||||
i.split('-')[1] for i in f.cleaned_data.get('itemvars', []) if '-' in i
|
|
||||||
]))
|
|
||||||
i.items.add(*[_i for _i in selected_items])
|
|
||||||
i.variations.add(*[_i for _i in selected_variations])
|
|
||||||
|
|
||||||
change_data = {k: f.cleaned_data.get(k) for k in f.changed_data}
|
cnt += 1
|
||||||
change_data['id'] = i.pk
|
|
||||||
i.log_action(action='pretix.event.quota.added', user=self.request.user, data=change_data)
|
|
||||||
se.log_action('pretix.subevent.quota.added', user=self.request.user, data=change_data)
|
|
||||||
|
|
||||||
for f in self.cl_formset.forms:
|
|
||||||
if self.cl_formset._should_delete_form(f):
|
|
||||||
continue
|
|
||||||
i = copy.copy(f.instance)
|
|
||||||
i.subevent = se
|
|
||||||
i.event = se.event
|
|
||||||
i.save()
|
|
||||||
i.limit_products.add(*f.cleaned_data.get('limit_products', []))
|
|
||||||
change_data = {k: f.cleaned_data.get(k) for k in f.changed_data}
|
|
||||||
change_data['id'] = i.pk
|
|
||||||
i.log_action(action='pretix.event.checkinlist.added', user=self.request.user, data=change_data)
|
|
||||||
|
|
||||||
for f in self.itemvar_forms:
|
|
||||||
i = copy.copy(f.instance)
|
|
||||||
i.subevent = se
|
|
||||||
i.save()
|
|
||||||
|
|
||||||
for f in self.plugin_forms:
|
|
||||||
f.is_valid()
|
|
||||||
f.subevent = se
|
|
||||||
f.save()
|
|
||||||
|
|
||||||
cnt += 1
|
|
||||||
|
|
||||||
messages.success(self.request, pgettext_lazy('subevent', '{} new dates have been created.').format(cnt))
|
messages.success(self.request, pgettext_lazy('subevent', '{} new dates have been created.').format(cnt))
|
||||||
return redirect(reverse('control:event.subevents', kwargs={
|
return redirect(reverse('control:event.subevents', kwargs={
|
||||||
|
|||||||
@@ -406,6 +406,18 @@ table td > .checkbox input[type="checkbox"] {
|
|||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#time-formset {
|
||||||
|
input {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
.ticketoutput-panel .panel-heading {
|
.ticketoutput-panel .panel-heading {
|
||||||
.checkbox {
|
.checkbox {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|||||||
@@ -1174,10 +1174,14 @@ class SubEventsTest(SoupTest):
|
|||||||
'rruleformset-0-end': 'count',
|
'rruleformset-0-end': 'count',
|
||||||
'rruleformset-0-count': '10',
|
'rruleformset-0-count': '10',
|
||||||
'rruleformset-0-until': '2019-04-03',
|
'rruleformset-0-until': '2019-04-03',
|
||||||
|
'timeformset-TOTAL_FORMS': '1',
|
||||||
|
'timeformset-INITIAL_FORMS': '0',
|
||||||
|
'timeformset-MIN_NUM_FORMS': '1',
|
||||||
|
'timeformset-MAX_NUM_FORMS': '1000',
|
||||||
|
'timeformset-0-time_from': '13:29:31',
|
||||||
|
'timeformset-0-time_to': '15:29:31',
|
||||||
'name_0': 'Foo',
|
'name_0': 'Foo',
|
||||||
'active': 'on',
|
'active': 'on',
|
||||||
'time_from': '13:29:31',
|
|
||||||
'time_to': '15:29:31',
|
|
||||||
'location_0': 'Loc',
|
'location_0': 'Loc',
|
||||||
'time_admission': '',
|
'time_admission': '',
|
||||||
'frontpage_text_0': '',
|
'frontpage_text_0': '',
|
||||||
@@ -1262,10 +1266,14 @@ class SubEventsTest(SoupTest):
|
|||||||
'rruleformset-0-end': 'until',
|
'rruleformset-0-end': 'until',
|
||||||
'rruleformset-0-count': '10',
|
'rruleformset-0-count': '10',
|
||||||
'rruleformset-0-until': '2019-04-03',
|
'rruleformset-0-until': '2019-04-03',
|
||||||
|
'timeformset-TOTAL_FORMS': '1',
|
||||||
|
'timeformset-INITIAL_FORMS': '0',
|
||||||
|
'timeformset-MIN_NUM_FORMS': '1',
|
||||||
|
'timeformset-MAX_NUM_FORMS': '1000',
|
||||||
|
'timeformset-0-time_from': '13:29:31',
|
||||||
|
'timeformset-0-time_to': '15:29:31',
|
||||||
'name_0': 'Foo',
|
'name_0': 'Foo',
|
||||||
'active': 'on',
|
'active': 'on',
|
||||||
'time_from': '13:29:31',
|
|
||||||
'time_to': '15:29:31',
|
|
||||||
'frontpage_text_0': '',
|
'frontpage_text_0': '',
|
||||||
'rel_presale_start_0': 'unset',
|
'rel_presale_start_0': 'unset',
|
||||||
'rel_presale_start_1': '',
|
'rel_presale_start_1': '',
|
||||||
@@ -1277,10 +1285,13 @@ class SubEventsTest(SoupTest):
|
|||||||
'rel_presale_end_2': '1',
|
'rel_presale_end_2': '1',
|
||||||
'rel_presale_end_3': 'date_from',
|
'rel_presale_end_3': 'date_from',
|
||||||
'rel_presale_end_4': '13:29:31',
|
'rel_presale_end_4': '13:29:31',
|
||||||
'quotas-TOTAL_FORMS': '0',
|
'quotas-TOTAL_FORMS': '1',
|
||||||
'quotas-INITIAL_FORMS': '0',
|
'quotas-INITIAL_FORMS': '0',
|
||||||
'quotas-MIN_NUM_FORMS': '0',
|
'quotas-MIN_NUM_FORMS': '1',
|
||||||
'quotas-MAX_NUM_FORMS': '1000',
|
'quotas-MAX_NUM_FORMS': '1000',
|
||||||
|
'quotas-0-name': 'Q1',
|
||||||
|
'quotas-0-size': '50',
|
||||||
|
'quotas-0-itemvars': str(self.ticket.pk),
|
||||||
'checkinlist_set-TOTAL_FORMS': '0',
|
'checkinlist_set-TOTAL_FORMS': '0',
|
||||||
'checkinlist_set-INITIAL_FORMS': '0',
|
'checkinlist_set-INITIAL_FORMS': '0',
|
||||||
'checkinlist_set-MIN_NUM_FORMS': '0',
|
'checkinlist_set-MIN_NUM_FORMS': '0',
|
||||||
@@ -1295,6 +1306,74 @@ class SubEventsTest(SoupTest):
|
|||||||
assert ses[110].date_from.isoformat() == "2018-11-09T12:29:31+00:00" # DST :)
|
assert ses[110].date_from.isoformat() == "2018-11-09T12:29:31+00:00" # DST :)
|
||||||
assert ses[-1].date_from.isoformat() == "2019-04-02T11:29:31+00:00"
|
assert ses[-1].date_from.isoformat() == "2019-04-02T11:29:31+00:00"
|
||||||
|
|
||||||
|
def test_create_bulk_daily_interval_multiple_times(self):
|
||||||
|
with scopes_disabled():
|
||||||
|
self.event1.subevents.all().delete()
|
||||||
|
self.event1.settings.timezone = 'Europe/Berlin'
|
||||||
|
|
||||||
|
doc = self.get_doc('/control/event/ccc/30c3/subevents/bulk_add')
|
||||||
|
assert doc.select("input[name=rruleformset-TOTAL_FORMS]")
|
||||||
|
doc = self.post_doc('/control/event/ccc/30c3/subevents/bulk_add', {
|
||||||
|
'rruleformset-TOTAL_FORMS': '1',
|
||||||
|
'rruleformset-INITIAL_FORMS': '0',
|
||||||
|
'rruleformset-MIN_NUM_FORMS': '0',
|
||||||
|
'rruleformset-MAX_NUM_FORMS': '1000',
|
||||||
|
'rruleformset-0-interval': '2',
|
||||||
|
'rruleformset-0-freq': 'daily',
|
||||||
|
'rruleformset-0-dtstart': '2018-04-03',
|
||||||
|
'rruleformset-0-yearly_same': 'on',
|
||||||
|
'rruleformset-0-yearly_bysetpos': '1',
|
||||||
|
'rruleformset-0-yearly_byweekday': 'MO',
|
||||||
|
'rruleformset-0-yearly_bymonth': '1',
|
||||||
|
'rruleformset-0-monthly_same': 'on',
|
||||||
|
'rruleformset-0-monthly_bysetpos': '1',
|
||||||
|
'rruleformset-0-monthly_byweekday': 'MO',
|
||||||
|
'rruleformset-0-end': 'until',
|
||||||
|
'rruleformset-0-count': '10',
|
||||||
|
'rruleformset-0-until': '2019-04-03',
|
||||||
|
'timeformset-TOTAL_FORMS': '2',
|
||||||
|
'timeformset-INITIAL_FORMS': '0',
|
||||||
|
'timeformset-MIN_NUM_FORMS': '1',
|
||||||
|
'timeformset-MAX_NUM_FORMS': '1000',
|
||||||
|
'timeformset-0-time_from': '13:29:31',
|
||||||
|
'timeformset-0-time_to': '15:29:31',
|
||||||
|
'timeformset-1-time_from': '15:29:31',
|
||||||
|
'timeformset-1-time_to': '17:29:31',
|
||||||
|
'name_0': 'Foo',
|
||||||
|
'active': 'on',
|
||||||
|
'frontpage_text_0': '',
|
||||||
|
'rel_presale_start_0': 'unset',
|
||||||
|
'rel_presale_start_1': '',
|
||||||
|
'rel_presale_start_2': '1',
|
||||||
|
'rel_presale_start_3': 'date_from',
|
||||||
|
'rel_presale_start_4': '',
|
||||||
|
'rel_presale_end_1': '',
|
||||||
|
'rel_presale_end_0': 'relative',
|
||||||
|
'rel_presale_end_2': '1',
|
||||||
|
'rel_presale_end_3': 'date_from',
|
||||||
|
'rel_presale_end_4': '13:29:31',
|
||||||
|
'quotas-TOTAL_FORMS': '1',
|
||||||
|
'quotas-INITIAL_FORMS': '0',
|
||||||
|
'quotas-MIN_NUM_FORMS': '0',
|
||||||
|
'quotas-MAX_NUM_FORMS': '1000',
|
||||||
|
'quotas-0-name': 'Q1',
|
||||||
|
'quotas-0-size': '50',
|
||||||
|
'quotas-0-itemvars': str(self.ticket.pk),
|
||||||
|
'checkinlist_set-TOTAL_FORMS': '0',
|
||||||
|
'checkinlist_set-INITIAL_FORMS': '0',
|
||||||
|
'checkinlist_set-MIN_NUM_FORMS': '0',
|
||||||
|
'checkinlist_set-MAX_NUM_FORMS': '1000',
|
||||||
|
})
|
||||||
|
assert doc.select(".alert-success")
|
||||||
|
with scopes_disabled():
|
||||||
|
ses = list(self.event1.subevents.order_by('date_from'))
|
||||||
|
assert len(ses) == 183 * 2
|
||||||
|
|
||||||
|
assert ses[0].date_from.isoformat() == "2018-04-03T11:29:31+00:00"
|
||||||
|
assert ses[1].date_from.isoformat() == "2018-04-03T13:29:31+00:00"
|
||||||
|
assert ses[220].date_from.isoformat() == "2018-11-09T12:29:31+00:00" # DST :)
|
||||||
|
assert ses[-1].date_from.isoformat() == "2019-04-02T13:29:31+00:00"
|
||||||
|
|
||||||
def test_create_bulk_exclude(self):
|
def test_create_bulk_exclude(self):
|
||||||
with scopes_disabled():
|
with scopes_disabled():
|
||||||
self.event1.subevents.all().delete()
|
self.event1.subevents.all().delete()
|
||||||
@@ -1335,10 +1414,14 @@ class SubEventsTest(SoupTest):
|
|||||||
'rruleformset-1-count': '10',
|
'rruleformset-1-count': '10',
|
||||||
'rruleformset-1-until': '2019-04-03',
|
'rruleformset-1-until': '2019-04-03',
|
||||||
'rruleformset-1-exclude': 'on',
|
'rruleformset-1-exclude': 'on',
|
||||||
|
'timeformset-TOTAL_FORMS': '1',
|
||||||
|
'timeformset-INITIAL_FORMS': '0',
|
||||||
|
'timeformset-MIN_NUM_FORMS': '1',
|
||||||
|
'timeformset-MAX_NUM_FORMS': '1000',
|
||||||
|
'timeformset-0-time_from': '13:29:31',
|
||||||
|
'timeformset-0-time_to': '15:29:31',
|
||||||
'name_0': 'Foo',
|
'name_0': 'Foo',
|
||||||
'active': 'on',
|
'active': 'on',
|
||||||
'time_from': '13:29:31',
|
|
||||||
'time_to': '15:29:31',
|
|
||||||
'frontpage_text_0': '',
|
'frontpage_text_0': '',
|
||||||
'rel_presale_start_0': 'unset',
|
'rel_presale_start_0': 'unset',
|
||||||
'rel_presale_start_1': '',
|
'rel_presale_start_1': '',
|
||||||
@@ -1350,10 +1433,13 @@ class SubEventsTest(SoupTest):
|
|||||||
'rel_presale_end_2': '1',
|
'rel_presale_end_2': '1',
|
||||||
'rel_presale_end_3': 'date_from',
|
'rel_presale_end_3': 'date_from',
|
||||||
'rel_presale_end_4': '13:29:31',
|
'rel_presale_end_4': '13:29:31',
|
||||||
'quotas-TOTAL_FORMS': '0',
|
'quotas-TOTAL_FORMS': '1',
|
||||||
'quotas-INITIAL_FORMS': '0',
|
'quotas-INITIAL_FORMS': '0',
|
||||||
'quotas-MIN_NUM_FORMS': '0',
|
'quotas-MIN_NUM_FORMS': '0',
|
||||||
'quotas-MAX_NUM_FORMS': '1000',
|
'quotas-MAX_NUM_FORMS': '1000',
|
||||||
|
'quotas-0-name': 'Q1',
|
||||||
|
'quotas-0-size': '50',
|
||||||
|
'quotas-0-itemvars': str(self.ticket.pk),
|
||||||
'checkinlist_set-TOTAL_FORMS': '0',
|
'checkinlist_set-TOTAL_FORMS': '0',
|
||||||
'checkinlist_set-INITIAL_FORMS': '0',
|
'checkinlist_set-INITIAL_FORMS': '0',
|
||||||
'checkinlist_set-MIN_NUM_FORMS': '0',
|
'checkinlist_set-MIN_NUM_FORMS': '0',
|
||||||
@@ -1392,10 +1478,14 @@ class SubEventsTest(SoupTest):
|
|||||||
'rruleformset-0-end': 'until',
|
'rruleformset-0-end': 'until',
|
||||||
'rruleformset-0-count': '10',
|
'rruleformset-0-count': '10',
|
||||||
'rruleformset-0-until': '2019-04-03',
|
'rruleformset-0-until': '2019-04-03',
|
||||||
|
'timeformset-TOTAL_FORMS': '1',
|
||||||
|
'timeformset-INITIAL_FORMS': '0',
|
||||||
|
'timeformset-MIN_NUM_FORMS': '1',
|
||||||
|
'timeformset-MAX_NUM_FORMS': '1000',
|
||||||
|
'timeformset-0-time_from': '13:29:31',
|
||||||
|
'timeformset-0-time_to': '15:29:31',
|
||||||
'name_0': 'Foo',
|
'name_0': 'Foo',
|
||||||
'active': 'on',
|
'active': 'on',
|
||||||
'time_from': '13:29:31',
|
|
||||||
'time_to': '15:29:31',
|
|
||||||
'frontpage_text_0': '',
|
'frontpage_text_0': '',
|
||||||
'rel_presale_start_0': 'unset',
|
'rel_presale_start_0': 'unset',
|
||||||
'rel_presale_start_1': '',
|
'rel_presale_start_1': '',
|
||||||
@@ -1407,10 +1497,13 @@ class SubEventsTest(SoupTest):
|
|||||||
'rel_presale_end_2': '1',
|
'rel_presale_end_2': '1',
|
||||||
'rel_presale_end_3': 'date_from',
|
'rel_presale_end_3': 'date_from',
|
||||||
'rel_presale_end_4': '13:29:31',
|
'rel_presale_end_4': '13:29:31',
|
||||||
'quotas-TOTAL_FORMS': '0',
|
'quotas-TOTAL_FORMS': '1',
|
||||||
'quotas-INITIAL_FORMS': '0',
|
'quotas-INITIAL_FORMS': '0',
|
||||||
'quotas-MIN_NUM_FORMS': '0',
|
'quotas-MIN_NUM_FORMS': '0',
|
||||||
'quotas-MAX_NUM_FORMS': '1000',
|
'quotas-MAX_NUM_FORMS': '1000',
|
||||||
|
'quotas-0-name': 'Q1',
|
||||||
|
'quotas-0-size': '50',
|
||||||
|
'quotas-0-itemvars': str(self.ticket.pk),
|
||||||
'checkinlist_set-TOTAL_FORMS': '0',
|
'checkinlist_set-TOTAL_FORMS': '0',
|
||||||
'checkinlist_set-INITIAL_FORMS': '0',
|
'checkinlist_set-INITIAL_FORMS': '0',
|
||||||
'checkinlist_set-MIN_NUM_FORMS': '0',
|
'checkinlist_set-MIN_NUM_FORMS': '0',
|
||||||
@@ -1449,10 +1542,14 @@ class SubEventsTest(SoupTest):
|
|||||||
'rruleformset-0-end': 'until',
|
'rruleformset-0-end': 'until',
|
||||||
'rruleformset-0-count': '10',
|
'rruleformset-0-count': '10',
|
||||||
'rruleformset-0-until': '2019-04-03',
|
'rruleformset-0-until': '2019-04-03',
|
||||||
|
'timeformset-TOTAL_FORMS': '1',
|
||||||
|
'timeformset-INITIAL_FORMS': '0',
|
||||||
|
'timeformset-MIN_NUM_FORMS': '1',
|
||||||
|
'timeformset-MAX_NUM_FORMS': '1000',
|
||||||
|
'timeformset-0-time_from': '13:29:31',
|
||||||
|
'timeformset-0-time_to': '15:29:31',
|
||||||
'name_0': 'Foo',
|
'name_0': 'Foo',
|
||||||
'active': 'on',
|
'active': 'on',
|
||||||
'time_from': '13:29:31',
|
|
||||||
'time_to': '15:29:31',
|
|
||||||
'frontpage_text_0': '',
|
'frontpage_text_0': '',
|
||||||
'rel_presale_start_0': 'unset',
|
'rel_presale_start_0': 'unset',
|
||||||
'rel_presale_start_1': '',
|
'rel_presale_start_1': '',
|
||||||
@@ -1464,10 +1561,13 @@ class SubEventsTest(SoupTest):
|
|||||||
'rel_presale_end_2': '1',
|
'rel_presale_end_2': '1',
|
||||||
'rel_presale_end_3': 'date_from',
|
'rel_presale_end_3': 'date_from',
|
||||||
'rel_presale_end_4': '13:29:31',
|
'rel_presale_end_4': '13:29:31',
|
||||||
'quotas-TOTAL_FORMS': '0',
|
'quotas-TOTAL_FORMS': '1',
|
||||||
'quotas-INITIAL_FORMS': '0',
|
'quotas-INITIAL_FORMS': '0',
|
||||||
'quotas-MIN_NUM_FORMS': '0',
|
'quotas-MIN_NUM_FORMS': '0',
|
||||||
'quotas-MAX_NUM_FORMS': '1000',
|
'quotas-MAX_NUM_FORMS': '1000',
|
||||||
|
'quotas-0-name': 'Q1',
|
||||||
|
'quotas-0-size': '50',
|
||||||
|
'quotas-0-itemvars': str(self.ticket.pk),
|
||||||
'checkinlist_set-TOTAL_FORMS': '0',
|
'checkinlist_set-TOTAL_FORMS': '0',
|
||||||
'checkinlist_set-INITIAL_FORMS': '0',
|
'checkinlist_set-INITIAL_FORMS': '0',
|
||||||
'checkinlist_set-MIN_NUM_FORMS': '0',
|
'checkinlist_set-MIN_NUM_FORMS': '0',
|
||||||
|
|||||||
Reference in New Issue
Block a user