mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
* Allow to directly bulk-send vouchers via email * Send mails * Log messages * Fix test failures * Add new test cases
This commit is contained in:
@@ -13,7 +13,7 @@ class PretixBaseConfig(AppConfig):
|
|||||||
from . import invoice # NOQA
|
from . import invoice # NOQA
|
||||||
from . import notifications # NOQA
|
from . import notifications # NOQA
|
||||||
from . import email # NOQA
|
from . import email # NOQA
|
||||||
from .services import auth, checkin, export, mail, tickets, cart, orders, invoices, cleanup, update_check, quotas, notifications # NOQA
|
from .services import auth, checkin, export, mail, tickets, cart, orders, invoices, cleanup, update_check, quotas, notifications, vouchers # NOQA
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from .celery_app import app as celery_app # NOQA
|
from .celery_app import app as celery_app # NOQA
|
||||||
|
|||||||
@@ -377,6 +377,23 @@ def base_placeholders(sender, **kwargs):
|
|||||||
'code', ['waiting_list_entry'], lambda waiting_list_entry: waiting_list_entry.voucher.code,
|
'code', ['waiting_list_entry'], lambda waiting_list_entry: waiting_list_entry.voucher.code,
|
||||||
'68CYU2H6ZTP3WLK5'
|
'68CYU2H6ZTP3WLK5'
|
||||||
),
|
),
|
||||||
|
SimpleFunctionalMailTextPlaceholder(
|
||||||
|
'voucher_list', ['voucher_list'], lambda voucher_list: '\n'.join(voucher_list),
|
||||||
|
' 68CYU2H6ZTP3WLK5\n 7MB94KKPVEPSMVF2'
|
||||||
|
),
|
||||||
|
SimpleFunctionalMailTextPlaceholder(
|
||||||
|
'url', ['event', 'voucher_list'], lambda event, voucher_list: build_absolute_uri(event, 'presale:event.index', kwargs={
|
||||||
|
'event': event.slug,
|
||||||
|
'organizer': event.organizer.slug,
|
||||||
|
}), lambda event: build_absolute_uri(event, 'presale:event.index', kwargs={
|
||||||
|
'event': event.slug,
|
||||||
|
'organizer': event.organizer.slug,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
SimpleFunctionalMailTextPlaceholder(
|
||||||
|
'name', ['name'], lambda name: name,
|
||||||
|
_('John Doe')
|
||||||
|
),
|
||||||
SimpleFunctionalMailTextPlaceholder(
|
SimpleFunctionalMailTextPlaceholder(
|
||||||
'comment', ['comment'], lambda comment: comment,
|
'comment', ['comment'], lambda comment: comment,
|
||||||
_('An individual text with a reason can be inserted here.'),
|
_('An individual text with a reason can be inserted here.'),
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
|
|||||||
})
|
})
|
||||||
renderer = ClassicMailRenderer(None)
|
renderer = ClassicMailRenderer(None)
|
||||||
content_plain = body_plain = render_mail(template, context)
|
content_plain = body_plain = render_mail(template, context)
|
||||||
subject = str(subject).format_map(context)
|
subject = str(subject).format_map(TolerantDict(context))
|
||||||
sender = sender or (event.settings.get('mail_from') if event else settings.MAIL_FROM)
|
sender = sender or (event.settings.get('mail_from') if event else settings.MAIL_FROM)
|
||||||
if event:
|
if event:
|
||||||
sender_name = event.settings.mail_from_name or str(event.name)
|
sender_name = event.settings.mail_from_name or str(event.name)
|
||||||
|
|||||||
46
src/pretix/base/services/vouchers.py
Normal file
46
src/pretix/base/services/vouchers.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
from django.utils.translation import gettext
|
||||||
|
from i18nfield.strings import LazyI18nString
|
||||||
|
|
||||||
|
from pretix.base.email import get_email_context
|
||||||
|
from pretix.base.i18n import language
|
||||||
|
from pretix.base.models import Event, User, Voucher
|
||||||
|
from pretix.base.services.mail import mail
|
||||||
|
from pretix.base.services.tasks import ProfiledEventTask
|
||||||
|
from pretix.celery_app import app
|
||||||
|
|
||||||
|
|
||||||
|
@app.task(base=ProfiledEventTask)
|
||||||
|
def vouchers_send(event: Event, vouchers: list, subject: str, message: str, recipients: list, user: int) -> None:
|
||||||
|
vouchers = list(Voucher.objects.filter(id__in=vouchers).order_by('id'))
|
||||||
|
user = User.objects.get(pk=user)
|
||||||
|
for r in recipients:
|
||||||
|
voucher_list = []
|
||||||
|
for i in range(r['number']):
|
||||||
|
voucher_list.append(vouchers.pop())
|
||||||
|
with language(event.settings.locale):
|
||||||
|
email_context = get_email_context(event=event, name=r.get('name') or '', voucher_list=[v.code for v in voucher_list])
|
||||||
|
mail(
|
||||||
|
r['email'],
|
||||||
|
subject,
|
||||||
|
LazyI18nString(message),
|
||||||
|
email_context,
|
||||||
|
event,
|
||||||
|
locale=event.settings.locale,
|
||||||
|
)
|
||||||
|
for v in voucher_list:
|
||||||
|
if r.get('tag') and r.get('tag') != v.tag:
|
||||||
|
v.tag = r.get('tag')
|
||||||
|
if v.comment:
|
||||||
|
v.comment += '\n\n'
|
||||||
|
v.comment = gettext('The voucher has been sent to {recipient}.').format(recipient=r['email'])
|
||||||
|
v.save(update_fields=['tag', 'comment'])
|
||||||
|
v.log_action(
|
||||||
|
'pretix.voucher.sent',
|
||||||
|
user=user,
|
||||||
|
data={
|
||||||
|
'recipient': r['email'],
|
||||||
|
'name': r.get('name'),
|
||||||
|
'subject': subject,
|
||||||
|
'message': message,
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
|
import csv
|
||||||
|
from collections import namedtuple
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
|
from django.core.validators import EmailValidator
|
||||||
from django.db.models.functions import Lower
|
from django.db.models.functions import Lower
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
|
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
|
||||||
from django_scopes.forms import SafeModelChoiceField
|
from django_scopes.forms import SafeModelChoiceField
|
||||||
|
|
||||||
from pretix.base.forms import I18nModelForm
|
from pretix.base.email import get_available_placeholders
|
||||||
|
from pretix.base.forms import I18nModelForm, PlaceholderValidator
|
||||||
from pretix.base.models import Item, Voucher
|
from pretix.base.models import Item, Voucher
|
||||||
from pretix.control.forms import SplitDateTimeField, SplitDateTimePickerWidget
|
from pretix.control.forms import SplitDateTimeField, SplitDateTimePickerWidget
|
||||||
from pretix.control.forms.widgets import Select2, Select2ItemVarQuota
|
from pretix.control.forms.widgets import Select2, Select2ItemVarQuota
|
||||||
@@ -191,6 +197,54 @@ class VoucherBulkForm(VoucherForm):
|
|||||||
),
|
),
|
||||||
required=True
|
required=True
|
||||||
)
|
)
|
||||||
|
send = forms.BooleanField(
|
||||||
|
label=_("Send vouchers via email"),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
send_subject = forms.CharField(
|
||||||
|
label=_("Subject"),
|
||||||
|
widget=forms.TextInput(attrs={'data-display-dependency': '#id_send'}),
|
||||||
|
required=False,
|
||||||
|
initial=_('Your voucher for {event}')
|
||||||
|
)
|
||||||
|
send_message = forms.CharField(
|
||||||
|
label=_("Message"),
|
||||||
|
widget=forms.Textarea(attrs={'data-display-dependency': '#id_send'}),
|
||||||
|
required=False,
|
||||||
|
initial=_('Hello,\n\n'
|
||||||
|
'with this email, we\'re sending you one or more vouchers for {event}:\n\n{voucher_list}\n\n'
|
||||||
|
'You can redeem them here in our ticket shop:\n\n{url}\n\nBest regards,\n\n'
|
||||||
|
'Your {event} team')
|
||||||
|
)
|
||||||
|
send_recipients = forms.CharField(
|
||||||
|
label=_('Recipients'),
|
||||||
|
widget=forms.Textarea(attrs={
|
||||||
|
'data-display-dependency': '#id_send',
|
||||||
|
'placeholder': 'email,number,name,tag\njohn@example.org,3,John,example\n\n-- {} --\n\njohn@example.org\njane@example.net'.format(
|
||||||
|
_('or')
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
required=False,
|
||||||
|
help_text=_('You can either supply a list of email addresses with one email address per line, or a CSV file with a title column '
|
||||||
|
'and one or more of the columns "email", "number", "name", or "tag".')
|
||||||
|
)
|
||||||
|
Recipient = namedtuple('Recipient', 'email number name tag')
|
||||||
|
|
||||||
|
def _set_field_placeholders(self, fn, base_parameters):
|
||||||
|
phs = [
|
||||||
|
'{%s}' % p
|
||||||
|
for p in sorted(get_available_placeholders(self.instance.event, base_parameters).keys())
|
||||||
|
]
|
||||||
|
ht = _('Available placeholders: {list}').format(
|
||||||
|
list=', '.join(phs)
|
||||||
|
)
|
||||||
|
if self.fields[fn].help_text:
|
||||||
|
self.fields[fn].help_text += ' ' + str(ht)
|
||||||
|
else:
|
||||||
|
self.fields[fn].help_text = ht
|
||||||
|
self.fields[fn].validators.append(
|
||||||
|
PlaceholderValidator(phs)
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Voucher
|
model = Voucher
|
||||||
@@ -213,6 +267,51 @@ class VoucherBulkForm(VoucherForm):
|
|||||||
'max_usages': _('Number of times times EACH of these vouchers can be redeemed.')
|
'max_usages': _('Number of times times EACH of these vouchers can be redeemed.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self._set_field_placeholders('send_subject', ['event', 'name'])
|
||||||
|
self._set_field_placeholders('send_message', ['event', 'voucher_list', 'name'])
|
||||||
|
|
||||||
|
def clean_send_recipients(self):
|
||||||
|
raw = self.cleaned_data['send_recipients']
|
||||||
|
if not raw:
|
||||||
|
return []
|
||||||
|
r = raw.split('\n')
|
||||||
|
res = []
|
||||||
|
if ',' in raw or ';' in raw:
|
||||||
|
if '@' in r[0]:
|
||||||
|
raise ValidationError(_('CSV input needs to contain a header row in the first line.'))
|
||||||
|
dialect = csv.Sniffer().sniff(raw[:1024])
|
||||||
|
reader = csv.DictReader(StringIO(raw), dialect=dialect)
|
||||||
|
if 'email' not in reader.fieldnames:
|
||||||
|
raise ValidationError(_('CSV input needs to contain a field with the header "{header}".').format(header="email"))
|
||||||
|
unknown_fields = [f for f in reader.fieldnames if f not in ('email', 'name', 'tag', 'number')]
|
||||||
|
if unknown_fields:
|
||||||
|
raise ValidationError(_('CSV input contains an unknown field with the header "{header}".').format(header=unknown_fields[0]))
|
||||||
|
for i, row in enumerate(reader):
|
||||||
|
try:
|
||||||
|
EmailValidator()(row['email'])
|
||||||
|
except ValidationError as err:
|
||||||
|
raise ValidationError(_('{value} is not a valid email address.').format(value=row['email'])) from err
|
||||||
|
try:
|
||||||
|
res.append(self.Recipient(
|
||||||
|
name=row.get('name', ''),
|
||||||
|
email=row['email'],
|
||||||
|
number=int(row.get('number', 1)),
|
||||||
|
tag=row.get('tag', None)
|
||||||
|
))
|
||||||
|
except ValueError as err:
|
||||||
|
raise ValidationError(_('Invalid value in row {number}.').format(number=i + 1)) from err
|
||||||
|
else:
|
||||||
|
for e in r:
|
||||||
|
try:
|
||||||
|
EmailValidator()(e.strip())
|
||||||
|
except ValidationError as err:
|
||||||
|
raise ValidationError(_('{value} is not a valid email address.').format(value=e.strip())) from err
|
||||||
|
else:
|
||||||
|
res.append(self.Recipient(email=e, number=1, tag=None, name=''))
|
||||||
|
return res
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
data = super().clean()
|
data = super().clean()
|
||||||
|
|
||||||
@@ -222,6 +321,16 @@ class VoucherBulkForm(VoucherForm):
|
|||||||
if vouchers.exists():
|
if vouchers.exists():
|
||||||
raise ValidationError(_('A voucher with one of these codes already exists.'))
|
raise ValidationError(_('A voucher with one of these codes already exists.'))
|
||||||
|
|
||||||
|
if data.get('send') and not all([data.get('send_subject'), data.get('send_message'), data.get('send_recipients')]):
|
||||||
|
raise ValidationError(_('If vouchers should be sent by email, subject, message and recipients need to be specified.'))
|
||||||
|
|
||||||
|
if data.get('codes') and data.get('send'):
|
||||||
|
recp = self.cleaned_data.get('send_recipients', [])
|
||||||
|
code_len = len(data.get('codes'))
|
||||||
|
recp_len = sum(r.number for r in recp)
|
||||||
|
if code_len != recp_len:
|
||||||
|
raise ValidationError(_('You generated {codes} vouchers, but entered recipients for {recp} vouchers.').format(codes=code_len, recp=recp_len))
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def save(self, event, *args, **kwargs):
|
def save(self, event, *args, **kwargs):
|
||||||
|
|||||||
@@ -244,6 +244,7 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
|||||||
'pretix.control.auth.user.forgot_password.recovered': _('The password has been reset.'),
|
'pretix.control.auth.user.forgot_password.recovered': _('The password has been reset.'),
|
||||||
'pretix.organizer.deleted': _('The organizer "{name}" has been deleted.'),
|
'pretix.organizer.deleted': _('The organizer "{name}" has been deleted.'),
|
||||||
'pretix.voucher.added': _('The voucher has been created.'),
|
'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.added.waitinglist': _('The voucher has been created and sent to a person on the waiting list.'),
|
||||||
'pretix.voucher.changed': _('The voucher has been changed.'),
|
'pretix.voucher.changed': _('The voucher has been changed.'),
|
||||||
'pretix.voucher.deleted': _('The voucher has been deleted.'),
|
'pretix.voucher.deleted': _('The voucher has been deleted.'),
|
||||||
|
|||||||
@@ -74,6 +74,13 @@
|
|||||||
{% bootstrap_field form.comment layout="control" %}
|
{% bootstrap_field form.comment layout="control" %}
|
||||||
{% bootstrap_field form.show_hidden_items layout="control" %}
|
{% bootstrap_field form.show_hidden_items layout="control" %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>{% trans "Send out emails" %}</legend>
|
||||||
|
{% bootstrap_field form.send layout="control" %}
|
||||||
|
{% bootstrap_field form.send_subject layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.send_message layout="horizontal" %}
|
||||||
|
{% bootstrap_field form.send_recipients layout="horizontal" %}
|
||||||
|
</fieldset>
|
||||||
{% eventsignal request.event "pretix.control.signals.voucher_form_html" form=form %}
|
{% eventsignal request.event "pretix.control.signals.voucher_form_html" form=form %}
|
||||||
<div class="form-group submit-group">
|
<div class="form-group submit-group">
|
||||||
<button type="submit" class="btn btn-primary btn-save">
|
<button type="submit" class="btn btn-primary btn-save">
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from django.views.generic import (
|
|||||||
|
|
||||||
from pretix.base.models import CartPosition, LogEntry, OrderPosition, Voucher
|
from pretix.base.models import CartPosition, LogEntry, OrderPosition, Voucher
|
||||||
from pretix.base.models.vouchers import _generate_random_code
|
from pretix.base.models.vouchers import _generate_random_code
|
||||||
|
from pretix.base.services.vouchers import vouchers_send
|
||||||
from pretix.control.forms.filter import VoucherFilterForm, VoucherTagFilterForm
|
from pretix.control.forms.filter import VoucherFilterForm, VoucherTagFilterForm
|
||||||
from pretix.control.forms.vouchers import VoucherBulkForm, VoucherForm
|
from pretix.control.forms.vouchers import VoucherBulkForm, VoucherForm
|
||||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||||
@@ -314,12 +315,26 @@ class VoucherBulkCreate(EventPermissionRequiredMixin, CreateView):
|
|||||||
form.save(self.request.event)
|
form.save(self.request.event)
|
||||||
# We need to query them again as form.save() uses bulk_create which does not fill in .pk values on databases
|
# We need to query them again as form.save() uses bulk_create which does not fill in .pk values on databases
|
||||||
# other than PostgreSQL
|
# other than PostgreSQL
|
||||||
|
voucherids = []
|
||||||
for v in self.request.event.vouchers.filter(code__in=form.cleaned_data['codes']):
|
for v in self.request.event.vouchers.filter(code__in=form.cleaned_data['codes']):
|
||||||
log_entries.append(
|
log_entries.append(
|
||||||
v.log_action('pretix.voucher.added', data=form.cleaned_data, user=self.request.user, save=False)
|
v.log_action('pretix.voucher.added', data=form.cleaned_data, user=self.request.user, save=False)
|
||||||
)
|
)
|
||||||
|
voucherids.append(v.pk)
|
||||||
LogEntry.objects.bulk_create(log_entries)
|
LogEntry.objects.bulk_create(log_entries)
|
||||||
messages.success(self.request, _('The new vouchers have been created.'))
|
|
||||||
|
if form.cleaned_data['send']:
|
||||||
|
vouchers_send.apply_async(kwargs={
|
||||||
|
'event': self.request.event.pk,
|
||||||
|
'vouchers': voucherids,
|
||||||
|
'subject': form.cleaned_data['send_subject'],
|
||||||
|
'message': form.cleaned_data['send_message'],
|
||||||
|
'recipients': [r._asdict() for r in form.cleaned_data['send_recipients']],
|
||||||
|
'user': self.request.user.pk,
|
||||||
|
})
|
||||||
|
messages.success(self.request, _('The new vouchers have been created and will be sent out shortly.'))
|
||||||
|
else:
|
||||||
|
messages.success(self.request, _('The new vouchers have been created.'))
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
def get_form_class(self):
|
def get_form_class(self):
|
||||||
|
|||||||
@@ -273,13 +273,13 @@ var form_handlers = function (el) {
|
|||||||
dependency.on("change", update);
|
dependency.on("change", update);
|
||||||
});
|
});
|
||||||
|
|
||||||
el.find("div[data-display-dependency], input[data-display-dependency]").each(function () {
|
el.find("div[data-display-dependency], textarea[data-display-dependency], input[data-display-dependency]").each(function () {
|
||||||
var dependent = $(this),
|
var dependent = $(this),
|
||||||
dependency = $($(this).attr("data-display-dependency")),
|
dependency = $($(this).attr("data-display-dependency")),
|
||||||
update = function (ev) {
|
update = function (ev) {
|
||||||
var enabled = (dependency.attr("type") === 'checkbox' || dependency.attr("type") === 'radio') ? dependency.prop('checked') : !!dependency.val();
|
var enabled = (dependency.attr("type") === 'checkbox' || dependency.attr("type") === 'radio') ? dependency.prop('checked') : !!dependency.val();
|
||||||
var $toggling = dependent;
|
var $toggling = dependent;
|
||||||
if (dependent.get(0).tagName.toLowerCase() === "input") {
|
if (dependent.get(0).tagName.toLowerCase() !== "div") {
|
||||||
$toggling = dependent.closest('.form-group');
|
$toggling = dependent.closest('.form-group');
|
||||||
}
|
}
|
||||||
if (ev) {
|
if (ev) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import datetime
|
|||||||
import decimal
|
import decimal
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from django.core import mail as djmail
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django_scopes import scopes_disabled
|
from django_scopes import scopes_disabled
|
||||||
from tests.base import SoupTest, extract_form_fields
|
from tests.base import SoupTest, extract_form_fields
|
||||||
@@ -476,6 +477,121 @@ class VoucherFormTest(SoupTest):
|
|||||||
'itemvar': '%d' % self.shirt.pk,
|
'itemvar': '%d' % self.shirt.pk,
|
||||||
}, expected_failure=True)
|
}, expected_failure=True)
|
||||||
|
|
||||||
|
def test_create_bulk_send(self):
|
||||||
|
self._create_bulk_vouchers({
|
||||||
|
'codes': 'ABCDE\nDEFGH',
|
||||||
|
'itemvar': '%d' % self.shirt.pk,
|
||||||
|
'send': 'on',
|
||||||
|
'send_subject': 'Your voucher',
|
||||||
|
'send_message': 'Voucher list: {voucher_list}',
|
||||||
|
'send_recipients': 'foo@example.com\nfoo@example.net'
|
||||||
|
})
|
||||||
|
assert len(djmail.outbox) == 2
|
||||||
|
assert len([m for m in djmail.outbox if m.to == ['foo@example.com']]) == 1
|
||||||
|
assert len([m for m in djmail.outbox if m.to == ['foo@example.net']]) == 1
|
||||||
|
assert len([m for m in djmail.outbox if 'ABCDE' in m.body]) == 1
|
||||||
|
assert len([m for m in djmail.outbox if 'DEFGH' in m.body]) == 1
|
||||||
|
|
||||||
|
def test_create_bulk_send_csv(self):
|
||||||
|
self._create_bulk_vouchers({
|
||||||
|
'codes': 'ABCDE\nDEFGH',
|
||||||
|
'itemvar': '%d' % self.shirt.pk,
|
||||||
|
'send': 'on',
|
||||||
|
'send_subject': 'Your voucher',
|
||||||
|
'send_message': 'Voucher list: {voucher_list}',
|
||||||
|
'send_recipients': 'email,number\nfoo@example.com,2'
|
||||||
|
})
|
||||||
|
assert len(djmail.outbox) == 1
|
||||||
|
assert 'ABCDE' in djmail.outbox[0].body
|
||||||
|
assert 'DEFGH' in djmail.outbox[0].body
|
||||||
|
assert ['foo@example.com'] == djmail.outbox[0].to
|
||||||
|
|
||||||
|
def test_create_bulk_send_csv_tag(self):
|
||||||
|
self._create_bulk_vouchers({
|
||||||
|
'codes': 'ABCDE\nDEFGH',
|
||||||
|
'itemvar': '%d' % self.shirt.pk,
|
||||||
|
'send': 'on',
|
||||||
|
'send_subject': 'Your voucher',
|
||||||
|
'send_message': 'Voucher list: {voucher_list}',
|
||||||
|
'send_recipients': 'email,number,tag\nfoo@example.com,2,mytag'
|
||||||
|
})
|
||||||
|
assert len(djmail.outbox) == 1
|
||||||
|
assert 'ABCDE' in djmail.outbox[0].body
|
||||||
|
assert 'DEFGH' in djmail.outbox[0].body
|
||||||
|
assert ['foo@example.com'] == djmail.outbox[0].to
|
||||||
|
with scopes_disabled():
|
||||||
|
assert Voucher.objects.get(code="ABCDE").tag == "mytag"
|
||||||
|
|
||||||
|
def test_create_bulk_send_invalid_placeholder(self):
|
||||||
|
self._create_bulk_vouchers({
|
||||||
|
'codes': 'ABCDE\nDEFGH',
|
||||||
|
'itemvar': '%d' % self.shirt.pk,
|
||||||
|
'send': 'on',
|
||||||
|
'send_subject': 'Your voucher',
|
||||||
|
'send_message': 'Voucher list: {order}',
|
||||||
|
'send_recipients': 'foo@example.com\nfoo@example.net'
|
||||||
|
}, expected_failure=True)
|
||||||
|
|
||||||
|
def test_create_bulk_send_empty_subject(self):
|
||||||
|
self._create_bulk_vouchers({
|
||||||
|
'codes': 'ABCDE\nDEFGH',
|
||||||
|
'itemvar': '%d' % self.shirt.pk,
|
||||||
|
'send': 'on',
|
||||||
|
'send_subject': '',
|
||||||
|
'send_message': 'Voucher list: {voucher_list}',
|
||||||
|
'send_recipients': 'foo@example.com\nfoo@example.net'
|
||||||
|
}, expected_failure=True)
|
||||||
|
|
||||||
|
def test_create_bulk_send_invalid_mail_list(self):
|
||||||
|
self._create_bulk_vouchers({
|
||||||
|
'codes': 'ABCDE\nDEFGH',
|
||||||
|
'itemvar': '%d' % self.shirt.pk,
|
||||||
|
'send': 'on',
|
||||||
|
'send_subject': 'Your voucher',
|
||||||
|
'send_message': 'Voucher list: {voucher_list}',
|
||||||
|
'send_recipients': 'foooo\nfoo@example.org'
|
||||||
|
}, expected_failure=True)
|
||||||
|
|
||||||
|
def test_create_bulk_send_invalid_mail_count(self):
|
||||||
|
self._create_bulk_vouchers({
|
||||||
|
'codes': 'ABCDE\nDEFGH',
|
||||||
|
'itemvar': '%d' % self.shirt.pk,
|
||||||
|
'send': 'on',
|
||||||
|
'send_subject': 'Your voucher',
|
||||||
|
'send_message': 'Voucher list: {voucher_list}',
|
||||||
|
'send_recipients': 'foooo@example.org'
|
||||||
|
}, expected_failure=True)
|
||||||
|
|
||||||
|
def test_create_bulk_send_missing_csv_header(self):
|
||||||
|
self._create_bulk_vouchers({
|
||||||
|
'codes': 'ABCDE\nDEFGH',
|
||||||
|
'itemvar': '%d' % self.shirt.pk,
|
||||||
|
'send': 'on',
|
||||||
|
'send_subject': 'Your voucher',
|
||||||
|
'send_message': 'Voucher list: {voucher_list}',
|
||||||
|
'send_recipients': 'foooo@example.org,bar,baz'
|
||||||
|
}, expected_failure=True)
|
||||||
|
|
||||||
|
def test_create_bulk_send_missing_csv_header_email(self):
|
||||||
|
self._create_bulk_vouchers({
|
||||||
|
'codes': 'ABCDE\nDEFGH',
|
||||||
|
'itemvar': '%d' % self.shirt.pk,
|
||||||
|
'send': 'on',
|
||||||
|
'send_subject': 'Your voucher',
|
||||||
|
'send_message': 'Voucher list: {voucher_list}',
|
||||||
|
'send_recipients': 'mail,number,tag\nfoooo@example.org,2,baz'
|
||||||
|
}, expected_failure=True)
|
||||||
|
|
||||||
|
def test_create_bulk_send_missing_csv_unknown_header(self):
|
||||||
|
self._create_bulk_vouchers({
|
||||||
|
'codes': 'ABCDE\nDEFGH',
|
||||||
|
'itemvar': '%d' % self.shirt.pk,
|
||||||
|
'send': 'on',
|
||||||
|
'send_subject': 'Your voucher',
|
||||||
|
'send_message': 'Voucher list: {voucher_list}',
|
||||||
|
'send_recipients': 'email,number,flop\nfoooo@example.org,2,baz'
|
||||||
|
}, expected_failure=True)
|
||||||
|
|
||||||
def test_delete_voucher(self):
|
def test_delete_voucher(self):
|
||||||
with scopes_disabled():
|
with scopes_disabled():
|
||||||
v = self.event.vouchers.create(quota=self.quota_tickets)
|
v = self.event.vouchers.create(quota=self.quota_tickets)
|
||||||
|
|||||||
Reference in New Issue
Block a user