More tests

This commit is contained in:
Raphael Michel
2026-04-20 23:52:19 +02:00
parent 178a5525d5
commit 20da00d4fb
3 changed files with 291 additions and 64 deletions

View File

@@ -33,13 +33,13 @@
# License for the specific language governing permissions and limitations under the License.
import csv
from collections import namedtuple, Counter
from collections import Counter, namedtuple
from io import StringIO
from django import forms
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.validators import EmailValidator
from django.db.models import Max, Sum, Count, Q, F
from django.db.models import Count, F, Max
from django.db.models.functions import Upper
from django.forms.utils import ErrorDict
from django.urls import reverse
@@ -53,7 +53,7 @@ from pretix.base.forms import (
)
from pretix.base.forms.widgets import format_placeholders_help_text
from pretix.base.i18n import language
from pretix.base.models import Item, Voucher, Quota, SubEvent, ItemVariation
from pretix.base.models import Item, ItemVariation, Quota, SubEvent, Voucher
from pretix.base.services.locking import lock_objects
from pretix.base.services.quotas import QuotaAvailability
from pretix.control.forms import SplitDateTimeField, SplitDateTimePickerWidget
@@ -283,10 +283,10 @@ class VoucherBulkEditForm(VoucherForm):
def clean(self):
# We skip the parent class because it's not suited for bulk editing and implement custom validation here.
# This does not validate everything we validate in VoucherForm. For example, we skip validation that one does
# not create a voucher for an add-on product to save on complexity. This is a UX validation only anyways, since
# one could first create the voucher and then make the product an add-on product. However, we need to validate
# everything that we don't want violated in the database.
# This does not validate *everything* we validate in VoucherForm. For example, we skip validation that one does
# not create a voucher for an add-on product or that the seat matches the product to save on complexity.
# This is a UX validation only anyway, since one could first create the voucher and then make the product an
# add-on product. However, we need to validate everything that we don't want violated in the database.
data = super(VoucherForm, self).clean()
if self.prefix + "itemvar" in self.data.getlist('_bulk'):
@@ -321,7 +321,7 @@ class VoucherBulkEditForm(VoucherForm):
except ObjectDoesNotExist:
raise ValidationError(_("Invalid product selected."))
if self.prefix + "max_usages" in self.data.getlist('_bulk'):
if self.prefix + "max_usages" in self.data.getlist('_bulk') and "max_usages" in data:
max_redeemed = self.queryset.aggregate(m=Max("redeemed"))["m"]
if data["max_usages"] < max_redeemed:
raise ValidationError(_(
@@ -375,26 +375,26 @@ class VoucherBulkEditForm(VoucherForm):
# Predict state after change
after_change = dict(current)
if self.prefix + "itemvar" in self.data.getlist('_bulk'):
if self.prefix + "itemvar" in self.data.getlist('_bulk') and "itemvar" in data:
after_change["item"] = data["item"]
after_change["variation"] = data["variation"]
after_change["quota"] = data["quota"]
if self.prefix + "subevent" in self.data.getlist('_bulk'):
if self.prefix + "subevent" in self.data.getlist('_bulk') and "subevent" in data:
after_change["subevent"] = data["subevent"]
if self.prefix + "max_usages" in self.data.getlist('_bulk'):
if self.prefix + "max_usages" in self.data.getlist('_bulk') and "max_usages" in data:
after_change["max_usages"] = data["max_usages"]
if self.prefix + "block_quota" in self.data.getlist('_bulk'):
if self.prefix + "block_quota" in self.data.getlist('_bulk') and "block_quota" in data:
after_change["block_quota"] = data["block_quota"]
if self.prefix + "valid_until" in self.data.getlist('_bulk'):
if self.prefix + "valid_until" in self.data.getlist('_bulk') and "valid_until" in data:
after_change["valid_until"] = data["valid_until"]
if self.prefix + "allow_ignore_quota" in self.data.getlist('_bulk'):
if self.prefix + "allow_ignore_quota" in self.data.getlist('_bulk') and "allow_ignore_quota" in data:
after_change["allow_ignore_quota"] = data["allow_ignore_quota"]
if after_change["quota"] and self.event.has_subevents and not after_change["subevent"]:
raise _("You cannot create a voucher that allows selection of a quota but has no date selected.")
raise ValidationError(_("You cannot create a voucher that allows selection of a quota but has no date selected."))
if after_change["quota"] and after_change["subevent"] and after_change["quota"].subevent_id != after_change["subevent"].pk:
raise _("The selected quota does not match the selected subevent.")
raise ValidationError(_("The selected quota does not match the selected subevent."))
if after_change["block_quota"] and self.event.has_subevents and not after_change["subevent"]:
raise ValidationError(
@@ -410,7 +410,7 @@ class VoucherBulkEditForm(VoucherForm):
# todo: is this the most useful way to do this?
continue
will_be_valid = current["valid_until"] is None or current["valid_until"] >= now()
will_be_valid = after_change["valid_until"] is None or after_change["valid_until"] >= now()
new_quotas = set()
if will_be_valid and after_change["block_quota"] and after_change["max_usages"] > current["redeemed"]:
if after_change["quota"]:
@@ -428,7 +428,7 @@ class VoucherBulkEditForm(VoucherForm):
else:
new_quotas |= set(after_change["item"].quotas.filter(subevent=after_change["subevent"]))
new_amount = max(current["max_usages"] - current["redeemed"], 0)
new_amount = max(after_change["max_usages"] - after_change["redeemed"], 0) * current["c"]
if new_quotas != old_quotas or new_amount != old_amount:
for q in old_quotas:
quota_diff[q] -= old_amount
@@ -441,7 +441,8 @@ class VoucherBulkEditForm(VoucherForm):
qa.queue(*(q for q, v in quota_diff.items() if v > 0))
qa.compute()
if any(qa.results[q][0] != Quota.AVAILABILITY_OK or (qa.results[q][1] is not None and qa.results[q][1] < required) for q, required in quota_diff.items() if required > 0):
if any(qa.results[q][0] != Quota.AVAILABILITY_OK or (qa.results[q][1] is not None and qa.results[q][1] < required)
for q, required in quota_diff.items() if required > 0):
raise ValidationError(_(
'There is no sufficient quota available to perform this change.'
))
@@ -473,8 +474,12 @@ class VoucherBulkEditForm(VoucherForm):
valid_until__lt=now(),
)
if self.event.has_subevents:
conflicts = currently_not_blocked_seats.exclude(
seat_id__in=self.event.free_seats.values("pk")
subevents = self.event.subevents.filter(pk__in=currently_not_blocked_seats.values_list("subevent"))
for se in subevents:
conflicts = currently_not_blocked_seats.filter(
subevent=se
).exclude(
seat_id__in=se.free_seats().values("pk")
)
if conflicts:
raise ValidationError(_(
@@ -482,12 +487,8 @@ class VoucherBulkEditForm(VoucherForm):
'still available'
))
else:
subevents = self.event.subevents.filter(pk__in=currently_not_blocked_seats.values_list("subevent"))
for se in subevents:
conflicts = currently_not_blocked_seats.filter(
subevent=se
).exclude(
seat_id__in=se.free_seats.values("pk")
conflicts = currently_not_blocked_seats.exclude(
seat_id__in=self.event.free_seats().values("pk")
)
if conflicts:
raise ValidationError(_(

View File

@@ -42,7 +42,7 @@ from django.conf import settings
from django.contrib import messages
from django.core.exceptions import PermissionDenied, ValidationError
from django.db import connection, transaction
from django.db.models import Exists, OuterRef, Sum, Subquery, Count
from django.db.models import Count, Exists, OuterRef, Sum
from django.http import (
Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect,
JsonResponse,
@@ -55,7 +55,7 @@ from django.utils.safestring import mark_safe
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from django.views.generic import (
CreateView, ListView, TemplateView, UpdateView, View, FormView,
CreateView, FormView, ListView, TemplateView, UpdateView, View,
)
from django_scopes import scopes_disabled
@@ -70,7 +70,9 @@ from pretix.base.services.vouchers import vouchers_send
from pretix.base.templatetags.rich_text import markdown_compile_email
from pretix.base.views.tasks import AsyncFormView
from pretix.control.forms.filter import VoucherFilterForm, VoucherTagFilterForm
from pretix.control.forms.vouchers import VoucherBulkForm, VoucherForm, VoucherBulkEditForm
from pretix.control.forms.vouchers import (
VoucherBulkEditForm, VoucherBulkForm, VoucherForm,
)
from pretix.control.permissions import EventPermissionRequiredMixin
from pretix.control.signals import voucher_form_class
from pretix.control.views import PaginationMixin
@@ -671,7 +673,6 @@ class VoucherBulkAction(VoucherQueryMixin, EventPermissionRequiredMixin, View):
})
class VoucherBulkUpdateView(VoucherQueryMixin, EventPermissionRequiredMixin, FormView):
template_name = 'pretixcontrol/vouchers/bulk_edit.html'
permission = 'event.vouchers:write'
@@ -757,7 +758,7 @@ class VoucherBulkUpdateView(VoucherQueryMixin, EventPermissionRequiredMixin, For
data['_raw_bulk_data'] = self.request.POST.dict()
for obj in self.get_queryset():
log_entries.append(
obj.log_action('pretix.event.quota.changed', data=data, user=self.request.user, save=False)
obj.log_action('pretix.voucher.changed', data=data, user=self.request.user, save=False)
)
LogEntry.bulk_create_and_postprocess(log_entries)

View File

@@ -44,8 +44,8 @@ from django_scopes import scopes_disabled
from tests.base import SoupTestMixin, extract_form_fields
from pretix.base.models import (
Event, Item, ItemVariation, Order, OrderPosition, Organizer, Quota, Team,
User, Voucher,
Event, Item, ItemVariation, Order, OrderPosition, Organizer, Quota,
SeatingPlan, Team, User, Voucher,
)
@@ -135,49 +135,49 @@ class VoucherFormTest(SoupTestMixin, TransactionTestCase):
def test_filter_status_valid(self):
with scopes_disabled():
v = self.event.vouchers.create(item=self.ticket)
doc = self.client.get('/control/event/%s/%s/vouchers/?status=v' % (self.orga.slug, self.event.slug))
doc = self.client.get('/control/event/%s/%s/vouchers/?filter-status=v' % (self.orga.slug, self.event.slug))
assert v.code in doc.content.decode()
v.redeemed = 1
v.save()
doc = self.client.get('/control/event/%s/%s/vouchers/?status=v' % (self.orga.slug, self.event.slug))
doc = self.client.get('/control/event/%s/%s/vouchers/?filter-status=v' % (self.orga.slug, self.event.slug))
assert v.code not in doc.content.decode()
def test_filter_status_redeemed(self):
with scopes_disabled():
v = self.event.vouchers.create(item=self.ticket, redeemed=1)
doc = self.client.get('/control/event/%s/%s/vouchers/?status=r' % (self.orga.slug, self.event.slug))
doc = self.client.get('/control/event/%s/%s/vouchers/?filter-status=r' % (self.orga.slug, self.event.slug))
assert v.code in doc.content.decode()
v.redeemed = 0
v.save()
doc = self.client.get('/control/event/%s/%s/vouchers/?status=r' % (self.orga.slug, self.event.slug))
doc = self.client.get('/control/event/%s/%s/vouchers/?filter-status=r' % (self.orga.slug, self.event.slug))
assert v.code not in doc.content.decode()
def test_filter_status_expired(self):
with scopes_disabled():
v = self.event.vouchers.create(item=self.ticket, valid_until=now() + datetime.timedelta(days=1))
doc = self.client.get('/control/event/%s/%s/vouchers/?status=e' % (self.orga.slug, self.event.slug))
doc = self.client.get('/control/event/%s/%s/vouchers/?filter-status=e' % (self.orga.slug, self.event.slug))
assert v.code not in doc.content.decode()
v.valid_until = now() - datetime.timedelta(days=1)
v.save()
doc = self.client.get('/control/event/%s/%s/vouchers/?status=e' % (self.orga.slug, self.event.slug))
doc = self.client.get('/control/event/%s/%s/vouchers/?filter-status=e' % (self.orga.slug, self.event.slug))
assert v.code in doc.content.decode()
def test_filter_tag(self):
with scopes_disabled():
self.event.vouchers.create(item=self.ticket, code='ABCDEFG', comment='Foo', tag='bar')
doc = self.client.get('/control/event/%s/%s/vouchers/?tag=bar' % (self.orga.slug, self.event.slug))
doc = self.client.get('/control/event/%s/%s/vouchers/?filter-tag=bar' % (self.orga.slug, self.event.slug))
assert 'ABCDEFG' in doc.content.decode()
doc = self.client.get('/control/event/%s/%s/vouchers/?tag=baz' % (self.orga.slug, self.event.slug))
doc = self.client.get('/control/event/%s/%s/vouchers/?filter-tag=baz' % (self.orga.slug, self.event.slug))
assert 'ABCDEFG' not in doc.content.decode()
def test_search_code(self):
with scopes_disabled():
self.event.vouchers.create(item=self.ticket, code='ABCDEFG', comment='Foo')
doc = self.client.get('/control/event/%s/%s/vouchers/?search=ABCDEFG' % (self.orga.slug, self.event.slug))
doc = self.client.get('/control/event/%s/%s/vouchers/?filter-search=ABCDEFG' % (self.orga.slug, self.event.slug))
assert 'ABCDEFG' in doc.content.decode()
doc = self.client.get('/control/event/%s/%s/vouchers/?search=Foo' % (self.orga.slug, self.event.slug))
doc = self.client.get('/control/event/%s/%s/vouchers/?filter-search=Foo' % (self.orga.slug, self.event.slug))
assert 'ABCDEFG' in doc.content.decode()
doc = self.client.get('/control/event/%s/%s/vouchers/?search=12345' % (self.orga.slug, self.event.slug))
doc = self.client.get('/control/event/%s/%s/vouchers/?filter-search=12345' % (self.orga.slug, self.event.slug))
assert 'ABCDEFG' not in doc.content.decode()
def test_bulk_rng(self):
@@ -851,11 +851,12 @@ class VoucherBulkEditFormTest(SoupTestMixin, TransactionTestCase):
fields = extract_form_fields(doc)
fields.update(data)
doc = self.post_doc(self.url, fields, follow=True)
error_texts = [el.text for el in doc.select(".alert-danger, .has-error")]
if expect_error:
assert doc.select(".alert-danger")
assert any(expect_error in el.text for el in doc.select(".alert-danger"))
assert any(expect_error in t for t in error_texts), error_texts
else:
assert doc.select(".alert-success")
assert doc.select(".alert-success"), error_texts
def test_change_itemvar_to_product(self):
with scopes_disabled():
@@ -902,6 +903,21 @@ class VoucherBulkEditFormTest(SoupTestMixin, TransactionTestCase):
assert not v.variation
assert v.quota == self.quota_tickets
def test_change_itemvar_to_all(self):
with scopes_disabled():
self.event.vouchers.create(quota=self.quota_tickets)
self.event.vouchers.create(item=self.ticket)
self._update_all({
'_bulk': ['bulkedititemvar'],
'bulkedit-itemvar': '',
})
with scopes_disabled():
for v in self.event.vouchers.all():
assert not v.item
assert not v.variation
assert not v.quota
def test_change_max_usages(self):
with scopes_disabled():
self.event.vouchers.create(quota=self.quota_tickets, max_usages=15, redeemed=4)
@@ -919,10 +935,11 @@ class VoucherBulkEditFormTest(SoupTestMixin, TransactionTestCase):
for v in self.event.vouchers.all():
assert v.max_usages == 4
def _requires_one_more_quota(self, data: dict, expect_error: str=None):
def _requires_one_more_quota(self, data: dict, quota=None, expect_error: str=None):
self._update_all(data, expect_error="no sufficient quota")
self.quota_tickets.size += 1
self.quota_tickets.save()
quota = quota or self.quota_tickets
quota.size += 1
quota.save()
self._update_all(data)
def test_quota_check_change_item(self):
@@ -937,10 +954,36 @@ class VoucherBulkEditFormTest(SoupTestMixin, TransactionTestCase):
for v in self.event.vouchers.all():
assert v.item == self.ticket
def test_quota_check_change_variation(self):
with scopes_disabled():
self.event.vouchers.create(item=self.ticket, block_quota=True, max_usages=2, redeemed=1)
self.event.vouchers.create(item=self.ticket, block_quota=True, max_usages=3, redeemed=1)
self._requires_one_more_quota({
'_bulk': ['bulkedititemvar'],
'bulkedit-itemvar': f'{self.shirt.pk}-{self.shirt_red.pk}',
}, quota=self.quota_shirts)
with scopes_disabled():
for v in self.event.vouchers.all():
assert v.item == self.shirt
assert v.variation == self.shirt_red
def test_quota_check_change_item_with_variations(self):
with scopes_disabled():
self.event.vouchers.create(item=self.ticket, block_quota=True, max_usages=2, redeemed=1)
self.event.vouchers.create(item=self.ticket, block_quota=True, max_usages=3, redeemed=1)
self._requires_one_more_quota({
'_bulk': ['bulkedititemvar'],
'bulkedit-itemvar': f'{self.shirt.pk}',
}, quota=self.quota_shirts)
with scopes_disabled():
for v in self.event.vouchers.all():
assert v.item == self.shirt
assert not v.variation
def test_quota_check_change_expired_to_valid(self):
with scopes_disabled():
self.event.vouchers.create(item=self.shirt, block_quota=True, max_usages=2)
self.event.vouchers.create(item=self.shirt, block_quota=True, max_usages=1, valid_until=now() - datetime.timedelta(days=1))
self.event.vouchers.create(item=self.ticket, block_quota=True, max_usages=2)
self.event.vouchers.create(item=self.ticket, block_quota=True, max_usages=1, valid_until=now() - datetime.timedelta(days=1))
self._requires_one_more_quota({
'_bulk': ['bulkeditvalid_until'],
'bulkedit-valid_until_0': '',
@@ -952,20 +995,202 @@ class VoucherBulkEditFormTest(SoupTestMixin, TransactionTestCase):
def test_quota_check_change_max_usages(self):
with scopes_disabled():
self.event.vouchers.create(item=self.shirt, block_quota=True, max_usages=2)
self.event.vouchers.create(item=self.shirt, block_quota=True, max_usages=1, redeemed=1)
self.event.vouchers.create(item=self.ticket, block_quota=True, max_usages=2)
self.event.vouchers.create(item=self.ticket, block_quota=True, max_usages=1, redeemed=1)
self._requires_one_more_quota({
'_bulk': ['bulkeditmax_usages'],
'bulkedit-max_usages': '',
'bulkedit-max_usages': '2',
})
with scopes_disabled():
for v in self.event.vouchers.all():
assert v.max_usages == 2
# test quota use existing credit
# test quota changed subevent
# test quota changed subevent to mismatch quota
# test quota changed subevent to none
# test quota changed block quota, ignore
# test change seat properties
# test seats still available after validity change
def test_quota_check_no_change(self):
with scopes_disabled():
# Technically overbooked, but we don't have a diff in quota
self.event.vouchers.create(item=self.shirt, variation=self.shirt_red, block_quota=True)
self.event.vouchers.create(item=self.shirt, variation=self.shirt_red, block_quota=True)
self.event.vouchers.create(item=self.shirt, variation=self.shirt_red, block_quota=True)
self._update_all({
'_bulk': ['bulkedititemvar'],
'bulkedit-itemvar': f'{self.shirt.pk}-{self.shirt_blue.pk}',
})
with scopes_disabled():
for v in self.event.vouchers.all():
assert v.variation == self.shirt_blue
def test_quota_check_change_subevent(self):
with scopes_disabled():
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now())
se2 = self.event.subevents.create(name="Bar", date_from=now())
self.quota_tickets.subevent = se1
self.quota_tickets.save()
Quota.objects.create(event=self.event, subevent=se2, name='Tickets', size=3)
self.event.vouchers.create(item=self.ticket, block_quota=True, subevent=se2)
self.event.vouchers.create(item=self.ticket, block_quota=True, subevent=se2)
self.event.vouchers.create(item=self.ticket, block_quota=True, subevent=se2)
self._requires_one_more_quota({
'_bulk': ['bulkeditsubevent'],
'bulkedit-subevent': f'{se1.pk}',
})
with scopes_disabled():
for v in self.event.vouchers.all():
assert v.subevent == se1
def test_change_subevent_quota_invalid(self):
with scopes_disabled():
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now())
se2 = self.event.subevents.create(name="Bar", date_from=now())
self.quota_tickets.subevent = se1
self.quota_tickets.save()
v1 = self.event.vouchers.create(quota=self.quota_tickets, block_quota=True, subevent=se1)
self._update_all({
'_bulk': ['bulkeditsubevent'],
'bulkedit-subevent': f'{se2.pk}',
}, expect_error="selected quota does not match the selected subevent")
self._update_all({
'_bulk': ['bulkeditsubevent'],
'bulkedit-subevent': '',
}, expect_error="has no date selected")
v1.quota = None
v1.item = self.ticket
v1.save()
self._update_all({
'_bulk': ['bulkeditsubevent'],
'bulkedit-subevent': '',
}, expect_error="If you want this voucher to block quota, you need to select a specific date")
with scopes_disabled():
for v in self.event.vouchers.all():
assert v.subevent == se1
def test_change_missing_itemvar_with_block_quota(self):
with scopes_disabled():
self.event.vouchers.create(quota=self.quota_tickets, block_quota=True)
self.event.vouchers.create(quota=self.quota_tickets, block_quota=True)
self._update_all({
'_bulk': ['bulkedititemvar'],
'bulkedit-itemvar': '',
}, expect_error="You need to select a specific product or quota if this voucher should reserve")
self._update_all({
'_bulk': ['bulkedititemvar', 'bulkeditblock_quota'],
'bulkedit-itemvar': '',
'bulkedit-block_quota': '',
})
with scopes_disabled():
for v in self.event.vouchers.all():
assert not v.subevent
assert not v.block_quota
def test_change_subevent_and_quota(self):
with scopes_disabled():
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now())
se2 = self.event.subevents.create(name="Bar", date_from=now())
self.quota_tickets.subevent = se1
self.quota_tickets.save()
q2 = Quota.objects.create(event=self.event, subevent=se2, name='Tickets', size=3)
self.event.vouchers.create(quota=self.quota_tickets, block_quota=True, subevent=se1)
self._update_all({
'_bulk': ['bulkedititemvar', 'bulkeditsubevent'],
'bulkedit-subevent': f'{se2.pk}',
'bulkedit-itemvar': f'q-{q2.pk}',
})
with scopes_disabled():
for v in self.event.vouchers.all():
assert v.subevent == se2
assert v.quota == q2
def test_quota_check_change_block_quota(self):
with scopes_disabled():
self.event.vouchers.create(item=self.ticket, max_usages=3)
self._requires_one_more_quota({
'_bulk': ['bulkeditblock_quota'],
'bulkedit-block_quota': 'on',
})
with scopes_disabled():
for v in self.event.vouchers.all():
assert v.block_quota
def test_ignore_quota(self):
with scopes_disabled():
self.event.vouchers.create(item=self.ticket, max_usages=3)
self._update_all({
'_bulk': ['bulkeditblock_quota', 'bulkeditallow_ignore_quota'],
'bulkedit-block_quota': 'on',
'bulkedit-allow_ignore_quota': 'on',
})
with scopes_disabled():
for v in self.event.vouchers.all():
assert v.block_quota
assert v.allow_ignore_quota
@scopes_disabled()
def _create_seat(self, **kwargs):
plan = SeatingPlan.objects.create(
name="Plan", organizer=self.orga, layout="{}"
)
self.event.seating_plan = plan
self.event.save()
return self.event.seats.create(seat_number="A1", product=self.ticket, seat_guid="A1", **kwargs)
def test_seated_unsupported(self):
with scopes_disabled():
self.event.vouchers.create(item=self.ticket, max_usages=1, seat=self._create_seat())
self._update_all({
'_bulk': ['bulkeditmax_usages'],
'bulkedit-max_usages': '2',
}, expect_error="Changing the maximum number of usages in bulk is not supported")
self._update_all({
'_bulk': ['bulkeditsubevent'],
'bulkedit-subevent': '',
}, expect_error="Changing the date in bulk is not supported")
self._update_all({
'_bulk': ['bulkedititemvar'],
'bulkedit-itemvar': f'q-{self.quota_tickets.pk}',
}, expect_error="Changing the product to a quota is not supported")
def test_seat_changed_to_valid_needs_to_be_available(self):
with scopes_disabled():
seat = self._create_seat(blocked=True)
self.event.vouchers.create(item=self.ticket, max_usages=1, valid_until=now() - datetime.timedelta(days=1), seat=seat)
self._update_all({
'_bulk': ['bulkeditvalid_until'],
'bulkedit-valid_until_0': '',
'bulkedit-valid_until_1': '',
}, expect_error="not all assigned seats of the vouchers are still available")
seat.blocked = False
seat.save()
self._update_all({
'_bulk': ['bulkeditvalid_until'],
'bulkedit-valid_until_0': '',
'bulkedit-valid_until_1': '',
})
def test_seat_changed_to_valid_needs_to_be_available_subevents(self):
with scopes_disabled():
self.event.has_subevents = True
self.event.save()
se1 = self.event.subevents.create(name="Foo", date_from=now())
seat = self._create_seat(subevent=se1, blocked=True)
self.event.vouchers.create(item=self.ticket, max_usages=1, valid_until=now() - datetime.timedelta(days=1), seat=seat, subevent=se1)
self._update_all({
'_bulk': ['bulkeditvalid_until'],
'bulkedit-valid_until_0': '',
'bulkedit-valid_until_1': '',
}, expect_error="not all assigned seats of the vouchers are still available")
seat.blocked = False
seat.save()
self._update_all({
'_bulk': ['bulkeditvalid_until'],
'bulkedit-valid_until_0': '',
'bulkedit-valid_until_1': '',
})