Add order lifecycle signals

This commit is contained in:
Raphael Michel
2019-04-06 15:05:16 +02:00
parent c372bffc57
commit b686978074
6 changed files with 251 additions and 171 deletions

View File

@@ -20,7 +20,7 @@ Order events
There are multiple signals that will be sent out in the ordering cycle: There are multiple signals that will be sent out in the ordering cycle:
.. automodule:: pretix.base.signals .. automodule:: pretix.base.signals
:members: validate_cart, order_fee_calculation, order_paid, order_placed, order_fee_type_name, allow_ticket_download :members: validate_cart, order_fee_calculation, order_paid, order_placed, order_canceled, order_expired, order_modified, order_changed, order_approved, order_denied, order_fee_type_name, allow_ticket_download
Frontend Frontend
-------- --------

View File

@@ -42,7 +42,9 @@ from pretix.base.services.orders import (
extend_order, mark_order_expired, mark_order_refunded, extend_order, mark_order_expired, mark_order_refunded,
) )
from pretix.base.services.tickets import generate from pretix.base.services.tickets import generate
from pretix.base.signals import order_placed, register_ticket_outputs from pretix.base.signals import (
order_modified, order_placed, register_ticket_outputs,
)
class OrderFilter(FilterSet): class OrderFilter(FilterSet):
@@ -451,8 +453,8 @@ class OrderViewSet(viewsets.ModelViewSet):
) )
return super().update(request, *args, **kwargs) return super().update(request, *args, **kwargs)
@transaction.atomic
def perform_update(self, serializer): def perform_update(self, serializer):
with transaction.atomic():
if 'comment' in self.request.data and serializer.instance.comment != self.request.data.get('comment'): if 'comment' in self.request.data and serializer.instance.comment != self.request.data.get('comment'):
serializer.instance.log_action( serializer.instance.log_action(
'pretix.event.order.comment', 'pretix.event.order.comment',
@@ -507,6 +509,9 @@ class OrderViewSet(viewsets.ModelViewSet):
serializer.save() serializer.save()
if 'invoice_address' in self.request.data:
order_modified.send(sender=serializer.instance.event, order=serializer.instance)
def perform_create(self, serializer): def perform_create(self, serializer):
serializer.save() serializer.save()

View File

@@ -42,7 +42,9 @@ from pretix.base.services.mail import SendMailException
from pretix.base.services.pricing import get_price from pretix.base.services.pricing import get_price
from pretix.base.services.tasks import ProfiledTask from pretix.base.services.tasks import ProfiledTask
from pretix.base.signals import ( from pretix.base.signals import (
allow_ticket_download, order_fee_calculation, order_placed, periodic_task, allow_ticket_download, order_approved, order_canceled, order_changed,
order_denied, order_expired, order_fee_calculation, order_placed,
periodic_task,
) )
from pretix.celery_app import app from pretix.celery_app import app
from pretix.helpers.models import modelcopy from pretix.helpers.models import modelcopy
@@ -134,13 +136,13 @@ def mark_order_refunded(order, user=None, auth=None, api_token=None):
) )
@transaction.atomic
def mark_order_expired(order, user=None, auth=None): def mark_order_expired(order, user=None, auth=None):
""" """
Mark this order as expired. This sets the payment status and returns the order object. Mark this order as expired. This sets the payment status and returns the order object.
:param order: The order to change :param order: The order to change
:param user: The user that performed the change :param user: The user that performed the change
""" """
with transaction.atomic():
if isinstance(order, int): if isinstance(order, int):
order = Order.objects.get(pk=order) order = Order.objects.get(pk=order)
if isinstance(user, int): if isinstance(user, int):
@@ -154,16 +156,17 @@ def mark_order_expired(order, user=None, auth=None):
if i: if i:
generate_cancellation(i) generate_cancellation(i)
order_expired.send(order.event, order=order)
return order return order
@transaction.atomic
def approve_order(order, user=None, send_mail: bool=True, auth=None): def approve_order(order, user=None, send_mail: bool=True, auth=None):
""" """
Mark this order as approved Mark this order as approved
:param order: The order to change :param order: The order to change
:param user: The user that performed the change :param user: The user that performed the change
""" """
with transaction.atomic():
if not order.require_approval or not order.status == Order.STATUS_PENDING: if not order.require_approval or not order.status == Order.STATUS_PENDING:
raise OrderError(_('This order is not pending approval.')) raise OrderError(_('This order is not pending approval.'))
@@ -184,6 +187,8 @@ def approve_order(order, user=None, send_mail: bool=True, auth=None):
except Quota.QuotaExceededException: except Quota.QuotaExceededException:
raise OrderError(error_messages['unavailable']) raise OrderError(error_messages['unavailable'])
order_approved.send(order.event, order=order)
invoice = order.invoices.last() # Might be generated by plugin already invoice = order.invoices.last() # Might be generated by plugin already
if order.event.settings.get('invoice_generate') == 'True' and invoice_qualified(order): if order.event.settings.get('invoice_generate') == 'True' and invoice_qualified(order):
if not invoice: if not invoice:
@@ -234,13 +239,13 @@ def approve_order(order, user=None, send_mail: bool=True, auth=None):
return order.pk return order.pk
@transaction.atomic
def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None): def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None):
""" """
Mark this order as canceled Mark this order as canceled
:param order: The order to change :param order: The order to change
:param user: The user that performed the change :param user: The user that performed the change
""" """
with transaction.atomic():
if not order.require_approval or not order.status == Order.STATUS_PENDING: if not order.require_approval or not order.status == Order.STATUS_PENDING:
raise OrderError(_('This order is not pending approval.')) raise OrderError(_('This order is not pending approval.'))
@@ -259,6 +264,8 @@ def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None):
if position.voucher: if position.voucher:
Voucher.objects.filter(pk=position.voucher.pk).update(redeemed=Greatest(0, F('redeemed') - 1)) Voucher.objects.filter(pk=position.voucher.pk).update(redeemed=Greatest(0, F('redeemed') - 1))
order_denied.send(order.event, order=order)
if send_mail: if send_mail:
try: try:
invoice_name = order.invoice_address.name invoice_name = order.invoice_address.name
@@ -294,7 +301,6 @@ def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None):
return order.pk return order.pk
@transaction.atomic
def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device=None, oauth_application=None, def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device=None, oauth_application=None,
cancellation_fee=None): cancellation_fee=None):
""" """
@@ -302,6 +308,7 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
:param order: The order to change :param order: The order to change
:param user: The user that performed the change :param user: The user that performed the change
""" """
with transaction.atomic():
if isinstance(order, int): if isinstance(order, int):
order = Order.objects.get(pk=order) order = Order.objects.get(pk=order)
if isinstance(user, int): if isinstance(user, int):
@@ -381,6 +388,7 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
except SendMailException: except SendMailException:
logger.exception('Order canceled email could not be sent') logger.exception('Order canceled email could not be sent')
order_canceled.send(order.event, order=order)
return order.pk return order.pk
@@ -1377,6 +1385,8 @@ class OrderChangeManager:
if self.split_order: if self.split_order:
self._notify_user(self.split_order) self._notify_user(self.split_order)
order_changed.send(self.order.event, order=self.order)
def _clear_tickets_cache(self): def _clear_tickets_cache(self):
CachedTicket.objects.filter(order_position__order=self.order).delete() CachedTicket.objects.filter(order_position__order=self.order).delete()
CachedCombinedTicket.objects.filter(order=self.order).delete() CachedCombinedTicket.objects.filter(order=self.order).delete()

