diff --git a/src/pretix/control/forms/vouchers.py b/src/pretix/control/forms/vouchers.py index 63f8d3069e..81fc6081c0 100644 --- a/src/pretix/control/forms/vouchers.py +++ b/src/pretix/control/forms/vouchers.py @@ -106,6 +106,11 @@ class VoucherForm(I18nModelForm): pass super().__init__(*args, **kwargs) + self.fields['tag'].widget.attrs['data-typeahead-url'] = reverse('control:event.vouchers.tags.typeahead', kwargs={ + 'event': instance.event.slug, + 'organizer': instance.event.organizer.slug, + }) + if instance.event.has_subevents: self.fields['subevent'].queryset = instance.event.subevents.all() self.fields['subevent'].widget = Select2( diff --git a/src/pretix/control/urls.py b/src/pretix/control/urls.py index 31075eb771..a4a8a5e188 100644 --- a/src/pretix/control/urls.py +++ b/src/pretix/control/urls.py @@ -370,6 +370,7 @@ urlpatterns = [ re_path(r'^discounts/add$', discounts.DiscountCreate.as_view(), name='event.items.discounts.add'), re_path(r'^vouchers/$', vouchers.VoucherList.as_view(), name='event.vouchers'), re_path(r'^vouchers/tags/$', vouchers.VoucherTags.as_view(), name='event.vouchers.tags'), + re_path(r'^vouchers/tags/typeahead$', typeahead.voucher_tag_typeahead, name='event.vouchers.tags.typeahead'), re_path(r'^vouchers/rng$', vouchers.VoucherRNG.as_view(), name='event.vouchers.rng'), re_path(r'^vouchers/item_select$', typeahead.itemvarquota_select2, name='event.vouchers.itemselect2'), re_path(r'^vouchers/(?P\d+)/$', vouchers.VoucherUpdate.as_view(), name='event.voucher'), diff --git a/src/pretix/control/views/typeahead.py b/src/pretix/control/views/typeahead.py index 51c77de48e..cfaf33c6ad 100644 --- a/src/pretix/control/views/typeahead.py +++ b/src/pretix/control/views/typeahead.py @@ -975,6 +975,21 @@ def subevent_meta_values(request, organizer, event): }) +@event_permission_required('event.vouchers:read') +def voucher_tag_typeahead(request, **kwargs): + q = request.GET.get('q', '') + tags = request.event.vouchers.filter( + tag__isnull=False, + waitinglistentries__isnull=True, + ).filter( + tag__icontains=q, + ).values_list('tag', flat=True).distinct().order_by('tag')[:10] + + return JsonResponse({ + 'results': [{'name': t} for t in tags] + }) + + def item_meta_values(request, organizer, event): q = request.GET.get('q') propname = request.GET.get('property') diff --git a/src/tests/control/test_vouchers.py b/src/tests/control/test_vouchers.py index 80a9b0e94f..9d8a2ce88e 100644 --- a/src/tests/control/test_vouchers.py +++ b/src/tests/control/test_vouchers.py @@ -44,7 +44,7 @@ from tests.base import SoupTestMixin, extract_form_fields from pretix.base.models import ( Event, Item, ItemVariation, Order, OrderPosition, Organizer, Quota, Team, - User, Voucher, + User, Voucher, WaitingListEntry, ) @@ -771,3 +771,38 @@ class VoucherFormTest(SoupTestMixin, TransactionTestCase): assert len(doc.select('.alert-warning ul li')) == 1 # Check that there's exactly 1 item in the warning list assert doc.text.count('Order DEDUP') == 1 # Check that the order is listed exactly once + + def test_tag_typeahead(self): + with scopes_disabled(): + self.event.vouchers.create(item=self.ticket, code='AAAAAAA', tag='early-bird') + self.event.vouchers.create(item=self.ticket, code='BBBBBBB', tag='early-vip') + self.event.vouchers.create(item=self.ticket, code='CCCCCCC', tag='sponsor') + + url = '/control/event/%s/%s/vouchers/tags/typeahead' % (self.orga.slug, self.event.slug) + resp = self.client.get(url + '?q=early') + + assert resp.status_code == 200 + data = json.loads(resp.content.decode()) + names = [r['name'] for r in data['results']] + + assert 'early-bird' in names + assert 'early-vip' in names + assert 'sponsor' not in names + + def test_tag_typeahead_excludes_waiting_list(self): + with scopes_disabled(): + v = self.event.vouchers.create(item=self.ticket, code='AAAAAAA', tag='waiting-list') + WaitingListEntry.objects.create( + event=self.event, item=self.ticket, email='foo@example.com', voucher=v + ) + self.event.vouchers.create(item=self.ticket, code='BBBBBBB', tag='walk-ins') + + url = '/control/event/%s/%s/vouchers/tags/typeahead' % (self.orga.slug, self.event.slug) + resp = self.client.get(url + '?q=wa') + + assert resp.status_code == 200 + data = json.loads(resp.content.decode()) + names = [r['name'] for r in data['results']] + + assert 'walk-ins' in names + assert 'waiting-list' not in names