api: mark bookings and cancellations as out of min delay (#63809)

This commit is contained in:
Lauréline Guérin 2022-04-12 11:44:29 +02:00
parent 46e07301b6
commit d728a26447
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
6 changed files with 93 additions and 13 deletions

View File

@ -0,0 +1,16 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('agendas', '0117_events_type'),
]
operations = [
migrations.AddField(
model_name='booking',
name='out_of_min_delay',
field=models.BooleanField(default=False),
),
]

View File

@ -1932,6 +1932,7 @@ class Booking(models.Model):
user_phone_number = models.CharField(max_length=16, blank=True)
user_was_present = models.NullBooleanField()
user_absence_reason = models.CharField(max_length=250, blank=True)
out_of_min_delay = models.BooleanField(default=False)
extra_emails = ArrayField(models.EmailField(), default=list)
extra_phone_numbers = ArrayField(models.CharField(max_length=16), default=list)

View File

@ -697,6 +697,9 @@ def get_start_and_end_datetime_from_request(request):
def make_booking(event, payload, extra_data, primary_booking=None, in_waiting_list=False, color=None):
out_of_min_delay = False
if event.agenda.min_booking_datetime and event.start_datetime < event.agenda.min_booking_datetime:
out_of_min_delay = True
return Booking(
event_id=event.pk,
in_waiting_list=getattr(event, 'in_waiting_list', in_waiting_list),
@ -707,6 +710,7 @@ def make_booking(event, payload, extra_data, primary_booking=None, in_waiting_li
user_last_name=payload.get('user_last_name') or payload.get('user_name') or '',
user_email=payload.get('user_email', ''),
user_phone_number=payload.get('user_phone_number', ''),
out_of_min_delay=out_of_min_delay,
form_url=translate_to_publik_url(payload.get('form_url', '')),
backoffice_url=translate_to_publik_url(payload.get('backoffice_url', '')),
cancel_callback_url=translate_to_publik_url(payload.get('cancel_callback_url', '')),
@ -1719,6 +1723,9 @@ class RecurringFillslots(APIView):
)
extra_data = {k: v for k, v in request.data.items() if k not in payload}
# don't reload agendas and events types
for event in events_to_book:
event.agenda = agendas_by_id[event.agenda_id]
bookings = [make_booking(event, payload, extra_data) for event in events_to_book]
bookings_to_cancel = Booking.objects.filter(
@ -1746,9 +1753,6 @@ class RecurringFillslots(APIView):
'full_events': [get_event_detail(request, x, multiple_agendas=True) for x in full_events],
}
if payload.get('include_booked_events_detail'):
# don't reload agendas and events types
for event in events_to_book:
event.agenda = agendas_by_id[event.agenda_id]
events_to_book_by_id = {x.id: x for x in events_to_book}
response['booked_events'] = [
get_event_detail(request, events_to_book_by_id[x.event_id], booking=x, multiple_agendas=True)
@ -1844,20 +1848,23 @@ class EventsFillslots(APIView):
already_booked_events = already_booked_events.filter(start_datetime__lt=end_datetime)
agendas_by_ids = self.get_agendas_by_ids()
events_to_unbook = []
events_to_unbook_out_of_min_delay = []
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
out_of_min_delay = False
if agenda.min_booking_datetime and event.start_datetime < agenda.min_booking_datetime:
if not bypass_delays:
continue
out_of_min_delay = True
if agenda.max_booking_datetime and event.start_datetime > agenda.max_booking_datetime:
continue
events_to_unbook.append(event)
if out_of_min_delay:
events_to_unbook_out_of_min_delay.append(event)
else:
events_to_unbook.append(event)
# outdated bookings to remove (cancelled bookings to replace by an active booking)
events_cancelled_to_delete = events.filter(
@ -1888,6 +1895,11 @@ 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_out_of_min_delay = Booking.objects.filter(
user_external_id=user_external_id,
event__in=events_to_unbook_out_of_min_delay,
cancellation_datetime__isnull=True,
)
bookings_to_cancel = Booking.objects.filter(
user_external_id=user_external_id, event__in=events_to_unbook, cancellation_datetime__isnull=True
)
@ -1895,10 +1907,19 @@ class EventsFillslots(APIView):
with transaction.atomic():
# cancel existing bookings
cancellation_datetime = now()
Booking.objects.filter(primary_booking__in=bookings_to_cancel).update(
cancellation_datetime=cancellation_datetime
Booking.objects.filter(primary_booking__in=bookings_to_cancel_out_of_min_delay).update(
cancellation_datetime=cancellation_datetime,
out_of_min_delay=True,
)
cancelled_count = bookings_to_cancel_out_of_min_delay.update(
cancellation_datetime=cancellation_datetime, out_of_min_delay=True
)
Booking.objects.filter(primary_booking__in=bookings_to_cancel).update(
cancellation_datetime=cancellation_datetime, out_of_min_delay=False
)
cancelled_count += bookings_to_cancel.update(
cancellation_datetime=cancellation_datetime, out_of_min_delay=False
)
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

View File

@ -257,6 +257,8 @@ def test_booking_api_check_delays(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 0
booking = Booking.objects.latest('pk')
assert booking.out_of_min_delay is False
# test minimal_booking_delay
agenda.minimal_booking_delay = 6
@ -267,6 +269,8 @@ def test_booking_api_check_delays(app, user):
agenda.save()
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'bypass_delays': True})
assert resp.json['err'] == 0
booking = Booking.objects.latest('pk')
assert booking.out_of_min_delay is True
# test maximal_booking_delay
agenda.minimal_booking_delay = 0
@ -278,6 +282,8 @@ def test_booking_api_check_delays(app, user):
agenda.save()
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'bypass_delays': True})
assert resp.json['err'] == 0
booking = Booking.objects.latest('pk')
assert booking.out_of_min_delay is False
@pytest.mark.freeze_time('2021-02-23')

View File

@ -215,6 +215,9 @@ def test_api_events_fillslots_check_delays(app, user):
fillslots_url = '/api/agenda/%s/events/fillslots/' % agenda.slug
resp = app.post(fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event-slug'})
assert resp.json['err'] == 0
booking = Booking.objects.latest('pk')
assert booking.out_of_min_delay is False
booking.delete()
# test minimal_booking_delay
agenda.minimal_booking_delay = 6
@ -227,6 +230,9 @@ def test_api_events_fillslots_check_delays(app, user):
fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event-slug', 'bypass_delays': True}
)
assert resp.json['err'] == 0
booking = Booking.objects.latest('pk')
assert booking.out_of_min_delay is True
booking.delete()
# test maximal_booking_delay
agenda.minimal_booking_delay = 0
@ -240,6 +246,8 @@ def test_api_events_fillslots_check_delays(app, user):
fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event-slug', 'bypass_delays': True}
)
assert resp.json['err'] == 0
booking = Booking.objects.latest('pk')
assert booking.out_of_min_delay is False
@pytest.mark.freeze_time('2021-09-06 12:00')
@ -334,6 +342,8 @@ def test_api_events_fillslots_preserve_out_of_delays_bookings(app, user, freezer
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 2
assert resp.json['cancelled_booking_count'] == 0
assert event.booking_set.get().out_of_min_delay is False
assert second_event.booking_set.get().out_of_min_delay is False
# book only second event while first event is out of delay
freezer.move_to('2021-09-10')
@ -342,7 +352,9 @@ def test_api_events_fillslots_preserve_out_of_delays_bookings(app, user, freezer
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 0
assert event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
assert event.booking_set.get().out_of_min_delay is False
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
assert second_event.booking_set.get().out_of_min_delay is False
booking = event.booking_set.get()
# except if we want to bypass delays
params = {'user_external_id': 'user_id', 'slots': 'event-2', 'bypass_delays': True}
@ -350,7 +362,9 @@ def test_api_events_fillslots_preserve_out_of_delays_bookings(app, user, freezer
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 1
assert event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
assert event.booking_set.get().out_of_min_delay is True
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
assert second_event.booking_set.get().out_of_min_delay is False
booking.save() # reset
# cancel all bookings in delays
@ -359,14 +373,18 @@ def test_api_events_fillslots_preserve_out_of_delays_bookings(app, user, freezer
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 1
assert event.booking_set.count() == 1
assert event.booking_set.get().out_of_min_delay is False
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
assert second_event.booking_set.get().out_of_min_delay is False
# 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.filter(cancellation_datetime__isnull=True).count() == 0
assert event.booking_set.get().out_of_min_delay is True
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
assert second_event.booking_set.get().out_of_min_delay is False
booking.save() # reset
# book only first event while second event is out of delay

View File

@ -72,6 +72,8 @@ def test_api_events_fillslots_preserve_out_of_delays_bookings_multiple_agendas(a
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
assert resp.json['booking_count'] == 2
assert resp.json['cancelled_booking_count'] == 0
assert event.booking_set.get().out_of_min_delay is False
assert second_event.booking_set.get().out_of_min_delay is False
# book only second event while first event is out of delay with multiple agendas API
freezer.move_to('2021-09-10')
@ -80,7 +82,9 @@ def test_api_events_fillslots_preserve_out_of_delays_bookings_multiple_agendas(a
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 0
assert event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
assert event.booking_set.get().out_of_min_delay is False
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
assert second_event.booking_set.get().out_of_min_delay is False
booking = event.booking_set.get()
# except if we want to bypass delays
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event-2', 'bypass_delays': True}
@ -88,7 +92,9 @@ def test_api_events_fillslots_preserve_out_of_delays_bookings_multiple_agendas(a
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 1
assert event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
assert event.booking_set.get().out_of_min_delay is True
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
assert second_event.booking_set.get().out_of_min_delay is False
booking.save() # reset
# cancel all bookings in delays
@ -97,14 +103,18 @@ def test_api_events_fillslots_preserve_out_of_delays_bookings_multiple_agendas(a
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 1
assert event.booking_set.count() == 1
assert event.booking_set.get().out_of_min_delay is False
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
assert second_event.booking_set.get().out_of_min_delay is False
# bypass delays
params = {'user_external_id': 'user_id', 'slots': '', '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.filter(cancellation_datetime__isnull=True).count() == 0
assert event.booking_set.get().out_of_min_delay is True
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
assert second_event.booking_set.get().out_of_min_delay is False
booking.save() # reset
# book only first event while second event is out of delay with multiple agendas API
@ -339,6 +349,9 @@ def test_api_events_fillslots_multiple_agendas_check_delays(app, user):
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug'},
)
assert resp.json['err'] == 0
booking = Booking.objects.latest('pk')
assert booking.out_of_min_delay is False
booking.delete()
# test minimal_booking_delay
agenda.minimal_booking_delay = 6
@ -355,6 +368,9 @@ def test_api_events_fillslots_multiple_agendas_check_delays(app, user):
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug', 'bypass_delays': True},
)
assert resp.json['err'] == 0
booking = Booking.objects.latest('pk')
assert booking.out_of_min_delay is True
booking.delete()
# test maximal_booking_delay
agenda.minimal_booking_delay = 0
@ -372,6 +388,8 @@ def test_api_events_fillslots_multiple_agendas_check_delays(app, user):
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug', 'bypass_delays': True},
)
assert resp.json['err'] == 0
booking = Booking.objects.latest('pk')
assert booking.out_of_min_delay is False
@pytest.mark.freeze_time('2021-09-06 12:00')