general: add possibility to book multiple places at once (#15218)
This commit is contained in:
parent
2907e4bb90
commit
ffcda3904a
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agendas', '0013_auto_20161028_1603'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='booking',
|
||||
name='primary_booking',
|
||||
field=models.ForeignKey(related_name='secondary_booking_set', to='agendas.Booking', null=True),
|
||||
),
|
||||
]
|
|
@ -310,6 +310,9 @@ class Booking(models.Model):
|
|||
cancellation_datetime = models.DateTimeField(null=True)
|
||||
in_waiting_list = models.BooleanField(default=False)
|
||||
creation_datetime = models.DateTimeField(auto_now_add=True)
|
||||
# primary booking is used to group multiple bookings together
|
||||
primary_booking = models.ForeignKey('self', null=True,
|
||||
on_delete=models.CASCADE, related_name='secondary_booking_set')
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
with transaction.atomic():
|
||||
|
@ -320,9 +323,14 @@ class Booking(models.Model):
|
|||
self.event.save()
|
||||
|
||||
def cancel(self):
|
||||
self.cancellation_datetime = now()
|
||||
self.save()
|
||||
timestamp = now()
|
||||
with transaction.atomic():
|
||||
self.secondary_booking_set.update(cancellation_datetime=timestamp)
|
||||
self.cancellation_datetime = timestamp
|
||||
self.save()
|
||||
|
||||
def accept(self):
|
||||
self.in_waiting_list = False
|
||||
self.save()
|
||||
with transaction.atomic():
|
||||
self.secondary_booking_set.update(in_waiting_list=False)
|
||||
self.save()
|
||||
|
|
|
@ -214,6 +214,11 @@ class Fillslot(GenericAPIView):
|
|||
except (ValueError, Agenda.DoesNotExist):
|
||||
raise Http404()
|
||||
|
||||
if 'count' in request.GET:
|
||||
places_count = int(request.GET['count'])
|
||||
else:
|
||||
places_count = 1
|
||||
|
||||
if agenda.kind == 'meetings':
|
||||
# event is actually a timeslot, convert to a real event object
|
||||
meeting_type_id, start_datetime_str = event_pk.split(':')
|
||||
|
@ -231,18 +236,24 @@ class Fillslot(GenericAPIView):
|
|||
new_booking = Booking(event_id=event_pk, extra_data=request.data)
|
||||
|
||||
if event.waiting_list_places:
|
||||
if event.waiting_list >= event.waiting_list_places:
|
||||
if (event.waiting_list + places_count) > event.waiting_list_places:
|
||||
return Response({'err': 1, 'reason': 'sold out'})
|
||||
|
||||
if event.booked_places >= event.places or event.waiting_list:
|
||||
if (event.booked_places + places_count) > event.places or event.waiting_list:
|
||||
# if this is full or there are people waiting, put new bookings
|
||||
# in the waiting list.
|
||||
new_booking.in_waiting_list = True
|
||||
else:
|
||||
if event.booked_places >= event.places:
|
||||
if (event.booked_places + places_count) > event.places:
|
||||
return Response({'err': 1, 'reason': 'sold out'})
|
||||
|
||||
new_booking.save()
|
||||
for i in range(places_count-1):
|
||||
additional_booking = Booking(event_id=event_pk, extra_data=request.data)
|
||||
additional_booking.in_waiting_list = new_booking.in_waiting_list
|
||||
additional_booking.primary_booking = new_booking
|
||||
additional_booking.save()
|
||||
|
||||
response = {
|
||||
'err': 0,
|
||||
'in_waiting_list': new_booking.in_waiting_list,
|
||||
|
|
|
@ -546,3 +546,45 @@ def test_accept_booking(app, some_data, user):
|
|||
assert resp.json['err'] == 1
|
||||
assert Booking.objects.filter(in_waiting_list=True).count() == 1
|
||||
assert Booking.objects.filter(in_waiting_list=False).count() == 0
|
||||
|
||||
def test_multiple_booking_api(app, some_data, user):
|
||||
agenda = Agenda.objects.filter(label=u'Foo bar')[0]
|
||||
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
|
||||
|
||||
resp_datetimes = app.get('/api/agenda/%s/datetimes/' % agenda.id)
|
||||
event_fillslot_url = [x for x in resp_datetimes.json['data'] if x['id'] == event.id][0]['api']['fillslot_url']
|
||||
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
resp = app.post('/api/agenda/%s/fillslot/%s/?count=3' % (agenda.slug, event.id))
|
||||
Booking.objects.get(id=resp.json['booking_id'])
|
||||
assert resp.json['datetime'] == localtime(event.start_datetime).isoformat()
|
||||
assert 'accept_url' not in resp.json['api']
|
||||
assert 'cancel_url' in resp.json['api']
|
||||
assert event.booked_places == 3
|
||||
|
||||
resp2 = app.post('/api/agenda/%s/fillslot/%s/?count=2' % (agenda.slug, event.id))
|
||||
assert event.booked_places == 5
|
||||
|
||||
resp = app.post(resp.json['api']['cancel_url'])
|
||||
assert event.booked_places == 2
|
||||
|
||||
# check available places overflow
|
||||
event.places = 3
|
||||
event.waiting_list_places = 8
|
||||
event.save()
|
||||
|
||||
resp3 = app.post('/api/agenda/%s/fillslot/%s/?count=5' % (agenda.slug, event.id))
|
||||
assert event.booked_places == 2
|
||||
assert event.waiting_list == 5
|
||||
|
||||
# check waiting list overflow
|
||||
resp = app.post('/api/agenda/%s/fillslot/%s/?count=5' % (agenda.slug, event.id))
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['reason'] == 'sold out'
|
||||
assert event.booked_places == 2
|
||||
assert event.waiting_list == 5
|
||||
|
||||
# accept the waiting list
|
||||
resp = app.post(resp3.json['api']['accept_url'])
|
||||
assert event.booked_places == 7
|
||||
assert event.waiting_list == 0
|
||||
|
|
Loading…
Reference in New Issue