api: do not cancel out of delay bookings in events fillslots (#59821)

This commit is contained in:
Lauréline Guérin 2021-12-17 09:44:36 +01:00
parent 20f86b91f3
commit 5e4675f533
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
2 changed files with 133 additions and 2 deletions

View File

@ -1703,6 +1703,7 @@ class EventsFillslots(APIView):
raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
payload = serializer.validated_data
user_external_id = payload['user_external_id']
bypass_delays = payload.get('bypass_delays')
events = self.get_events(request, payload)
@ -1712,8 +1713,23 @@ class EventsFillslots(APIView):
already_booked_events = already_booked_events.filter(start_datetime__gte=start_datetime)
if end_datetime:
already_booked_events = already_booked_events.filter(start_datetime__lt=end_datetime)
agendas_by_ids = self.get_agendas_by_ids()
events_to_unbook_ids = []
events_in_request_ids = [e.pk for e in events]
for event in already_booked_events:
if event.pk in events_in_request_ids:
continue
agenda = agendas_by_ids[event.agenda_id]
if (
not bypass_delays
and agenda.min_booking_datetime
and event.start_datetime < agenda.min_booking_datetime
):
continue
if agenda.max_booking_datetime and event.start_datetime > agenda.max_booking_datetime:
continue
events_to_unbook_ids.append(event.pk)
events_to_unbook = list(already_booked_events.exclude(pk__in=events).values_list('pk', flat=True))
events = events.exclude(booking__user_external_id=user_external_id)
full_events = [str(event) for event in events.filter(full=True)]
@ -1735,7 +1751,7 @@ class EventsFillslots(APIView):
with transaction.atomic():
deleted_count = Booking.objects.filter(
user_external_id=user_external_id, event__in=events_to_unbook
user_external_id=user_external_id, event__in=events_to_unbook_ids
).delete()[0]
Booking.objects.bulk_create(bookings)
@ -1753,6 +1769,9 @@ class EventsFillslots(APIView):
def get_already_booked_events(self, user_external_id):
return self.agenda.event_set.filter(booking__user_external_id=user_external_id)
def get_agendas_by_ids(self):
return {self.agenda.pk: self.agenda}
events_fillslots = EventsFillslots.as_view()
@ -1806,6 +1825,9 @@ class MultipleAgendasEventsFillslots(EventsFillslots):
def get_already_booked_events(self, user_external_id):
return Event.objects.filter(agenda__in=self.agendas, booking__user_external_id=user_external_id)
def get_agendas_by_ids(self):
return {a.pk: a for a in self.agendas}
@property
def serializer_extra_context(self):
return {'allowed_agenda_slugs': self.agenda_slugs}

View File

@ -2773,6 +2773,115 @@ def test_api_events_fillslots_preserve_past_bookings(app, user, freezer):
assert second_event.booking_set.count() == 0
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_api_events_fillslots_preserve_out_of_delays_bookings(app, user, freezer):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=2, maximal_booking_delay=10
)
event = Event.objects.create(
label='Event', start_datetime=now() + datetime.timedelta(days=5), places=2, agenda=agenda
)
second_event = Event.objects.create(
label='Event 2', start_datetime=now() + datetime.timedelta(days=9), places=2, agenda=agenda
)
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agenda/%s/events/fillslots/' % agenda.slug
params = {'user_external_id': 'user_id', 'slots': 'event,event-2'}
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 2
assert resp.json['cancelled_booking_count'] == 0
# book only second event while first event is out of delay
freezer.move_to('2021-09-10')
params = {'user_external_id': 'user_id', 'slots': 'event-2'}
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 0
assert event.booking_set.count() == 1
assert second_event.booking_set.count() == 1
booking = event.booking_set.get()
# except if we want to bypass delays
params = {'user_external_id': 'user_id', 'slots': 'event-2', 'bypass_delays': True}
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 1
assert event.booking_set.count() == 0
assert second_event.booking_set.count() == 1
booking.save() # reset
# cancel all bookings in delays
params = {'user_external_id': 'user_id', 'slots': ''}
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 1
assert event.booking_set.count() == 1
assert second_event.booking_set.count() == 0
# bypass delays
params = {'user_external_id': 'user_id', 'slots': '', 'bypass_delays': True}
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 1
assert event.booking_set.count() == 0
assert second_event.booking_set.count() == 0
booking.save() # reset
# book only second event while first event is out of delay with multiple agendas API
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event-2'}
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
assert resp.json['booking_count'] == 1
assert resp.json['cancelled_booking_count'] == 0
assert event.booking_set.count() == 1
assert second_event.booking_set.count() == 1
# except if we want to bypass delays
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event-2', 'bypass_delays': True}
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 1
assert event.booking_set.count() == 0
assert second_event.booking_set.count() == 1
booking.save() # reset
# book only first event while second event is out of delay
freezer.move_to('2021-09-04')
params = {'user_external_id': 'user_id', 'slots': 'event'}
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 0
assert event.booking_set.count() == 1
assert second_event.booking_set.count() == 1
# bypass_delays has no effect on maximal_booking_delay
params = {'user_external_id': 'user_id', 'slots': 'event', 'bypass_delays': True}
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 0
assert event.booking_set.count() == 1
assert second_event.booking_set.count() == 1
# cancel all bookings in delays
params = {'user_external_id': 'user_id', 'slots': ''}
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 1
assert event.booking_set.count() == 0
assert second_event.booking_set.count() == 1
# book only first event while second event is out of delay with multiple agendas API
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event'}
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
assert resp.json['booking_count'] == 1
assert resp.json['cancelled_booking_count'] == 0
assert event.booking_set.count() == 1
assert second_event.booking_set.count() == 1
# bypass_delays has no effect on maximal_booking_delay
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event', 'bypass_delays': True}
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 0
assert event.booking_set.count() == 1
assert second_event.booking_set.count() == 1
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_api_events_fillslots_multiple_agendas(app, user):
first_agenda = Agenda.objects.create(label='First agenda', kind='events')