api: bypass_delays parameter for event fillslot endpoints (#57961)

This commit is contained in:
Lauréline Guérin 2021-10-29 10:40:42 +02:00
parent b07288d1be
commit 268a0db229
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
4 changed files with 146 additions and 7 deletions

View File

@ -1451,19 +1451,25 @@ class Event(models.Model):
self.checked = True
self.save(update_fields=['checked'])
def in_bookable_period(self):
def in_bookable_period(self, bypass_delays=False):
if self.publication_datetime and now() < self.publication_datetime:
return False
if self.agenda.maximal_booking_delay and self.start_datetime > self.agenda.max_booking_datetime:
if (
not bypass_delays
and self.agenda.maximal_booking_delay
and self.start_datetime > self.agenda.max_booking_datetime
):
return False
if self.recurrence_days is not None:
# bookable recurrences probably exist
return True
if self.agenda.minimal_booking_delay and self.start_datetime < self.agenda.min_booking_datetime:
if (
not bypass_delays
and self.agenda.minimal_booking_delay
and self.start_datetime < self.agenda.min_booking_datetime
):
return False
if self.start_datetime < now():
# past the event date, we may want in the future to allow for some
# extra late booking but it's forbidden for now.
return False
return True

View File

@ -33,6 +33,7 @@ class SlotSerializer(serializers.Serializer):
user_phone_number = serializers.CharField(max_length=16, allow_blank=True)
exclude_user = serializers.BooleanField(default=False)
events = serializers.CharField(max_length=16, allow_blank=True)
bypass_delays = serializers.BooleanField(default=False)
form_url = serializers.CharField(max_length=250, allow_blank=True)
backoffice_url = serializers.URLField(allow_blank=True)
cancel_callback_url = serializers.URLField(allow_blank=True)
@ -57,7 +58,7 @@ class EventsSlotsSerializer(SlotSerializer):
def validate(self, attrs):
super().validate(attrs)
if not 'slots' in attrs:
if 'slots' not in attrs:
raise serializers.ValidationError({'slots': _('This field is required.')})
if not attrs.get('user_external_id'):
raise serializers.ValidationError({'user_external_id': _('This field is required.')})

View File

@ -598,6 +598,7 @@ def get_events_from_slots(slots, request, agenda, payload):
book_events = payload.get('events') or request.query_params.get('events') or 'future'
book_past = book_events in ['all', 'past']
book_future = book_events in ['all', 'future']
bypass_delays = payload.get('bypass_delays')
# convert event recurrence identifiers to real event slugs
for i, slot in enumerate(slots.copy()):
@ -613,7 +614,7 @@ def get_events_from_slots(slots, request, agenda, payload):
for event in events:
if event.start_datetime >= now():
if not book_future or not event.in_bookable_period():
if not book_future or not event.in_bookable_period(bypass_delays=bypass_delays):
raise APIError(_('event %s is not bookable') % event.slug, err_class='event not bookable')
else:
if not book_past:

View File

@ -168,6 +168,43 @@ def test_booking_api(app, some_data, user):
resp = app.post('/api/agenda/0/fillslot/%s/' % event.id, status=404)
def test_booking_api_check_delays(app, user):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7
)
event = Event.objects.create(
slug='event-slug',
start_datetime=localtime() + datetime.timedelta(days=5),
places=5,
agenda=agenda,
)
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 0
# test minimal_booking_delay
agenda.minimal_booking_delay = 6
agenda.save()
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 1
assert resp.json['err_class'] == 'event not bookable'
agenda.save()
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'bypass_delays': True})
assert resp.json['err'] == 0
# test maximal_booking_delay
agenda.minimal_booking_delay = 0
agenda.maximal_booking_delay = 3
agenda.save()
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug))
assert resp.json['err'] == 1
assert resp.json['err_class'] == 'event not bookable'
agenda.save()
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'bypass_delays': True})
assert resp.json['err'] == 0
@pytest.mark.freeze_time('2021-02-23')
def test_booking_api_exclude_slots(app, user):
agenda = Agenda.objects.create(
@ -2398,6 +2435,48 @@ def test_api_events_fillslots(app, user):
resp = app.post('/api/agenda/0/events/fillslots/', status=404)
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
)
Event.objects.create(
slug='event-slug',
start_datetime=localtime() + datetime.timedelta(days=5),
places=5,
agenda=agenda,
)
app.authorization = ('Basic', ('john.doe', 'password'))
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
# test minimal_booking_delay
agenda.minimal_booking_delay = 6
agenda.save()
resp = app.post(fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event-slug'})
assert resp.json['err'] == 1
assert resp.json['err_class'] == 'event not bookable'
agenda.save()
resp = app.post(
fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event-slug', 'bypass_delays': True}
)
assert resp.json['err'] == 0
# test maximal_booking_delay
agenda.minimal_booking_delay = 0
agenda.maximal_booking_delay = 3
agenda.save()
resp = app.post(fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event-slug'})
assert resp.json['err'] == 1
assert resp.json['err_class'] == 'event not bookable'
agenda.save()
resp = app.post(
fillslots_url, params={'user_external_id': 'user_id', 'slots': 'event-slug', 'bypass_delays': True}
)
assert resp.json['err'] == 0
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_api_events_fillslots_past_event(app, user):
app.authorization = ('Basic', ('john.doe', 'password'))
@ -2512,6 +2591,58 @@ def test_api_events_fillslots_multiple_agendas(app, user):
)
def test_api_events_fillslots_multiple_agendas_check_delays(app, user):
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7
)
Event.objects.create(
slug='event-slug',
start_datetime=localtime() + datetime.timedelta(days=5),
places=5,
agenda=agenda,
)
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.post(
'/api/agendas/events/fillslots/?agendas=foo-bar',
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug'},
)
assert resp.json['err'] == 0
# test minimal_booking_delay
agenda.minimal_booking_delay = 6
agenda.save()
resp = app.post(
'/api/agendas/events/fillslots/?agendas=foo-bar',
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug'},
)
assert resp.json['err'] == 1
assert resp.json['err_class'] == 'event not bookable'
agenda.save()
resp = app.post(
'/api/agendas/events/fillslots/?agendas=foo-bar',
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug', 'bypass_delays': True},
)
assert resp.json['err'] == 0
# test maximal_booking_delay
agenda.minimal_booking_delay = 0
agenda.maximal_booking_delay = 3
agenda.save()
resp = app.post(
'/api/agendas/events/fillslots/?agendas=foo-bar',
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug'},
)
assert resp.json['err'] == 1
assert resp.json['err_class'] == 'event not bookable'
agenda.save()
resp = app.post(
'/api/agendas/events/fillslots/?agendas=foo-bar',
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug', 'bypass_delays': True},
)
assert resp.json['err'] == 0
def test_url_translation(app, some_data, user):
app.authorization = ('Basic', ('john.doe', 'password'))
agenda_id = Agenda.objects.filter(label='Foo bar')[0].id