api: cancel booking in events fillslots - instead of delete (#61066)

This commit is contained in:
Lauréline Guérin 2022-02-07 09:59:20 +01:00
parent f65717725b
commit b61a867667
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
2 changed files with 104 additions and 11 deletions

View File

@ -1788,7 +1788,7 @@ class EventsFillslots(APIView):
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_to_unbook = []
events_in_request_ids = [e.pk for e in events]
for event in already_booked_events:
if event.pk in events_in_request_ids:
@ -1802,9 +1802,19 @@ class EventsFillslots(APIView):
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.append(event)
events = events.exclude(booking__user_external_id=user_external_id)
# outdated bookings to remove (cancelled bookings to replace by an active booking)
events_cancelled_to_delete = events.filter(
booking__user_external_id=user_external_id,
booking__cancellation_datetime__isnull=False,
)
# book only events without active booking for the user
events = events.exclude(
pk__in=Booking.objects.filter(
event__in=events, user_external_id=user_external_id, cancellation_datetime__isnull=True
).values('event')
)
full_events = [str(event) for event in events.filter(full=True)]
if full_events:
@ -1823,10 +1833,22 @@ class EventsFillslots(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]
bookings_to_cancel = Booking.objects.filter(
user_external_id=user_external_id, event__in=events_to_unbook, cancellation_datetime__isnull=True
)
with transaction.atomic():
deleted_count = Booking.objects.filter(
user_external_id=user_external_id, event__in=events_to_unbook_ids
).delete()[0]
# cancel existing bookings
cancellation_datetime = now()
Booking.objects.filter(primary_booking__in=bookings_to_cancel).update(
cancellation_datetime=cancellation_datetime
)
cancelled_count = bookings_to_cancel.update(cancellation_datetime=cancellation_datetime)
# and delete outdated cancelled bookings
Booking.objects.filter(
user_external_id=user_external_id, event__in=events_cancelled_to_delete
).delete()
# create missing bookings
Booking.objects.bulk_create(bookings)
response = {
@ -1839,7 +1861,7 @@ class EventsFillslots(APIView):
get_event_detail(request, x, multiple_agendas=self.multiple_agendas)
for x in waiting_list_events
],
'cancelled_booking_count': deleted_count,
'cancelled_booking_count': cancelled_count,
}
return Response(response)
@ -1847,7 +1869,9 @@ class EventsFillslots(APIView):
return get_events_from_slots(payload['slots'], request, self.agenda, payload)
def get_already_booked_events(self, user_external_id):
return self.agenda.event_set.filter(booking__user_external_id=user_external_id)
return self.agenda.event_set.filter(
booking__user_external_id=user_external_id, booking__cancellation_datetime__isnull=True
)
def get_agendas_by_ids(self):
return {self.agenda.pk: self.agenda}

View File

@ -3460,9 +3460,9 @@ def test_api_events_fillslots(app, user):
assert resp.json['booking_count'] == 1
assert resp.json['cancelled_booking_count'] == 1
user_bookings = Booking.objects.filter(user_external_id='user_id')
user_bookings = Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True)
assert {b.event.slug for b in user_bookings} == {'event-2', 'event-3'}
assert event.booking_set.count() == 2
assert event.booking_set.filter(cancellation_datetime__isnull=True).count() == 2
assert second_event.booking_set.count() == 2
assert third_event.booking_set.count() == 1
@ -3489,12 +3489,81 @@ def test_api_events_fillslots(app, user):
params['slots'] = ''
resp = app.post_json(fillslots_url, params=params)
assert resp.json['cancelled_booking_count'] == 3
assert Booking.objects.filter(user_external_id='user_id').count() == 0
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 0
resp = app.post('/api/agenda/foobar/events/fillslots/', status=404)
resp = app.post('/api/agenda/0/events/fillslots/', status=404)
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_api_events_fillslots_with_cancelled(app, user):
agenda = Agenda.objects.create(label='Foo bar', kind='events')
event_1 = Event.objects.create(
label='Event 1',
start_datetime=now() + datetime.timedelta(days=1),
places=2,
agenda=agenda,
)
event_2 = Event.objects.create(
label='Event 2',
start_datetime=now() + datetime.timedelta(days=2),
places=2,
agenda=agenda,
)
Event.objects.create(
label='Event 3',
start_datetime=now() + datetime.timedelta(days=3),
places=2,
agenda=agenda,
)
# create cancelled booking for the user
booking_1 = Booking.objects.create(event=event_1, user_external_id='user_id')
booking_1.cancel()
assert booking_1.cancellation_datetime is not None
# and non cancelled booking for the user
booking_2 = Booking.objects.create(event=event_2, user_external_id='user_id')
assert booking_2.cancellation_datetime is None
# secondary booking for this one
booking_2_secondary = Booking.objects.create(event=event_2, primary_booking=booking_2)
# and bookings for another user
Booking.objects.create(event=event_1, user_external_id='user_id_foobar')
other_booking = Booking.objects.create(event=event_2, user_external_id='user_id_foobar')
other_booking.cancel()
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agenda/%s/events/fillslots/' % agenda.slug
params = {'user_external_id': 'user_id', 'slots': 'event-1,event-2,event-3'}
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 2
assert len(resp.json['booked_events']) == 2
assert resp.json['cancelled_booking_count'] == 0
assert resp.json['booked_events'][0]['id'] == 'event-1'
assert resp.json['booked_events'][1]['id'] == 'event-3'
assert Booking.objects.filter(user_external_id='user_id').count() == 3
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 3
assert Booking.objects.filter(pk=booking_1.pk).exists() is False # cancelled booking deleted
booking_2.refresh_from_db()
booking_2_secondary.refresh_from_db()
assert booking_2.cancellation_datetime is None
assert booking_2_secondary.cancellation_datetime is None
params = {'user_external_id': 'user_id', 'slots': 'event-3'}
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 2
assert Booking.objects.filter(user_external_id='user_id').count() == 3
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 1
assert Booking.objects.filter(pk=booking_1.pk).exists() is False # cancelled booking deleted
booking_2.refresh_from_db()
booking_2_secondary.refresh_from_db()
assert booking_2.cancellation_datetime is not None
assert booking_2_secondary.cancellation_datetime is not None
def test_api_events_fillslots_check_delays(app, user):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7