New implementation of sales channels (#4111)

Co-authored-by: Martin Gross <gross@rami.io>
This commit is contained in:
Raphael Michel
2024-06-30 19:24:30 +02:00
committed by GitHub
parent 95511b0330
commit 4fb5c6bef0
174 changed files with 2902 additions and 616 deletions

View File

@@ -20,56 +20,83 @@
# <https://www.gnu.org/licenses/>.
#
import logging
import warnings
from collections import OrderedDict
from django.dispatch import receiver
from django.utils.translation import gettext_lazy as _
from pretix.base.signals import register_sales_channels
from pretix.base.signals import (
register_sales_channel_types, register_sales_channels,
)
logger = logging.getLogger(__name__)
_ALL_CHANNELS = None
_ALL_CHANNEL_TYPES = None
class SalesChannel:
class SalesChannelType:
def __repr__(self):
return '<SalesChannel: {}>'.format(self.identifier)
return '<SalesChannelType: {}>'.format(self.identifier)
@property
def identifier(self) -> str:
"""
The internal identifier of this sales channel.
The internal identifier of this sales channel type.
"""
raise NotImplementedError() # NOQA
@property
def verbose_name(self) -> str:
"""
A human-readable name of this sales channel.
A human-readable name of this sales channel type.
"""
raise NotImplementedError() # NOQA
@property
def description(self) -> str:
"""
A human-readable description of this sales channel type.
"""
return ""
@property
def icon(self) -> str:
"""
The name of a Font Awesome icon to represent this channel
This can be:
- The name of a Font Awesome icon to represent this channel type.
- The name of a SVG icon file that is resolvable through the static file system. We recommend to design for a size of 18x14 pixels.
"""
return "circle"
@property
def default_created(self) -> bool:
"""
Indication, if a sales channel of this type should automatically be created for every organizer
"""
return True
@property
def multiple_allowed(self) -> bool:
"""
Indication, if multiple sales channels of this type may exist in the same organizer
"""
return False
@property
def testmode_supported(self) -> bool:
"""
Indication, if a saleschannels supports test mode orders
Indication, if a sales channel of this type supports test mode orders
"""
return True
@property
def payment_restrictions_supported(self) -> bool:
"""
If this property is ``True``, organizers can restrict the usage of payment providers to this sales channel.
If this property is ``True``, organizers can restrict the usage of payment providers to this sales channel type.
Example: pretixPOS provides its own sales channel, ignores the configured payment providers completely and
handles payments locally. Therefor, this property should be set to ``False`` for the pretixPOS sales channel as
Example: pretixPOS provides its own sales channel type, ignores the configured payment providers completely and
handles payments locally. Therefore, this property should be set to ``False`` for the pretixPOS sales channel as
the event organizer cannot restrict the usage of any payment provider through the backend.
"""
return True
@@ -77,8 +104,8 @@ class SalesChannel:
@property
def unlimited_items_per_order(self) -> bool:
"""
If this property is ``True``, purchases made using this sales channel are not limited to the maximum amount of
items defined in the event settings.
If this property is ``True``, purchases made using sales channels of this type are not limited to the maximum
amount of items defined in the event settings.
"""
return False
@@ -96,34 +123,67 @@ class SalesChannel:
"""
return True
@property
def required_event_plugin(self) -> str:
"""
Name of an event plugin that is required for this sales channel to be useful. Defaults to ``None``.
"""
return
def get_all_sales_channels():
global _ALL_CHANNELS
if _ALL_CHANNELS:
return _ALL_CHANNELS
def get_all_sales_channel_types():
from pretix.base.signals import register_sales_channel_types
global _ALL_CHANNEL_TYPES
if _ALL_CHANNEL_TYPES:
return _ALL_CHANNEL_TYPES
channels = []
for recv, ret in register_sales_channels.send(None):
for recv, ret in register_sales_channel_types.send(None):
if isinstance(ret, (list, tuple)):
channels += ret
else:
channels.append(ret)
for recv, ret in register_sales_channels.send(None): # todo: remove me
if isinstance(ret, (list, tuple)):
channels += ret
else:
channels.append(ret)
channels.sort(key=lambda c: c.identifier)
_ALL_CHANNELS = OrderedDict([(c.identifier, c) for c in channels])
if 'web' in _ALL_CHANNELS:
_ALL_CHANNELS.move_to_end('web', last=False)
return _ALL_CHANNELS
_ALL_CHANNEL_TYPES = OrderedDict([(c.identifier, c) for c in channels])
if 'web' in _ALL_CHANNEL_TYPES:
_ALL_CHANNEL_TYPES.move_to_end('web', last=False)
return _ALL_CHANNEL_TYPES
class WebshopSalesChannel(SalesChannel):
def get_all_sales_channels():
# TODO: remove me
warnings.warn('Using get_all_sales_channels() is no longer appropriate, use get_al_sales_channel_types() instead.',
DeprecationWarning, stacklevel=2)
return get_all_sales_channel_types()
class WebshopSalesChannelType(SalesChannelType):
identifier = "web"
verbose_name = _('Online shop')
icon = "globe"
@receiver(register_sales_channels, dispatch_uid="base_register_default_sales_channels")
class ApiSalesChannelType(SalesChannelType):
identifier = "api"
verbose_name = _('API')
description = _('API sales channels come with no built-in functionality, but may be used for custom integrations.')
icon = "exchange"
default_created = False
multiple_allowed = True
SalesChannel = SalesChannelType # TODO: remove me
@receiver(register_sales_channel_types, dispatch_uid="base_register_default_sales_channel_types")
def base_sales_channels(sender, **kwargs):
return (
WebshopSalesChannel(),
WebshopSalesChannelType(),
ApiSalesChannelType(),
)