From 77d07049aaac602a22569e5ea41ba5d09ed0da42 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Sat, 11 Apr 2026 18:09:17 +0200 Subject: [PATCH] Subevents: Allow to skip conflicting dates in bulk-creation --- src/pretix/control/forms/subevents.py | 12 ++++++++++- .../pretixcontrol/subevents/bulk.html | 1 + src/pretix/control/views/subevents.py | 21 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/pretix/control/forms/subevents.py b/src/pretix/control/forms/subevents.py index b0a78fd7f4..05607956e3 100644 --- a/src/pretix/control/forms/subevents.py +++ b/src/pretix/control/forms/subevents.py @@ -28,7 +28,7 @@ from django.forms import formset_factory from django.forms.utils import ErrorDict from django.urls import reverse from django.utils.functional import cached_property -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _, pgettext_lazy from i18nfield.forms import I18nInlineFormSet from pretix.base.forms import I18nModelForm @@ -102,6 +102,16 @@ class SubEventBulkForm(SubEventForm): required=False, limit_choices=('date_from', 'date_to'), ) + skip_if_overlap = forms.BooleanField( + label=pgettext_lazy('subevent', 'Skip dates that overlap with any existing date'), + help_text=pgettext_lazy( + 'subevent', + 'This can be useful if all your dates happen in the same location and no repeated dates should ' + 'be created in conflict with existing special events. This respects even inactive dates and works best if ' + 'all dates have both a start and end time.' + ), + required=False, + ) def __init__(self, *args, **kwargs): self.event = kwargs['event'] diff --git a/src/pretix/control/templates/pretixcontrol/subevents/bulk.html b/src/pretix/control/templates/pretixcontrol/subevents/bulk.html index 3f7a041f79..85941ae72f 100644 --- a/src/pretix/control/templates/pretixcontrol/subevents/bulk.html +++ b/src/pretix/control/templates/pretixcontrol/subevents/bulk.html @@ -379,6 +379,7 @@ {% trans "Add many time slots" %}

+ {% bootstrap_field form.skip_if_overlap layout="control" horizontal_label_class='sr-only' horizontal_field_class='col-md-12' %}
{% trans "General information" %} diff --git a/src/pretix/control/views/subevents.py b/src/pretix/control/views/subevents.py index 4ecf6a8237..61a8e65921 100644 --- a/src/pretix/control/views/subevents.py +++ b/src/pretix/control/views/subevents.py @@ -917,6 +917,27 @@ class SubEventBulkCreate(SubEventEditorMixin, EventPermissionRequiredMixin, Asyn if len(subevents) > 100_000: raise ValidationError(_('Please do not create more than 100.000 dates at once.')) + if form.cleaned_data.get("skip_if_overlap") and subevents: + date_min = min(se.date_from for se in subevents) + date_max = max(se.date_to or se.date_from for se in subevents) + dates_existing = list(self.request.event.subevents.annotate( + date_fromto=Coalesce('date_to', 'date_from'), + ).filter( + date_from__lte=date_max, + date_fromto__gte=date_min, + ).values('date_from', 'date_fromto')) + subevents = [ + se for se in subevents if not any( + (se.date_from <= other['date_from'] <= (se.date_to or se.date_from)) + or (se.date_from <= other['date_fromto'] <= (se.date_to or se.date_from)) + or (other['date_from'] <= se.date_from <= other['date_fromto']) + or (other['date_from'] <= (se.date_to or se.date_from) <= other['date_fromto']) + for other in dates_existing + ) + ] + if not subevents: + raise ValidationError(_('All dates would be skipped because they conflict with existing dates.')) + for i, se in enumerate(subevents): se.save(clear_cache=False) if i % 100 == 0: