diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py index 10a4e7fe4..b63045371 100644 --- a/src/pretix/api/serializers/event.py +++ b/src/pretix/api/serializers/event.py @@ -844,6 +844,7 @@ class EventSettingsSerializer(SettingsSerializer): 'reusable_media_type_nfc_mf0aes_autocreate_giftcard', 'reusable_media_type_nfc_mf0aes_autocreate_giftcard_currency', 'reusable_media_type_nfc_mf0aes_random_uid', + 'seating_allow_blocked_seats_for_channel', ] readonly_fields = [ # These are read-only since they are currently only settable on organizers, not events diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index 0ce87e56f..2c8f22f09 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -3363,7 +3363,9 @@ Your {organizer} team""")) # noqa: W291 }, 'seating_allow_blocked_seats_for_channel': { 'default': [], - 'type': list + 'type': list, + 'serializer_class': serializers.ListField, + 'serializer_kwargs': lambda: dict(child=serializers.CharField()), }, 'seating_distance_within_row': { 'default': 'False', @@ -3801,6 +3803,16 @@ def validate_event_settings(event, settings_dict): 'payment_term_last': _('The last payment date cannot be before the end of presale.') }) + if settings_dict.get('seating_allow_blocked_seats_for_channel'): + allowed_channels = set(event.organizer.sales_channels.values_list("identifier", flat=True)) + for channel in settings_dict['seating_allow_blocked_seats_for_channel']: + if channel not in allowed_channels: + raise ValidationError({ + 'seating_allow_blocked_seats_for_channel': _('The value "{identifier}" is not a valid sales channel.').format( + identifier=channel + ) + }) + if isinstance(event, Event): validate_event_settings.send(sender=event, settings_dict=settings_dict) diff --git a/src/tests/api/test_events.py b/src/tests/api/test_events.py index 4c0dae74a..586704ad1 100644 --- a/src/tests/api/test_events.py +++ b/src/tests/api/test_events.py @@ -1274,6 +1274,7 @@ def test_get_event_settings(token_client, organizer, event): ) assert resp.status_code == 200 assert resp.data['imprint_url'] == "https://example.org" + assert resp.data['seating_allow_blocked_seats_for_channel'] == [] resp = token_client.get( '/api/v1/organizers/{}/events/{}/settings/?explain=true'.format(organizer.slug, event.slug), @@ -1301,14 +1302,17 @@ def test_patch_event_settings(token_client, organizer, event): } ], 'reusable_media_active': True, # readonly, ignored + 'seating_allow_blocked_seats_for_channel': ['web'] }, format='json' ) assert resp.status_code == 200 assert resp.data['imprint_url'] == "https://example.com" + assert resp.data['seating_allow_blocked_seats_for_channel'] == ['web'] assert not resp.data['reusable_media_active'] event.settings.flush() assert event.settings.imprint_url == 'https://example.com' + assert event.settings.seating_allow_blocked_seats_for_channel == ['web'] assert not event.settings.reusable_media_active assert event.all_logentries().filter(action_type="pretix.event.settings").count() == 1 @@ -1443,6 +1447,18 @@ def test_patch_event_settings_validation(token_client, organizer, event): 'cancel_allow_user_until': ['Invalid relative date'] } + resp = token_client.patch( + '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), + { + 'seating_allow_blocked_seats_for_channel': ['lolnope'], + }, + format='json' + ) + assert resp.status_code == 400 + assert resp.data == { + 'seating_allow_blocked_seats_for_channel': ['The value \"lolnope\" is not a valid sales channel.'] + } + @pytest.mark.django_db def test_patch_event_settings_file(token_client, organizer, event):