api: allow changing recurrence bookings (#54746)

This commit is contained in:
Valentin Deniaud 2021-07-05 17:29:16 +02:00
parent 92d209ed11
commit 96b10d052b
2 changed files with 99 additions and 13 deletions

View File

@ -1571,6 +1571,7 @@ class RecurringFillslots(APIView):
http_status=status.HTTP_400_BAD_REQUEST,
)
payload = serializer.validated_data
user_external_id = payload['user_external_id']
open_event_slugs = set(agenda.get_open_recurring_events().values_list('slug', flat=True))
slots = collections.defaultdict(list)
@ -1600,15 +1601,18 @@ class RecurringFillslots(APIView):
events_to_book = Event.objects.filter(event_filter)
events_to_book = events_to_book.filter(start_datetime__gte=start_datetime, cancelled=False)
if end_datetime:
events_to_book = events_to_book.filter(start_datetime__lte=end_datetime)
events_to_unbook = list(
agenda.event_set.filter(booking__user_external_id=user_external_id, primary_event__isnull=False)
.exclude(pk__in=events_to_book)
.values_list('pk', flat=True)
)
events_to_book = events_to_book.exclude(booking__user_external_id=user_external_id)
full_events = list(events_to_book.filter(full=True))
events_to_book = events_to_book.filter(full=False)
if end_datetime:
events_to_book = events_to_book.filter(start_datetime__lte=end_datetime)
if not events_to_book.exists():
if full_events:
raise APIError(_('all events are all full'), err_class='all events are all full')
raise APIError(_('no event recurrences to book'), err_class='no event recurrences to book')
events_to_book = Event.annotate_queryset(events_to_book)
events_to_book = events_to_book.annotate(
@ -1621,9 +1625,15 @@ class RecurringFillslots(APIView):
extra_data = {k: v for k, v in request.data.items() if k not in payload}
bookings = [make_booking(event, payload, extra_data) for event in events_to_book]
events_to_update = Event.annotate_queryset(
agenda.event_set.filter(pk__in=events_to_unbook + [event.pk for event in events_to_book])
)
with transaction.atomic():
deleted_count = Booking.objects.filter(
user_external_id=user_external_id, event__in=events_to_unbook
).delete()[0]
Booking.objects.bulk_create(bookings)
events_to_book.update(
events_to_update.update(
full=Q(booked_places_count__gte=F('places'), waiting_list_places=0)
| Q(waiting_list_places__gt=0, waiting_list_count__gte=F('waiting_list_places')),
almost_full=Q(booked_places_count__gte=0.9 * F('places')),
@ -1632,6 +1642,7 @@ class RecurringFillslots(APIView):
response = {
'err': 0,
'booking_count': len(bookings),
'cancelled_booking_count': deleted_count,
'full_events': [get_event_detail(request, x, agenda=agenda) for x in full_events],
}
return Response(response)

View File

@ -2168,21 +2168,22 @@ def test_recurring_events_api_fillslots(app, user, freezer):
assert resp.json['booking_count'] == 155
assert len(resp.json['full_events']) == 1
assert resp.json['full_events'][0]['slug'] == event.slug
resp = app.post_json(fillslots_url, params=params)
# events are full
params['user_external_id'] = 'user_id_4'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'all events are all full'
assert resp.json['booking_count'] == 0
# no event in range
resp = app.post_json(fillslots_url + '?date_start=2020-10-06&date_end=2020-11-06', params=params)
assert resp.json['booking_count'] == 0
params['slots'] = 'event:1'
resp = app.post_json(fillslots_url + '?date_start=2021-10-06&date_end=2021-11-06', params=params)
assert resp.json['booking_count'] == 4
assert Booking.objects.filter(user_external_id='user_id_4').count() == 4
resp = app.post_json(fillslots_url + '?date_start=2020-10-06&date_end=2020-11-06', params=params)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'no event recurrences to book'
del params['user_external_id']
resp = app.post_json(fillslots_url, params=params, status=400)
assert resp.json['err'] == 1
@ -2226,6 +2227,80 @@ def test_recurring_events_api_fillslots_waiting_list(app, user, freezer):
assert events.filter(waiting_list_count=2).count() == 5
def test_recurring_events_api_fillslots_change_bookings(app, user, freezer):
freezer.move_to('2021-09-06 12:00')
agenda = Agenda.objects.create(label='Foo bar', kind='events')
event = Event.objects.create(
label='Event',
start_datetime=now(),
recurrence_days=[0, 1, 3, 4], # Monday, Tuesday, Thursday, Friday
places=1,
waiting_list_places=1,
agenda=agenda,
recurrence_end_date=now() + datetime.timedelta(days=364),
)
event.create_all_recurrences()
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agenda/%s/recurring-events/fillslots/' % agenda.slug
params = {'user_external_id': 'user_id'}
# Book Monday and Thursday
params['slots'] = 'event:0,event:3'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 104
assert resp.json['cancelled_booking_count'] == 0
assert Booking.objects.count() == 104
assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 52
assert Booking.objects.filter(event__start_datetime__week_day=5).count() == 52
# Change booking to Monday and Tuesday
params['slots'] = 'event:0,event:1'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 52
assert resp.json['cancelled_booking_count'] == 52
assert Booking.objects.count() == 104
assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 52
assert Booking.objects.filter(event__start_datetime__week_day=3).count() == 52
# Booking again does nothing
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 0
assert Booking.objects.count() == 104
params = {'user_external_id': 'user_id_2'}
params['slots'] = 'event:0,event:3'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 104
assert resp.json['cancelled_booking_count'] == 0
assert Booking.objects.count() == 208
assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 104
assert Booking.objects.filter(event__start_datetime__week_day=5).count() == 52
events = Event.annotate_queryset(Event.objects.filter(primary_event__isnull=False))
assert events.filter(booked_places_count=1).count() == 156
assert events.filter(waiting_list_count=1).count() == 52
params['slots'] = 'event:1,event:4'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 104
assert resp.json['cancelled_booking_count'] == 104
assert Booking.objects.count() == 208
assert Booking.objects.filter(event__start_datetime__week_day=3).count() == 104
assert Booking.objects.filter(event__start_datetime__week_day=6).count() == 52
events = Event.annotate_queryset(Event.objects.filter(primary_event__isnull=False))
assert events.filter(booked_places_count=1).count() == 156
assert events.filter(waiting_list_count=1).count() == 52
# only recurring events are impacted
normal_event = Event.objects.create(
start_datetime=now() + datetime.timedelta(days=1), places=2, agenda=agenda
)
Booking.objects.create(event=normal_event, user_external_id='user_id')
resp = app.post_json(fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event:0'})
assert resp.json['cancelled_booking_count'] == 52
assert Booking.objects.filter(user_external_id='user_id', event=normal_event).count() == 1
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_api_events_fillslots(app, user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')