diff --git a/src/pretix/base/orderimport.py b/src/pretix/base/orderimport.py index ba5a1ec0c8..09ef25c35e 100644 --- a/src/pretix/base/orderimport.py +++ b/src/pretix/base/orderimport.py @@ -19,6 +19,7 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # +import datetime import re from collections import defaultdict from decimal import Decimal, DecimalException @@ -40,7 +41,7 @@ from pretix.base.channels import get_all_sales_channels from pretix.base.forms.questions import guess_country from pretix.base.models import ( ItemVariation, OrderPosition, Question, QuestionAnswer, QuestionOption, - Seat, + Seat, SubEvent, ) from pretix.base.services.pricing import get_price from pretix.base.settings import ( @@ -160,6 +161,10 @@ class SubeventColumn(ImportColumn): verbose_name = pgettext_lazy('subevents', 'Date') default_value = None + def __init__(self, *args, **kwargs): + self._subevent_cache = {} + super().__init__(*args, **kwargs) + @cached_property def subevents(self): return list(self.event.subevents.filter(active=True).order_by('date_from')) @@ -172,6 +177,30 @@ class SubeventColumn(ImportColumn): def clean(self, value, previous_values): if not value: raise ValidationError(pgettext("subevent", "You need to select a date.")) + + if value in self._subevent_cache: + return self._subevent_cache[value] + + input_formats = formats.get_format('DATETIME_INPUT_FORMATS', use_l10n=True) + for format in input_formats: + try: + d = datetime.datetime.strptime(value, format) + d = self.event.timezone.localize(d) + try: + se = self.event.subevents.get( + active=True, + date_from__gt=d - datetime.timedelta(seconds=1), + date_from__lt=d + datetime.timedelta(seconds=1), + ) + self._subevent_cache[value] = se + return se + except SubEvent.DoesNotExist: + raise ValidationError(pgettext("subevent", "No matching date was found.")) + except SubEvent.MultipleObjectsReturned: + raise ValidationError(pgettext("subevent", "Multiple matching dates were found.")) + except (ValueError, TypeError): + continue + matches = [ p for p in self.subevents if str(p.pk) == value or any( @@ -181,6 +210,8 @@ class SubeventColumn(ImportColumn): raise ValidationError(pgettext("subevent", "No matching date was found.")) if len(matches) > 1: raise ValidationError(pgettext("subevent", "Multiple matching dates were found.")) + + self._subevent_cache[value] = matches[0] return matches[0] def assign(self, value, order, position, invoice_address, **kwargs): diff --git a/src/tests/base/test_orderimport.py b/src/tests/base/test_orderimport.py index b083810281..1c5534e9fc 100644 --- a/src/tests/base/test_orderimport.py +++ b/src/tests/base/test_orderimport.py @@ -20,6 +20,7 @@ # . # import csv +import datetime from decimal import Decimal from io import StringIO @@ -68,6 +69,7 @@ def inputfile_factory(): 'G': 'US', 'H': 'Texas', 'I': 'Foo', + 'J': '2021-06-28 11:00:00', }, { 'A': 'Daniel', @@ -79,6 +81,7 @@ def inputfile_factory(): 'G': 'DE', 'H': '', 'I': 'Bar', + 'J': '2021-06-28 11:00:00', }, { 'A': 'Anke', @@ -90,10 +93,11 @@ def inputfile_factory(): 'G': 'AU', 'H': '', 'I': 'Foo,Bar', + 'J': '2021-06-28 11:00:00', }, ] f = StringIO() - w = csv.DictWriter(f, ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', "I"], dialect=csv.excel) + w = csv.DictWriter(f, ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'], dialect=csv.excel) w.writeheader() w.writerows(d) f.seek(0) @@ -675,7 +679,7 @@ def test_import_subevent_required(user, event, item): @pytest.mark.django_db @scopes_disabled() -def test_import_subevent(user, event, item): +def test_import_subevent_by_name(user, event, item): settings = dict(DEFAULT_SETTINGS) event.has_subevents = True event.save() @@ -689,6 +693,23 @@ def test_import_subevent(user, event, item): assert OrderPosition.objects.filter(subevent=s).count() == 3 +@pytest.mark.django_db +@scopes_disabled() +def test_import_subevent_by_date(user, event, item): + settings = dict(DEFAULT_SETTINGS) + event.has_subevents = True + event.save() + event.settings.timezone = 'Europe/Berlin' + s = event.subevents.create(name='Test', date_from=event.timezone.localize(datetime.datetime(2021, 6, 28, 11, 0, 0, 0)), active=True) + settings['item'] = 'static:{}'.format(item.pk) + settings['subevent'] = 'csv:J' + + import_orders.apply( + args=(event.pk, inputfile_factory().id, settings, 'en', user.pk) + ).get() + assert OrderPosition.objects.filter(subevent=s).count() == 3 + + @pytest.mark.django_db @scopes_disabled() def test_import_question_validate(user, event, item):