diff --git a/doc/api/resources/events.rst b/doc/api/resources/events.rst
index 336214f1a..207cdf8f7 100644
--- a/doc/api/resources/events.rst
+++ b/doc/api/resources/events.rst
@@ -47,6 +47,8 @@ item_meta_properties object Item-specific m
valid_keys object Cryptographic keys for non-default signature schemes.
For performance reason, value is omitted in lists and
only contained in detail views. Value can be cached.
+sales_channels list A list of sales channels this event is available for
+ sale on.
===================================== ========================== =======================================================
@@ -91,6 +93,11 @@ valid_keys object Cryptographic k
The attribute ``valid_keys`` has been added.
+.. versionchanged:: 3.14
+
+ The attribute ``sales_channels`` has been added.
+
+
Endpoints
---------
@@ -147,11 +154,16 @@ Endpoints
"timezone": "Europe/Berlin",
"item_meta_properties": {},
"plugins": [
- "pretix.plugins.banktransfer"
- "pretix.plugins.stripe"
- "pretix.plugins.paypal"
+ "pretix.plugins.banktransfer",
+ "pretix.plugins.stripe",
+ "pretix.plugins.paypal",
"pretix.plugins.ticketoutputpdf"
],
+ "sales_channels": [
+ "web",
+ "pretixpos",
+ "resellers"
+ ]
}
]
}
@@ -170,6 +182,7 @@ Endpoints
only contain the events matching the set criteria. Providing ``?attr[Format]=Seminar`` would return only those
events having set their ``Format`` meta data to ``Seminar``, ``?attr[Format]=`` only those, that have no value
set. Please note that this filter will respect default values set on organizer level.
+ :query sales_channel: If set to a sales channel identifier, only events allowed to be sold on the specified sales channel are returned.
:param organizer: The ``slug`` field of a valid organizer
:statuscode 200: no error
:statuscode 401: Authentication failure
@@ -219,16 +232,21 @@ Endpoints
"timezone": "Europe/Berlin",
"item_meta_properties": {},
"plugins": [
- "pretix.plugins.banktransfer"
- "pretix.plugins.stripe"
- "pretix.plugins.paypal"
+ "pretix.plugins.banktransfer",
+ "pretix.plugins.stripe",
+ "pretix.plugins.paypal",
"pretix.plugins.ticketoutputpdf"
],
"valid_keys": {
"pretix_sig1": [
"LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUNvd0JRWURLMlZ3QXlFQTdBRDcvdkZBMzNFc1k0ejJQSHI3aVpQc1o4bjVkaDBhalA4Z3l6Tm1tSXM9Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo="
]
- }
+ },
+ "sales_channels": [
+ "web",
+ "pretixpos",
+ "resellers"
+ ]
}
:param organizer: The ``slug`` field of the organizer to fetch
@@ -279,6 +297,11 @@ Endpoints
"plugins": [
"pretix.plugins.stripe",
"pretix.plugins.paypal"
+ ],
+ "sales_channels": [
+ "web",
+ "pretixpos",
+ "resellers"
]
}
@@ -314,6 +337,11 @@ Endpoints
"plugins": [
"pretix.plugins.stripe",
"pretix.plugins.paypal"
+ ],
+ "sales_channels": [
+ "web",
+ "pretixpos",
+ "resellers"
]
}
@@ -369,6 +397,11 @@ Endpoints
"plugins": [
"pretix.plugins.stripe",
"pretix.plugins.paypal"
+ ],
+ "sales_channels": [
+ "web",
+ "pretixpos",
+ "resellers"
]
}
@@ -404,6 +437,11 @@ Endpoints
"plugins": [
"pretix.plugins.stripe",
"pretix.plugins.paypal"
+ ],
+ "sales_channels": [
+ "web",
+ "pretixpos",
+ "resellers"
]
}
@@ -473,6 +511,11 @@ Endpoints
"pretix.plugins.stripe",
"pretix.plugins.paypal",
"pretix.plugins.pretixdroid"
+ ],
+ "sales_channels": [
+ "web",
+ "pretixpos",
+ "resellers"
]
}
diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py
index 682fa59ed..f8336a529 100644
--- a/src/pretix/api/serializers/event.py
+++ b/src/pretix/api/serializers/event.py
@@ -124,7 +124,8 @@ class EventSerializer(I18nAwareModelSerializer):
fields = ('name', 'slug', 'live', 'testmode', 'currency', 'date_from',
'date_to', 'date_admission', 'is_public', 'presale_start',
'presale_end', 'location', 'geo_lat', 'geo_lon', 'has_subevents', 'meta_data', 'seating_plan',
- 'plugins', 'seat_category_mapping', 'timezone', 'item_meta_properties', 'valid_keys')
+ 'plugins', 'seat_category_mapping', 'timezone', 'item_meta_properties', 'valid_keys',
+ 'sales_channels')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
diff --git a/src/pretix/api/views/event.py b/src/pretix/api/views/event.py
index c04dba262..f6037ca5b 100644
--- a/src/pretix/api/views/event.py
+++ b/src/pretix/api/views/event.py
@@ -28,6 +28,7 @@ with scopes_disabled():
is_past = django_filters.rest_framework.BooleanFilter(method='is_past_qs')
is_future = django_filters.rest_framework.BooleanFilter(method='is_future_qs')
ends_after = django_filters.rest_framework.IsoDateTimeFilter(method='ends_after_qs')
+ sales_channel = django_filters.rest_framework.CharFilter(method='sales_channel_qs')
class Meta:
model = Event
@@ -69,6 +70,9 @@ with scopes_disabled():
else:
return queryset.exclude(expr)
+ def sales_channel_qs(self, queryset, name, value):
+ return queryset.filter(sales_channels__contains=value)
+
class EventViewSet(viewsets.ModelViewSet):
serializer_class = EventSerializer
diff --git a/src/pretix/base/migrations/0172_event_sales_channels.py b/src/pretix/base/migrations/0172_event_sales_channels.py
new file mode 100644
index 000000000..f04b3abf3
--- /dev/null
+++ b/src/pretix/base/migrations/0172_event_sales_channels.py
@@ -0,0 +1,20 @@
+# Generated by Django 3.0.9 on 2020-12-02 12:37
+
+from django.db import migrations
+
+import pretix.base.models.fields
+from pretix.base.channels import get_all_sales_channels
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('pretixbase', '0171_auto_20201126_1635'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='event',
+ name='sales_channels',
+ field=pretix.base.models.fields.MultiStringField(default=list(get_all_sales_channels().keys())),
+ ),
+ ]
diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py
index e42bcc242..15b258dd9 100644
--- a/src/pretix/base/models/event.py
+++ b/src/pretix/base/models/event.py
@@ -23,6 +23,7 @@ from django_scopes import ScopedManager, scopes_disabled
from i18nfield.fields import I18nCharField, I18nTextField
from pretix.base.models.base import LoggedModel
+from pretix.base.models.fields import MultiStringField
from pretix.base.reldate import RelativeDateWrapper
from pretix.base.validators import EventSlugBanlistValidator
from pretix.helpers.database import GroupConcat
@@ -331,6 +332,8 @@ class Event(EventMixin, LoggedModel):
:type plugins: str
:param has_subevents: Enable event series functionality
:type has_subevents: bool
+ :param sales_channels: A list of sales channel identifiers, that this event is available for sale on
+ :type sales_channels: list
"""
settings_namespace = 'event'
@@ -409,7 +412,11 @@ class Event(EventMixin, LoggedModel):
)
seating_plan = models.ForeignKey('SeatingPlan', on_delete=models.PROTECT, null=True, blank=True,
related_name='events')
-
+ sales_channels = MultiStringField(
+ verbose_name=_('Restrict to specific sales channels'),
+ help_text=_('Only sell tickets for this event on the following sales channels.'),
+ default=['web'],
+ )
objects = ScopedManager(organizer='organizer')
class Meta:
diff --git a/src/pretix/control/forms/event.py b/src/pretix/control/forms/event.py
index b6085b838..fa85e51ea 100644
--- a/src/pretix/control/forms/event.py
+++ b/src/pretix/control/forms/event.py
@@ -5,7 +5,7 @@ from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from django.db.models import Q
-from django.forms import formset_factory
+from django.forms import CheckboxSelectMultiple, formset_factory
from django.urls import reverse
from django.utils.html import escape
from django.utils.safestring import mark_safe
@@ -311,6 +311,16 @@ class EventUpdateForm(I18nModelForm):
required=False,
help_text=_('You need to configure the custom domain in the webserver beforehand.')
)
+ self.fields['sales_channels'] = forms.MultipleChoiceField(
+ label=self.fields['sales_channels'].label,
+ help_text=self.fields['sales_channels'].help_text,
+ required=self.fields['sales_channels'].required,
+ initial=self.fields['sales_channels'].initial,
+ choices=(
+ (c.identifier, c.verbose_name) for c in get_all_sales_channels().values()
+ ),
+ widget=forms.CheckboxSelectMultiple
+ )
def clean_domain(self):
d = self.cleaned_data['domain']
@@ -367,6 +377,7 @@ class EventUpdateForm(I18nModelForm):
'location',
'geo_lat',
'geo_lon',
+ 'sales_channels'
]
field_classes = {
'date_from': SplitDateTimeField,
@@ -381,6 +392,7 @@ class EventUpdateForm(I18nModelForm):
'date_admission': SplitDateTimePickerWidget(attrs={'data-date-default': '#id_date_from_0'}),
'presale_start': SplitDateTimePickerWidget(),
'presale_end': SplitDateTimePickerWidget(attrs={'data-date-after': '#id_presale_start_0'}),
+ 'sales_channels': CheckboxSelectMultiple()
}
diff --git a/src/pretix/control/templates/pretixcontrol/event/settings.html b/src/pretix/control/templates/pretixcontrol/event/settings.html
index 9b7c678ba..13117c713 100644
--- a/src/pretix/control/templates/pretixcontrol/event/settings.html
+++ b/src/pretix/control/templates/pretixcontrol/event/settings.html
@@ -219,6 +219,7 @@
{% if sform.event_list_type %}
{% bootstrap_field sform.event_list_type layout="control" %}
{% endif %}
+ {% bootstrap_field form.sales_channels layout="control" %}