diff --git a/doc/api/resources/webhooks.rst b/doc/api/resources/webhooks.rst
index c7be77326..94dcc1b68 100644
--- a/doc/api/resources/webhooks.rst
+++ b/doc/api/resources/webhooks.rst
@@ -60,6 +60,9 @@ The following values for ``action_types`` are valid with pretix core:
* ``pretix.event.added``
* ``pretix.event.changed``
* ``pretix.event.deleted``
+ * ``pretix.voucher.added``
+ * ``pretix.voucher.changed``
+ * ``pretix.voucher.deleted``
* ``pretix.subevent.added``
* ``pretix.subevent.changed``
* ``pretix.subevent.deleted``
diff --git a/src/pretix/api/webhooks.py b/src/pretix/api/webhooks.py
index 927f0f885..2dcf525e9 100644
--- a/src/pretix/api/webhooks.py
+++ b/src/pretix/api/webhooks.py
@@ -78,6 +78,13 @@ class WebhookEvent:
"""
raise NotImplementedError() # NOQA
+ @property
+ def help_text(self) -> str:
+ """
+ A human-readable description
+ """
+ return ""
+
def get_all_webhook_events():
global _ALL_EVENTS
@@ -97,9 +104,10 @@ def get_all_webhook_events():
class ParametrizedWebhookEvent(WebhookEvent):
- def __init__(self, action_type, verbose_name):
+ def __init__(self, action_type, verbose_name, help_text=""):
self._action_type = action_type
self._verbose_name = verbose_name
+ self._help_text = help_text
super().__init__()
@property
@@ -110,6 +118,10 @@ class ParametrizedWebhookEvent(WebhookEvent):
def verbose_name(self):
return self._verbose_name
+ @property
+ def help_text(self):
+ return self._help_text
+
class ParametrizedOrderWebhookEvent(ParametrizedWebhookEvent):
def build_payload(self, logentry: LogEntry):
@@ -161,6 +173,19 @@ class ParametrizedEventWebhookEvent(ParametrizedWebhookEvent):
}
+class ParametrizedVoucherWebhookEvent(ParametrizedWebhookEvent):
+
+ def build_payload(self, logentry: LogEntry):
+ # do not use content_object, this is also called in deletion
+ return {
+ 'notification_id': logentry.pk,
+ 'organizer': logentry.event.organizer.slug,
+ 'event': logentry.event.slug,
+ 'voucher': logentry.object_id,
+ 'action': logentry.action_type,
+ }
+
+
class ParametrizedSubEventWebhookEvent(ParametrizedWebhookEvent):
def build_payload(self, logentry: LogEntry):
@@ -346,8 +371,9 @@ def register_default_webhook_events(sender, **kwargs):
),
ParametrizedItemWebhookEvent(
'pretix.event.item.*',
- _('Product changed (including product added or deleted and including changes to nested objects like '
- 'variations or bundles)'),
+ _('Product changed'),
+ _('This includes product added or deleted and changes to nested objects like '
+ 'variations or bundles.'),
),
ParametrizedEventWebhookEvent(
'pretix.event.live.activated',
@@ -381,6 +407,19 @@ def register_default_webhook_events(sender, **kwargs):
'pretix.event.orders.waitinglist.voucher_assigned',
_('Waiting list entry received voucher'),
),
+ ParametrizedVoucherWebhookEvent(
+ 'pretix.voucher.added',
+ _('Voucher added'),
+ ),
+ ParametrizedVoucherWebhookEvent(
+ 'pretix.voucher.changed',
+ _('Voucher changed'),
+ _('Only includes explicit changes to the voucher, not e.g. an increase of the number of redemptions.')
+ ),
+ ParametrizedVoucherWebhookEvent(
+ 'pretix.voucher.deleted',
+ _('Voucher deleted'),
+ ),
ParametrizedCustomerWebhookEvent(
'pretix.customer.created',
_('Customer account created'),
diff --git a/src/pretix/base/models/waitinglist.py b/src/pretix/base/models/waitinglist.py
index 38f50de50..f7cf0ff8e 100644
--- a/src/pretix/base/models/waitinglist.py
+++ b/src/pretix/base/models/waitinglist.py
@@ -207,16 +207,19 @@ class WaitingListEntry(LoggedModel):
block_quota=True,
subevent=self.subevent,
)
- v.log_action('pretix.voucher.added.waitinglist', {
+ v.log_action('pretix.voucher.added', {
'item': self.item.pk,
'variation': self.variation.pk if self.variation else None,
'tag': 'waiting-list',
'block_quota': True,
'valid_until': v.valid_until.isoformat(),
'max_usages': 1,
+ 'subevent': self.subevent.pk if self.subevent else None,
+ 'source': 'waitinglist',
+ }, user=user, auth=auth)
+ v.log_action('pretix.voucher.added.waitinglist', {
'email': self.email,
'waitinglistentry': self.pk,
- 'subevent': self.subevent.pk if self.subevent else None,
}, user=user, auth=auth)
self.voucher = v
self.save()
diff --git a/src/pretix/control/forms/organizer.py b/src/pretix/control/forms/organizer.py
index 0b7252931..e3226c776 100644
--- a/src/pretix/control/forms/organizer.py
+++ b/src/pretix/control/forms/organizer.py
@@ -43,7 +43,7 @@ from django.forms import formset_factory, inlineformset_factory
from django.forms.utils import ErrorDict
from django.urls import reverse
from django.utils.crypto import get_random_string
-from django.utils.html import conditional_escape
+from django.utils.html import conditional_escape, format_html
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _, pgettext_lazy
from django_scopes.forms import SafeModelChoiceField
@@ -695,7 +695,9 @@ class WebHookForm(forms.ModelForm):
self.fields['events'].choices = [
(
a.action_type,
- mark_safe('{} – {}'.format(a.verbose_name, a.action_type))
+ format_html('{} – {}
{}', a.verbose_name, a.action_type, a.help_text)
+ if a.help_text else
+ format_html('{} – {}', a.verbose_name, a.action_type)
) for a in get_all_webhook_events().values()
]
if self.instance and self.instance.pk:
diff --git a/src/pretix/control/logdisplay.py b/src/pretix/control/logdisplay.py
index 34066f640..ffbe1e3bb 100644
--- a/src/pretix/control/logdisplay.py
+++ b/src/pretix/control/logdisplay.py
@@ -571,11 +571,11 @@ class CoreOrderLogEntryType(OrderLogEntryType):
@log_entry_types.new_from_dict({
'pretix.voucher.added': _('The voucher has been created.'),
'pretix.voucher.sent': _('The voucher has been sent to {recipient}.'),
- 'pretix.voucher.added.waitinglist': _('The voucher has been created and sent to a person on the waiting list.'),
'pretix.voucher.expired.waitinglist': _(
'The voucher has been set to expire because the recipient removed themselves from the waiting list.'),
'pretix.voucher.changed': _('The voucher has been changed.'),
'pretix.voucher.deleted': _('The voucher has been deleted.'),
+ 'pretix.voucher.added.waitinglist': _('The voucher has been sent to {email} through the waiting list.'),
})
class CoreVoucherLogEntryType(VoucherLogEntryType):
pass
diff --git a/src/pretix/control/views/vouchers.py b/src/pretix/control/views/vouchers.py
index 9990039cb..bee6db27e 100644
--- a/src/pretix/control/views/vouchers.py
+++ b/src/pretix/control/views/vouchers.py
@@ -380,7 +380,7 @@ class VoucherCreate(EventPermissionRequiredMixin, CreateView):
messages.success(self.request, mark_safe(_('The new voucher has been created: {code}').format(
code=format_html('{code}', url=url, code=self.object.code)
)))
- form.instance.log_action('pretix.voucher.added', data=dict(form.cleaned_data), user=self.request.user)
+ form.instance.log_action('pretix.voucher.added', data={**dict(form.cleaned_data), "source": "control"}, user=self.request.user)
return ret
@transaction.atomic
@@ -475,7 +475,7 @@ class VoucherBulkCreate(EventPermissionRequiredMixin, AsyncFormView):
data['bulk'] = True
del data['codes']
log_entries.append(
- v.log_action('pretix.voucher.added', data=data, user=self.request.user, save=False)
+ v.log_action('pretix.voucher.added', data={**data, "source": "control_bulk"}, user=self.request.user, save=False)
)
LogEntry.bulk_create_and_postprocess(log_entries)
form.post_bulk_save(batch_vouchers)
diff --git a/src/tests/base/test_shredders.py b/src/tests/base/test_shredders.py
index d8c9cb22d..e6e86913e 100644
--- a/src/tests/base/test_shredders.py
+++ b/src/tests/base/test_shredders.py
@@ -140,7 +140,7 @@ def test_waitinglist_shredder(event, item):
)
wle.send_voucher()
assert '@' in wle.voucher.comment
- assert '@' in wle.voucher.all_logentries().last().data
+ assert '@' in wle.voucher.all_logentries().get(action_type="pretix.voucher.added.waitinglist").data
s = WaitingListShredder(event)
f = list(s.generate_files())
assert json.loads(f[0][2]) == [
@@ -166,7 +166,7 @@ def test_waitinglist_shredder(event, item):
assert '@' not in wle.email
assert '+49' not in str(wle.phone)
assert '@' not in wle.voucher.comment
- assert '@' not in wle.voucher.all_logentries().last().data
+ assert '@' not in wle.voucher.all_logentries().get(action_type="pretix.voucher.added.waitinglist").data
@pytest.mark.django_db