View File

@@ -275,6 +275,66 @@ because an already-paid order has been split.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event. As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
""" """
order_canceled = EventPluginSignal(
providing_args=["order"]
)
"""
This signal is sent out every time an order is canceled. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_expired = EventPluginSignal(
providing_args=["order"]
)
"""
This signal is sent out every time an order is marked as expired. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_modified = EventPluginSignal(
providing_args=["order"]
)
"""
This signal is sent out every time an order's information is modified. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_changed = EventPluginSignal(
providing_args=["order"]
)
"""
This signal is sent out every time an order's content is changed. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_approved = EventPluginSignal(
providing_args=["order"]
)
"""
This signal is sent out every time an order is being approved. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
order_denied = EventPluginSignal(
providing_args=["order"]
)
"""
This signal is sent out every time an order is being denied. The order object is given
as the first argument.
As with all event-plugin signals, the ``sender`` keyword argument will contain the event.
"""
logentry_display = EventPluginSignal( logentry_display = EventPluginSignal(
providing_args=["logentry"] providing_args=["logentry"]
) )

View File

@@ -57,7 +57,7 @@ from pretix.base.services.orders import (
from pretix.base.services.stats import order_overview from pretix.base.services.stats import order_overview
from pretix.base.services.tickets import generate from pretix.base.services.tickets import generate
from pretix.base.signals import ( from pretix.base.signals import (
register_data_exporters, register_ticket_outputs, order_modified, register_data_exporters, register_ticket_outputs,
) )
from pretix.base.templatetags.money import money_filter from pretix.base.templatetags.money import money_filter
from pretix.base.templatetags.rich_text import markdown_compile_email from pretix.base.templatetags.rich_text import markdown_compile_email
@@ -1321,6 +1321,8 @@ class OrderModifyInformation(OrderQuestionsViewMixin, OrderView):
CachedTicket.objects.filter(order_position__order=self.order).delete() CachedTicket.objects.filter(order_position__order=self.order).delete()
CachedCombinedTicket.objects.filter(order=self.order).delete() CachedCombinedTicket.objects.filter(order=self.order).delete()
order_modified.send(sender=self.request.event, order=self.order)
return redirect(self.get_order_url()) return redirect(self.get_order_url())

View File

@@ -30,7 +30,9 @@ from pretix.base.services.invoices import (
from pretix.base.services.mail import SendMailException from pretix.base.services.mail import SendMailException
from pretix.base.services.orders import cancel_order, change_payment_provider from pretix.base.services.orders import cancel_order, change_payment_provider
from pretix.base.services.tickets import generate from pretix.base.services.tickets import generate
from pretix.base.signals import allow_ticket_download, register_ticket_outputs from pretix.base.signals import (
allow_ticket_download, order_modified, register_ticket_outputs,
)
from pretix.base.views.mixins import OrderQuestionsViewMixin from pretix.base.views.mixins import OrderQuestionsViewMixin
from pretix.base.views.tasks import AsyncAction from pretix.base.views.tasks import AsyncAction
from pretix.helpers.safedownload import check_token from pretix.helpers.safedownload import check_token
@@ -540,6 +542,7 @@ class OrderModify(EventViewMixin, OrderDetailMixin, OrderQuestionsViewMixin, Tem
for k in f.changed_data for k in f.changed_data
} for f in self.forms] } for f in self.forms]
}) })
order_modified.send(sender=self.request.event, order=self.order)
if self.invoice_form.has_changed(): if self.invoice_form.has_changed():
success_message = ('Your invoice address has been updated. Please contact us if you need us ' success_message = ('Your invoice address has been updated. Please contact us if you need us '
'to regenerate your invoice.') 'to regenerate your invoice.')