forked from CGM_Public/pretix_original
Cancelling events: Allow to inform waiting list
This commit is contained in:
@@ -12,9 +12,10 @@ from pretix.base.email import get_email_context
|
|||||||
from pretix.base.i18n import language
|
from pretix.base.i18n import language
|
||||||
from pretix.base.models import (
|
from pretix.base.models import (
|
||||||
Event, InvoiceAddress, Order, OrderFee, OrderPosition, SubEvent, User,
|
Event, InvoiceAddress, Order, OrderFee, OrderPosition, SubEvent, User,
|
||||||
|
WaitingListEntry,
|
||||||
)
|
)
|
||||||
from pretix.base.services.locking import LockTimeoutException
|
from pretix.base.services.locking import LockTimeoutException
|
||||||
from pretix.base.services.mail import SendMailException, TolerantDict
|
from pretix.base.services.mail import SendMailException, TolerantDict, mail
|
||||||
from pretix.base.services.orders import (
|
from pretix.base.services.orders import (
|
||||||
OrderChangeManager, OrderError, _cancel_order, _try_auto_refund,
|
OrderChangeManager, OrderError, _cancel_order, _try_auto_refund,
|
||||||
)
|
)
|
||||||
@@ -24,6 +25,22 @@ from pretix.celery_app import app
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _send_wle_mail(wle: WaitingListEntry, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent):
|
||||||
|
with language(wle.locale):
|
||||||
|
email_context = get_email_context(event_or_subevent=subevent or wle.event, event=wle.event)
|
||||||
|
try:
|
||||||
|
mail(
|
||||||
|
wle.email,
|
||||||
|
str(subject).format_map(TolerantDict(email_context)),
|
||||||
|
message,
|
||||||
|
email_context,
|
||||||
|
wle.event,
|
||||||
|
locale=wle.locale
|
||||||
|
)
|
||||||
|
except SendMailException:
|
||||||
|
logger.exception('Waiting list canceled email could not be sent')
|
||||||
|
|
||||||
|
|
||||||
def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent,
|
def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString, subevent: SubEvent,
|
||||||
refund_amount: Decimal, user: User, positions: list):
|
refund_amount: Decimal, user: User, positions: list):
|
||||||
with language(order.locale):
|
with language(order.locale):
|
||||||
@@ -68,10 +85,14 @@ def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString, s
|
|||||||
|
|
||||||
@app.task(base=ProfiledEventTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,))
|
@app.task(base=ProfiledEventTask, bind=True, max_retries=5, default_retry_delay=1, throws=(OrderError,))
|
||||||
def cancel_event(self, event: Event, subevent: int, auto_refund: bool, keep_fee_fixed: str,
|
def cancel_event(self, event: Event, subevent: int, auto_refund: bool, keep_fee_fixed: str,
|
||||||
keep_fee_percentage: str, keep_fees: bool, send: bool, send_subject: dict,
|
keep_fee_percentage: str, keep_fees: bool,
|
||||||
send_message: dict, user: int):
|
send: bool, send_subject: dict, send_message: dict,
|
||||||
|
send_waitinglist: bool=False, send_waitinglist_subject: dict={}, send_waitinglist_message: dict={},
|
||||||
|
user: int=None):
|
||||||
send_subject = LazyI18nString(send_subject)
|
send_subject = LazyI18nString(send_subject)
|
||||||
send_message = LazyI18nString(send_message)
|
send_message = LazyI18nString(send_message)
|
||||||
|
send_waitinglist_subject = LazyI18nString(send_waitinglist_subject)
|
||||||
|
send_waitinglist_message = LazyI18nString(send_waitinglist_message)
|
||||||
if user:
|
if user:
|
||||||
user = User.objects.get(pk=user)
|
user = User.objects.get(pk=user)
|
||||||
|
|
||||||
@@ -115,7 +136,7 @@ def cancel_event(self, event: Event, subevent: int, auto_refund: bool, keep_fee_
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
orders_to_change = event.orders.none()
|
orders_to_change = event.orders.none()
|
||||||
subevent.log_action(
|
event.log_action(
|
||||||
'pretix.event.canceled', user=user,
|
'pretix.event.canceled', user=user,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -195,4 +216,7 @@ def cancel_event(self, event: Event, subevent: int, auto_refund: bool, keep_fee_
|
|||||||
if send:
|
if send:
|
||||||
_send_mail(o, send_subject, send_message, subevent, refund_amount, user, positions)
|
_send_mail(o, send_subject, send_message, subevent, refund_amount, user, positions)
|
||||||
|
|
||||||
|
for wle in event.waitinglistentries.filter(subevent=subevent, voucher__isnull=True):
|
||||||
|
_send_wle_mail(wle, send_waitinglist_subject, send_waitinglist_message, subevent)
|
||||||
|
|
||||||
return failed
|
return failed
|
||||||
|
|||||||
@@ -552,6 +552,12 @@ class EventCancelForm(forms.Form):
|
|||||||
)
|
)
|
||||||
send_subject = forms.CharField()
|
send_subject = forms.CharField()
|
||||||
send_message = forms.CharField()
|
send_message = forms.CharField()
|
||||||
|
send_waitinglist = forms.BooleanField(
|
||||||
|
label=_("Send information to waiting list"),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
send_waitinglist_subject = forms.CharField()
|
||||||
|
send_waitinglist_message = forms.CharField()
|
||||||
|
|
||||||
def _set_field_placeholders(self, fn, base_parameters):
|
def _set_field_placeholders(self, fn, base_parameters):
|
||||||
phs = [
|
phs = [
|
||||||
@@ -596,6 +602,28 @@ class EventCancelForm(forms.Form):
|
|||||||
'order', 'event'])
|
'order', 'event'])
|
||||||
self._set_field_placeholders('send_message', ['event_or_subevent', 'refund_amount', 'position_or_address',
|
self._set_field_placeholders('send_message', ['event_or_subevent', 'refund_amount', 'position_or_address',
|
||||||
'order', 'event'])
|
'order', 'event'])
|
||||||
|
self.fields['send_waitinglist_subject'] = I18nFormField(
|
||||||
|
label=_("Subject"),
|
||||||
|
required=True,
|
||||||
|
initial=_('Canceled: {event}'),
|
||||||
|
widget=I18nTextInput,
|
||||||
|
locales=self.event.settings.get('locales'),
|
||||||
|
)
|
||||||
|
self.fields['send_waitinglist_message'] = I18nFormField(
|
||||||
|
label=_('Message'),
|
||||||
|
widget=I18nTextarea,
|
||||||
|
required=True,
|
||||||
|
locales=self.event.settings.get('locales'),
|
||||||
|
initial=LazyI18nString.from_gettext(gettext_noop(
|
||||||
|
'Hello,\n\n'
|
||||||
|
'with this email, we regret to inform you that {event} has been canceled.\n\n'
|
||||||
|
'You will therefore not receive a ticket from the waiting list.\n\n'
|
||||||
|
'Best regards,\n\n'
|
||||||
|
'Your {event} team'
|
||||||
|
))
|
||||||
|
)
|
||||||
|
self._set_field_placeholders('send_waitinglist_subject', ['event_or_subevent', 'event'])
|
||||||
|
self._set_field_placeholders('send_waitinglist_message', ['event_or_subevent', 'event'])
|
||||||
|
|
||||||
if self.event.has_subevents:
|
if self.event.has_subevents:
|
||||||
self.fields['subevent'].queryset = self.event.subevents.all()
|
self.fields['subevent'].queryset = self.event.subevents.all()
|
||||||
|
|||||||
@@ -43,6 +43,26 @@
|
|||||||
{% bootstrap_field form.send_subject layout="horizontal" %}
|
{% bootstrap_field form.send_subject layout="horizontal" %}
|
||||||
{% bootstrap_field form.send_message layout="horizontal" %}
|
{% bootstrap_field form.send_message layout="horizontal" %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>{% trans "Waiting list" %}</legend>
|
||||||
|
<p>
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
Your waiting list will not be deleted automatically, but it will receive no new tickets due to the
|
||||||
|
products being disabled. You can choose to inform people on the waiting list by using this option.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>
|
||||||
|
{% blocktrans trimmed %}
|
||||||
|
You should not execute this function multiple times for the same event, or everyone on the
|
||||||
|
waiting list will get multiple emails.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</strong>
|
||||||
|
</p>
|
||||||
|
{% bootstrap_field form.send_waitinglist layout="control" %}
|
||||||
|
{% bootstrap_field form.send_waitinglist_subject layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.send_waitinglist_message layout="horizontal" %}
|
||||||
|
</fieldset>
|
||||||
<div class="form-group submit-group">
|
<div class="form-group submit-group">
|
||||||
<button type="submit" class="btn btn-danger btn-save">
|
<button type="submit" class="btn btn-danger btn-save">
|
||||||
{% trans "Cancel all orders" %}
|
{% trans "Cancel all orders" %}
|
||||||
|
|||||||
@@ -1915,6 +1915,9 @@ class EventCancel(EventPermissionRequiredMixin, AsyncAction, FormView):
|
|||||||
send=form.cleaned_data.get('send'),
|
send=form.cleaned_data.get('send'),
|
||||||
send_subject=form.cleaned_data.get('send_subject').data,
|
send_subject=form.cleaned_data.get('send_subject').data,
|
||||||
send_message=form.cleaned_data.get('send_message').data,
|
send_message=form.cleaned_data.get('send_message').data,
|
||||||
|
send_waitinglist=form.cleaned_data.get('send'),
|
||||||
|
send_waitinglist_subject=form.cleaned_data.get('send_waitinglist_subject').data,
|
||||||
|
send_waitinglist_message=form.cleaned_data.get('send_waitinglist_message').data,
|
||||||
user=self.request.user.pk,
|
user=self.request.user.pk,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ from django.test import TestCase
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django_scopes import scope
|
from django_scopes import scope
|
||||||
|
|
||||||
from pretix.base.models import Event, Item, Order, OrderPosition, Organizer
|
from pretix.base.models import (
|
||||||
|
Event, Item, Order, OrderPosition, Organizer, Voucher, WaitingListEntry,
|
||||||
|
)
|
||||||
from pretix.base.models.orders import OrderFee, OrderPayment, OrderRefund
|
from pretix.base.models.orders import OrderFee, OrderPayment, OrderRefund
|
||||||
from pretix.base.services.cancelevent import cancel_event
|
from pretix.base.services.cancelevent import cancel_event
|
||||||
from pretix.base.services.invoices import generate_invoice
|
from pretix.base.services.invoices import generate_invoice
|
||||||
@@ -310,3 +312,22 @@ class SubEventCancelTests(TestCase):
|
|||||||
assert self.order.positions.filter(subevent=self.se1).count() == 0
|
assert self.order.positions.filter(subevent=self.se1).count() == 0
|
||||||
f = self.order.fees.get(fee_type=OrderFee.FEE_TYPE_CANCELLATION)
|
f = self.order.fees.get(fee_type=OrderFee.FEE_TYPE_CANCELLATION)
|
||||||
assert f.value == Decimal('1.80')
|
assert f.value == Decimal('1.80')
|
||||||
|
|
||||||
|
@classscope(attr='o')
|
||||||
|
def test_cancel_send_mail_waitinglist(self):
|
||||||
|
v = Voucher.objects.create(event=self.event, block_quota=True, redeemed=1)
|
||||||
|
WaitingListEntry.objects.create(
|
||||||
|
event=self.event, item=self.ticket, variation=None, email='foo@bar.com', voucher=v
|
||||||
|
)
|
||||||
|
WaitingListEntry.objects.create(
|
||||||
|
event=self.event, item=self.ticket, variation=None, email='foo@example.org'
|
||||||
|
)
|
||||||
|
cancel_event(
|
||||||
|
self.event.pk, subevent=None,
|
||||||
|
auto_refund=True, keep_fee_fixed="0.00", keep_fee_percentage="0.00",
|
||||||
|
keep_fees=True, send=False, send_subject="Event canceled", send_message="Event canceled :-(",
|
||||||
|
send_waitinglist=True, send_waitinglist_message="Event canceled", send_waitinglist_subject=":(",
|
||||||
|
user=None
|
||||||
|
)
|
||||||
|
assert len(djmail.outbox) == 1
|
||||||
|
assert djmail.outbox[0].to == ['foo@example.org']
|
||||||
|
|||||||
Reference in New Issue
Block a user