diff --git a/doc/development/api/general.rst b/doc/development/api/general.rst index 0e064551ca..8fffe27711 100644 --- a/doc/development/api/general.rst +++ b/doc/development/api/general.rst @@ -19,7 +19,7 @@ Order events There are multiple signals that will be sent out in the ordering cycle: .. automodule:: pretix.base.signals - :members: order_paid, order_placed + :members: validate_cart, order_paid, order_placed Frontend -------- diff --git a/src/pretix/base/services/cart.py b/src/pretix/base/services/cart.py index d209102adf..1fac08175d 100644 --- a/src/pretix/base/services/cart.py +++ b/src/pretix/base/services/cart.py @@ -12,6 +12,7 @@ from pretix.base.models import ( ) from pretix.base.services.async import ProfiledTask from pretix.base.services.locking import LockTimeoutException +from pretix.base.signals import validate_cart from pretix.celery_app import app @@ -207,6 +208,11 @@ def _add_items_to_cart(event: Event, items: List[dict], cart_id: str=None) -> No # TODO: i18n plurals raise CartError(error_messages['max_items'], (event.settings.max_items_per_order,)) + validate_cart.send( + sender=event, positions=CartPosition.objects.filter(Q(cart_id=cart_id) & Q(event=event)), + requested_add=items, requested_delete=[] + ) + expiry = now_dt + timedelta(minutes=event.settings.get('reservation_time', as_type=int)) _extend_existing(event, cart_id, expiry, now_dt) @@ -240,6 +246,11 @@ def add_items_to_cart(self, event: int, items: List[dict], cart_id: str=None) -> def _remove_items_from_cart(event: Event, items: List[dict], cart_id: str) -> None: with event.lock(): + validate_cart.send( + sender=event, positions=CartPosition.objects.filter(Q(cart_id=cart_id) & Q(event=event)), + requested_add=[], requested_delete=items + ) + for i in items: cw = Q(cart_id=cart_id) & Q(item_id=i['item']) & Q(event=event) if i['variation']: diff --git a/src/pretix/base/signals.py b/src/pretix/base/signals.py index 10f12f6769..d9eafd9560 100644 --- a/src/pretix/base/signals.py +++ b/src/pretix/base/signals.py @@ -94,6 +94,19 @@ subclass of pretix.base.exporter.BaseExporter As with all event-plugin signals, the ``sender`` keyword argument will contain the event. """ +validate_cart = EventPluginSignal( + providing_args=["positions", "requested_add", "requested_delete"] +) +""" +This signal is sent out every time a cart is about to be changed. It includes an iterable +with the current CartPosition objects as well as lists of dictionaries of the cart items +that the user wants to add. Those dictionaries can contain the keys ``item``, ``variation``, +``count``, ``price`` and ``voucher``. The response of receivers will be ignored, but you can +raise an OrderError with an appropriate exception message. + +As with all event-plugin signals, the ``sender`` keyword argument will contain the event. +""" + order_placed = EventPluginSignal( providing_args=["order"] )