diff --git a/src/pretix/control/forms/vouchers.py b/src/pretix/control/forms/vouchers.py index 4fbccea21..1d7d405cc 100644 --- a/src/pretix/control/forms/vouchers.py +++ b/src/pretix/control/forms/vouchers.py @@ -71,7 +71,7 @@ class VoucherForm(I18nModelForm): self.instance.item = None self.instance.variation = None - if not self.instance.pk and Voucher.objects.filter(code=data['code'], event=self.instance.event).exists(): + if 'code' in data and not self.instance.pk and Voucher.objects.filter(code=data['code'], event=self.instance.event).exists(): raise ValidationError(_('A voucher with this code already exists.')) return data @@ -80,3 +80,29 @@ class VoucherForm(I18nModelForm): super().save(commit) return ['item'] + + +class VoucherBulkForm(VoucherForm): + codes = forms.CharField( + widget=forms.Textarea, + label=_("Codes"), + help_text=_( + "Add one voucher code per line" + ) + ) + + class Meta: + model = Voucher + localized_fields = '__all__' + fields = [ + 'valid_until', 'block_quota', 'allow_ignore_quota', 'price', 'tag', 'comment' + ] + + def clean(self): + data = super().clean() + data['codes'] = [a.strip() for a in data['codes'].strip().split("\n")] + + if Voucher.objects.filter(code__in=data['codes'], event=self.instance.event).exists(): + raise ValidationError(_('A voucher with one of this codes already exists.')) + + return data diff --git a/src/pretix/control/templates/pretixcontrol/vouchers/bulk.html b/src/pretix/control/templates/pretixcontrol/vouchers/bulk.html new file mode 100644 index 000000000..81274e393 --- /dev/null +++ b/src/pretix/control/templates/pretixcontrol/vouchers/bulk.html @@ -0,0 +1,43 @@ +{% extends "pretixcontrol/items/base.html" %} +{% load i18n %} +{% load bootstrap3 %} +{% block title %}{% trans "Voucher" %}{% endblock %} +{% block inside %} +

{% trans "Create multiple voucher" %}

+
+ {% csrf_token %} + {% bootstrap_form_errors form %} +
+ {% trans "Voucher codes" %} + {% bootstrap_field form.codes layout="horizontal" %} +
+
+ {% trans "Voucher details" %} + {% bootstrap_field form.valid_until layout="horizontal" %} + {% bootstrap_field form.block_quota layout="horizontal" %} + {% bootstrap_field form.allow_ignore_quota layout="horizontal" %} + {% bootstrap_field form.price layout="horizontal" %} + {% bootstrap_field form.itemvar layout="horizontal" %} +
+
+
+
+ {% blocktrans trimmed %} + If you choose "any product" for a specific quota and choose to reserve quota for this + voucher above, the product can still be unavailable to the voucher holder if another quota + associated with the product is sold out! + {% endblocktrans %} +
+
+
+
+ {% bootstrap_field form.tag layout="horizontal" %} + {% bootstrap_field form.comment layout="horizontal" %} +
+
+ +
+
+{% endblock %} diff --git a/src/pretix/control/templates/pretixcontrol/vouchers/index.html b/src/pretix/control/templates/pretixcontrol/vouchers/index.html index 6e4249460..9f4029576 100644 --- a/src/pretix/control/templates/pretixcontrol/vouchers/index.html +++ b/src/pretix/control/templates/pretixcontrol/vouchers/index.html @@ -19,11 +19,16 @@ {% trans "Create a new voucher" %} + {% trans "Create multiple new vouchers" %} {% else %}

{% trans "Create a new voucher" %} + + {% trans "Create multiple new vouchers" %}

\d+)/delete$', vouchers.VoucherDelete.as_view(), name='event.voucher.delete'), url(r'^vouchers/add$', vouchers.VoucherCreate.as_view(), name='event.vouchers.add'), + url(r'^vouchers/bulk_add$', vouchers.VoucherBulkCreate.as_view(), name='event.vouchers.bulk'), url(r'^orders/(?P[0-9A-Z]+)/transition$', orders.OrderTransition.as_view(), name='event.order.transition'), url(r'^orders/(?P[0-9A-Z]+)/resend$', orders.OrderResendLink.as_view(), diff --git a/src/pretix/control/views/vouchers.py b/src/pretix/control/views/vouchers.py index 7bd31bc78..6e328a5ad 100644 --- a/src/pretix/control/views/vouchers.py +++ b/src/pretix/control/views/vouchers.py @@ -1,3 +1,5 @@ +import copy + from django.contrib import messages from django.core.urlresolvers import resolve, reverse from django.db import transaction @@ -7,7 +9,7 @@ from django.utils.translation import ugettext_lazy as _ from django.views.generic import CreateView, DeleteView, ListView, UpdateView from pretix.base.models import Voucher -from pretix.control.forms.vouchers import VoucherForm +from pretix.control.forms.vouchers import VoucherBulkForm, VoucherForm from pretix.control.permissions import EventPermissionRequiredMixin @@ -125,3 +127,38 @@ class VoucherCreate(EventPermissionRequiredMixin, CreateView): ret = super().form_valid(form) form.instance.log_action('pretix.voucher.added', data=dict(form.cleaned_data), user=self.request.user) return ret + + +class VoucherBulkCreate(EventPermissionRequiredMixin, CreateView): + model = Voucher + form_class = VoucherBulkForm + template_name = 'pretixcontrol/vouchers/bulk.html' + permission = 'can_change_vouchers' + context_object_name = 'voucher' + + def get_success_url(self) -> str: + return reverse('control:event.vouchers', kwargs={ + 'organizer': self.request.event.organizer.slug, + 'event': self.request.event.slug, + }) + + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + kwargs['instance'] = Voucher(event=self.request.event) + return kwargs + + @transaction.atomic() + def form_valid(self, form): + for code in form.cleaned_data['codes']: + obj = copy.copy(form.instance) + obj.event = self.request.event + obj.code = code + data = dict(form.cleaned_data) + data['code'] = code + data['bulk'] = True + del data['codes'] + obj.save() + obj.log_action('pretix.voucher.added', data=data, user=self.request.user) + + messages.success(self.request, _('The new vouchers have been created.')) + return HttpResponseRedirect(self.get_success_url())