api: mark bookings and cancellations as out of min delay (#63809)
This commit is contained in:
parent
46e07301b6
commit
d728a26447
|
@ -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),
|
||||
),
|
||||
]
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue