mirror of
https://github.com/pretix/pretix.git
synced 2026-08-25 13:12:00 +00:00
Fix waiting list availability calculation if WL vouchers have seats (Z#23226856)
This commit is contained in:
@@ -181,10 +181,11 @@ class WaitingListEntry(LoggedModel):
|
|||||||
block_quota=True,
|
block_quota=True,
|
||||||
item_id=self.item_id,
|
item_id=self.item_id,
|
||||||
subevent_id=self.subevent_id,
|
subevent_id=self.subevent_id,
|
||||||
waitinglistentries__isnull=False
|
waitinglistentries__isnull=False,
|
||||||
|
seat__isnull=True
|
||||||
).aggregate(free=Sum(F('max_usages') - F('redeemed')))['free'] or 0
|
).aggregate(free=Sum(F('max_usages') - F('redeemed')))['free'] or 0
|
||||||
free_seats = num_free_seats_for_product - num_valid_vouchers_for_product
|
free_seats = num_free_seats_for_product - num_valid_vouchers_for_product
|
||||||
if not free_seats:
|
if free_seats < 1:
|
||||||
raise WaitingListException(_('No seat with this product is currently available.'))
|
raise WaitingListException(_('No seat with this product is currently available.'))
|
||||||
|
|
||||||
if '@' not in self.email:
|
if '@' not in self.email:
|
||||||
|
|||||||
@@ -280,11 +280,12 @@ class WaitingListView(EventPermissionRequiredMixin, WaitingListQuerySetMixin, Pa
|
|||||||
block_quota=True,
|
block_quota=True,
|
||||||
item_id=wle.item_id,
|
item_id=wle.item_id,
|
||||||
subevent=wle.subevent_id,
|
subevent=wle.subevent_id,
|
||||||
waitinglistentries__isnull=False
|
waitinglistentries__isnull=False,
|
||||||
|
seat__isnull=True
|
||||||
).aggregate(free=Sum(F('max_usages') - F('redeemed')))['free'] or 0
|
).aggregate(free=Sum(F('max_usages') - F('redeemed')))['free'] or 0
|
||||||
free_seats = num_free_seats_for_product - num_valid_vouchers_for_product
|
free_seats = num_free_seats_for_product - num_valid_vouchers_for_product
|
||||||
wle.availability = (
|
wle.availability = (
|
||||||
Quota.AVAILABILITY_GONE if free_seats == 0 else wle.availability[0],
|
Quota.AVAILABILITY_GONE if free_seats < 1 else wle.availability[0],
|
||||||
min(free_seats, wle.availability[1]) if wle.availability[1] is not None else free_seats,
|
min(free_seats, wle.availability[1]) if wle.availability[1] is not None else free_seats,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ from pretix.base.models import (
|
|||||||
Event, Item, ItemVariation, Organizer, Quota, Team, User, Voucher,
|
Event, Item, ItemVariation, Organizer, Quota, Team, User, Voucher,
|
||||||
WaitingListEntry,
|
WaitingListEntry,
|
||||||
)
|
)
|
||||||
|
from pretix.base.models.seating import Seat, SeatingPlan
|
||||||
|
from pretix.base.models.waitinglist import WaitingListException
|
||||||
from pretix.control.views.dashboards import waitinglist_widgets
|
from pretix.control.views.dashboards import waitinglist_widgets
|
||||||
|
|
||||||
|
|
||||||
@@ -55,11 +57,11 @@ def env():
|
|||||||
WaitingListEntry.objects.create(
|
WaitingListEntry.objects.create(
|
||||||
event=event, item=item1, email='success@example.org', voucher=v
|
event=event, item=item1, email='success@example.org', voucher=v
|
||||||
)
|
)
|
||||||
v = Voucher.objects.create(item=item1, event=event, block_quota=True, redeemed=0, valid_until=now() - timedelta(days=5))
|
v = Voucher.objects.create(item=item2, event=event, block_quota=True, redeemed=0, valid_until=now() - timedelta(days=5))
|
||||||
WaitingListEntry.objects.create(
|
WaitingListEntry.objects.create(
|
||||||
event=event, item=item2, email='expired@example.org', voucher=v
|
event=event, item=item2, email='expired@example.org', voucher=v
|
||||||
)
|
)
|
||||||
v = Voucher.objects.create(item=item1, event=event, block_quota=True, redeemed=0, valid_until=now() + timedelta(days=5))
|
v = Voucher.objects.create(item=item2, event=event, block_quota=True, redeemed=0, valid_until=now() + timedelta(days=5))
|
||||||
WaitingListEntry.objects.create(
|
WaitingListEntry.objects.create(
|
||||||
event=event, item=item2, email='valid@example.org', voucher=v
|
event=event, item=item2, email='valid@example.org', voucher=v
|
||||||
)
|
)
|
||||||
@@ -345,5 +347,75 @@ def test_dashboard(client, env):
|
|||||||
quota.items.add(env['item1'])
|
quota.items.add(env['item1'])
|
||||||
w = waitinglist_widgets(env['event'])
|
w = waitinglist_widgets(env['event'])
|
||||||
|
|
||||||
assert '1' in w[0]['content']
|
assert '2' in w[0]['content']
|
||||||
assert '5' in w[1]['content']
|
assert '5' in w[1]['content']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_waitinglist_seat_calc(client, env):
|
||||||
|
item = env['item1']
|
||||||
|
event = env['event']
|
||||||
|
wle = env['wle']
|
||||||
|
|
||||||
|
SeatingPlan.objects.create(
|
||||||
|
name="Plan", organizer=event.organizer, layout="{}"
|
||||||
|
)
|
||||||
|
event.seat_category_mappings.create(
|
||||||
|
layout_category='Stalls', product=item
|
||||||
|
)
|
||||||
|
for i in range(2):
|
||||||
|
event.seats.create(seat_number=f"A{i}", product=item, seat_guid=f"A{i}")
|
||||||
|
|
||||||
|
quota = Quota.objects.create(event=event, size=10)
|
||||||
|
quota.items.add(item)
|
||||||
|
|
||||||
|
client.login(email='dummy@dummy.dummy', password='dummy')
|
||||||
|
|
||||||
|
# Calculated availability should not be more than number of available seats
|
||||||
|
response = client.get('/control/event/dummy/dummy/waitinglist/')
|
||||||
|
assert len(response.context['entries']) == 5
|
||||||
|
for entry in response.context['entries']:
|
||||||
|
assert entry.availability == (Quota.AVAILABILITY_OK, 2)
|
||||||
|
|
||||||
|
# Sending out a voucher reduces availability by 1
|
||||||
|
with scopes_disabled():
|
||||||
|
wle.send_voucher()
|
||||||
|
|
||||||
|
voucher = wle.voucher
|
||||||
|
assert voucher
|
||||||
|
|
||||||
|
response = client.get('/control/event/dummy/dummy/waitinglist/')
|
||||||
|
assert len(response.context['entries']) == 4
|
||||||
|
for entry in response.context['entries']:
|
||||||
|
assert entry.availability == (Quota.AVAILABILITY_OK, 1)
|
||||||
|
|
||||||
|
# Assigning a seat to a voucher does not decrease availability further
|
||||||
|
with scopes_disabled():
|
||||||
|
voucher.seat = Seat.objects.get(seat_guid="A0")
|
||||||
|
voucher.save()
|
||||||
|
|
||||||
|
response = client.get('/control/event/dummy/dummy/waitinglist/')
|
||||||
|
assert len(response.context['entries']) == 4
|
||||||
|
for entry in response.context['entries']:
|
||||||
|
assert entry.availability == (Quota.AVAILABILITY_OK, 1)
|
||||||
|
|
||||||
|
with scopes_disabled():
|
||||||
|
wle2 = WaitingListEntry.objects.filter(item=item, voucher__isnull=True).first()
|
||||||
|
wle2.send_voucher()
|
||||||
|
|
||||||
|
# Overbooking is handled correctly
|
||||||
|
# Regression test for calculation that used `not free_seats` instead of `free_seats < 1`
|
||||||
|
with scopes_disabled():
|
||||||
|
# Block seat
|
||||||
|
seat = Seat.objects.get(seat_guid="A1")
|
||||||
|
seat.blocked = True
|
||||||
|
seat.save()
|
||||||
|
|
||||||
|
response = client.get('/control/event/dummy/dummy/waitinglist/')
|
||||||
|
assert len(response.context['entries']) == 3
|
||||||
|
for entry in response.context['entries']:
|
||||||
|
assert entry.availability == (Quota.AVAILABILITY_GONE, -1)
|
||||||
|
|
||||||
|
with scopes_disabled(), pytest.raises(WaitingListException):
|
||||||
|
wle3 = WaitingListEntry.objects.filter(item=item, voucher__isnull=True).first()
|
||||||
|
wle3.send_voucher()
|
||||||
|
|||||||
Reference in New Issue
Block a user