api: exclude slots already booked by user in fillslot (#51432)

This commit is contained in:
Lauréline Guérin 2021-02-25 14:51:44 +01:00
parent 6d83b95b1e
commit 5b7c92ea5d
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
2 changed files with 173 additions and 2 deletions

View File

@ -856,6 +856,7 @@ class SlotSerializer(serializers.Serializer):
user_display_label = serializers.CharField(max_length=250, allow_blank=True)
user_email = serializers.CharField(max_length=250, allow_blank=True)
user_phone_number = serializers.CharField(max_length=16, allow_blank=True)
exclude_user = 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)
@ -978,6 +979,8 @@ class Fillslots(APIView):
available_desk = None
color = None
user_external_id = payload.get('user_external_id') or None
exclude_user = payload.get('exclude_user')
if agenda.accept_meetings():
# slots are actually timeslot ids (meeting_type:start_datetime), not events ids.
@ -1024,7 +1027,12 @@ class Fillslots(APIView):
http_status=status.HTTP_400_BAD_REQUEST,
)
all_slots = sorted(
get_all_slots(agenda, meeting_type, resources=resources),
get_all_slots(
agenda,
meeting_type,
resources=resources,
excluded_user_external_id=user_external_id if exclude_user else None,
),
key=lambda slot: slot.start_datetime,
)
@ -1117,7 +1125,7 @@ class Fillslots(APIView):
else:
# convert event recurrence identifiers to real event slugs
for i, slot in enumerate(slots.copy()):
if not ':' in slot:
if ':' not in slot:
continue
event = get_event_recurrence(agenda, slot)
slots[i] = event.slug
@ -1132,6 +1140,11 @@ class Fillslots(APIView):
raise APIError(_('event not bookable'), err_class='event not bookable')
if event.cancelled:
raise APIError(_('event is cancelled'), err_class='event is cancelled')
if exclude_user and user_external_id:
if event.booking_set.filter(user_external_id=user_external_id).exists():
raise APIError(
_('event is already booked by user'), err_class='event is already booked by user'
)
if not events.count():
raise APIError(

View File

@ -1173,6 +1173,103 @@ def test_booking_api(app, some_data, user):
resp = app.post('/api/agenda/0/fillslot/%s/' % event.id, status=404)
@pytest.mark.freeze_time('2021-02-23')
def test_booking_api_exclude_slots(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().replace(hour=10, minute=0),
places=5,
agenda=agenda,
)
Booking.objects.create(event=event, user_external_id='42')
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.post(
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'user_external_id': '42'}
)
assert resp.json['err'] == 0
resp = app.post(
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug),
params={'user_external_id': '42', 'exclude_user': True},
)
assert resp.json['err'] == 1
assert resp.json['err_class'] == 'event is already booked by user'
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'exclude_user': True})
assert resp.json['err'] == 0
event.delete()
# recurrent event
event = Event.objects.create(
slug='recurrent',
start_datetime=localtime().replace(hour=12, minute=0),
repeat='weekly',
places=2,
agenda=agenda,
)
first_recurrence = event.get_or_create_event_recurrence(event.start_datetime)
Booking.objects.create(event=first_recurrence, user_external_id='42')
resp = app.post(
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'user_external_id': '42'}
)
assert resp.json['err'] == 0
resp = app.post(
'/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug),
params={'user_external_id': '42', 'exclude_user': True},
)
assert resp.json['err'] == 1
assert resp.json['err_class'] == 'event is already booked by user'
resp = app.post('/api/agenda/%s/fillslot/%s/' % (agenda.slug, event.slug), params={'exclude_user': True})
assert resp.json['err'] == 0
@pytest.mark.freeze_time('2021-02-25')
def test_booking_api_meetings_agenda_exclude_slots(app, user):
tomorrow = now() + datetime.timedelta(days=1)
agenda = Agenda.objects.create(
label='Agenda', kind='meetings', minimal_booking_delay=0, maximal_booking_delay=10
)
desk = Desk.objects.create(agenda=agenda, slug='desk')
meeting_type = MeetingType.objects.create(agenda=agenda, slug='foo-bar')
TimePeriod.objects.create(
weekday=tomorrow.date().weekday(),
start_time=datetime.time(9, 0),
end_time=datetime.time(17, 00),
desk=desk,
)
desk.duplicate()
desk.duplicate()
event = Event.objects.create(
agenda=agenda,
meeting_type=meeting_type,
places=1,
start_datetime=localtime(tomorrow).replace(hour=9, minute=0),
desk=desk,
)
Booking.objects.create(event=event, user_external_id='42')
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.post(
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (agenda.slug, meeting_type.slug),
params={'user_external_id': '42'},
)
assert resp.json['err'] == 0
resp = app.post(
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (agenda.slug, meeting_type.slug),
params={'user_external_id': '42', 'exclude_user': True},
)
assert resp.json['err'] == 1
assert resp.json['err_class'] == 'no more desk available'
resp = app.post(
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (agenda.slug, meeting_type.slug),
params={'exclude_user': True},
)
assert resp.json['err'] == 0
def test_booking_ics(app, some_data, meetings_agenda, user):
agenda = Agenda.objects.filter(label=u'Foo bar')[0]
event = [x for x in Event.objects.filter(agenda=agenda) if x.in_bookable_period()][0]
@ -5055,6 +5152,67 @@ def test_virtual_agendas_meetings_booking(app, mock_now, user):
assert resp_booking.json['err_desc'] == 'no more desk available'
@pytest.mark.freeze_time('2021-02-25')
def test_virtual_agendas_meetings_booking_exclude_slots(app, user):
tomorrow = now() + datetime.timedelta(days=1)
agenda = Agenda.objects.create(
label='Agenda', kind='meetings', minimal_booking_delay=0, maximal_booking_delay=10
)
desk = Desk.objects.create(agenda=agenda, slug='desk')
meeting_type = MeetingType.objects.create(agenda=agenda, slug='foo-bar')
TimePeriod.objects.create(
weekday=tomorrow.date().weekday(),
start_time=datetime.time(9, 0),
end_time=datetime.time(17, 00),
desk=desk,
)
agenda2 = agenda.duplicate()
agenda3 = agenda.duplicate()
virt_agenda = Agenda.objects.create(
label='Virtual Agenda', kind='virtual', minimal_booking_delay=1, maximal_booking_delay=10
)
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=agenda)
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=agenda2)
VirtualMember.objects.create(virtual_agenda=virt_agenda, real_agenda=agenda3)
event = Event.objects.create(
agenda=agenda,
meeting_type=meeting_type,
places=1,
start_datetime=localtime(tomorrow).replace(hour=9, minute=0),
desk=desk,
)
Booking.objects.create(event=event, user_external_id='42')
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.post(
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (virt_agenda.slug, meeting_type.slug),
params={'user_external_id': '42'},
)
assert resp.json['err'] == 0
resp = app.post(
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (virt_agenda.slug, meeting_type.slug),
params={'user_external_id': '42', 'exclude_user': True},
)
assert resp.json['err'] == 1
assert resp.json['err_class'] == 'no more desk available'
resp = app.post(
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (virt_agenda.slug, meeting_type.slug),
params={'exclude_user': True},
)
assert resp.json['err'] == 0
virt_agenda.minimal_booking_delay = None
virt_agenda.maximal_booking_delay = None
virt_agenda.save()
resp = app.post(
'/api/agenda/%s/fillslot/%s:2021-02-26-0900/' % (virt_agenda.slug, meeting_type.slug),
params={'user_external_id': '42', 'exclude_user': True},
)
assert resp.json['err'] == 1
assert resp.json['err_class'] == 'no more desk available'
def test_virtual_agendas_meetings_booking_default_policy(app, mock_now, user):
foo_agenda = Agenda.objects.create(
label='Foo Meeting', kind='meetings', minimal_booking_delay=1, maximal_booking_delay=5