chrono/tests/api/fillslot/test_recurring_events.py

1546 lines
60 KiB
Python

import datetime
import pytest
from django.db import connection
from django.test.utils import CaptureQueriesContext
from django.utils.timezone import now
from chrono.agendas.models import (
Agenda,
Booking,
Category,
Desk,
Event,
EventsType,
Person,
SharedCustodyAgenda,
SharedCustodyRule,
Subscription,
)
pytestmark = pytest.mark.django_db
@pytest.mark.parametrize('action', ['book', 'update'])
def test_recurring_events_api_fillslots(app, user, freezer, action):
freezer.move_to('2021-09-06 12:00')
events_type = EventsType.objects.create(label='Foo')
agenda = Agenda.objects.create(
label='Foo bar',
kind='events',
minimal_booking_delay=7,
maximal_booking_delay=35, # only 4 bookable weeks
events_type=events_type,
)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
label='Event',
start_datetime=now(),
recurrence_days=[0, 1, 3, 4], # Monday, Tuesday, Thursday, Friday
places=2,
waiting_list_places=1,
agenda=agenda,
recurrence_end_date=now() + datetime.timedelta(days=14), # 2 weeks
)
event.create_all_recurrences()
sunday_event = Event.objects.create(
label='Sunday Event',
start_datetime=now(),
recurrence_days=[6],
places=2,
waiting_list_places=1,
agenda=agenda,
recurrence_end_date=now() + datetime.timedelta(days=14), # 2 weeks
)
sunday_event.create_all_recurrences()
resp = app.get('/api/agendas/recurring-events/?agendas=%s' % agenda.slug)
assert len(resp.json['data']) == 5
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s&action=%s' % (agenda.slug, action)
params = {'user_external_id': 'user_id'}
# Book Monday and Thursday of first event and Sunday of second event
params['slots'] = 'foo-bar@event:0,foo-bar@event:3,foo-bar@sunday-event:6'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 3
assert 'booked_events' not in resp.json
assert Booking.objects.count() == 3
assert Booking.objects.filter(event__primary_event=event).count() == 2
assert Booking.objects.filter(event__primary_event=sunday_event).count() == 1
events = Event.objects.filter(primary_event__isnull=False)
assert events.filter(booked_places=1).count() == 3
# remove delays
agenda.minimal_booking_delay = 0
agenda.maximal_booking_delay = 0
agenda.save()
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 3
assert 'booked_events' not in resp.json
assert Booking.objects.count() == 6
assert Booking.objects.filter(event__primary_event=event).count() == 4
assert Booking.objects.filter(event__primary_event=sunday_event).count() == 2
events = Event.objects.filter(primary_event__isnull=False)
assert events.filter(booked_places=1).count() == 6
# one recurrence is booked separately
event = Event.objects.filter(primary_event__isnull=False).first()
Booking.objects.create(event=event)
params['user_external_id'] = 'user_id_2'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 6
assert not resp.json['full_events']
assert Booking.objects.count() == 13
events = Event.objects.filter(primary_event__isnull=False)
assert events.filter(booked_places=2).count() == 6
# one booking has been put in waiting list
assert events.filter(booked_waiting_list_places=1).count() == 1
params['user_external_id'] = 'user_id_3'
with CaptureQueriesContext(connection) as ctx:
resp = app.post_json(fillslots_url, params=params)
assert len(ctx.captured_queries) in [12, 13]
# everything goes in waiting list
assert events.filter(booked_waiting_list_places=1).count() == 6
# but an event was full
assert resp.json['booking_count'] == 5
assert len(resp.json['full_events']) == 1
assert resp.json['full_events'][0]['slug'] == event.slug
resp = app.post_json(fillslots_url, params=params)
# events are full
params['user_external_id'] = 'user_id_4'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
# no event in range
resp = app.post_json(fillslots_url + '&date_start=2020-09-13&date_end=2020-09-19', params=params)
assert resp.json['booking_count'] == 0
params['slots'] = 'foo-bar@event:1'
params['include_booked_events_detail'] = True
resp = app.post_json(fillslots_url + '&date_start=2021-09-13&date_end=2021-09-19', params=params)
assert resp.json['booking_count'] == 1
assert Booking.objects.filter(user_external_id='user_id_4').count() == 1
assert len(resp.json['booked_events']) == 1
assert resp.json['booked_events'][0]['id'] == 'foo-bar@event--2021-09-14-1400'
assert (
resp.json['booked_events'][0]['booking']['id']
== Booking.objects.filter(user_external_id='user_id_4').get().pk
)
resp = app.post_json(fillslots_url, params={'slots': 'foo-bar@event:0'}, status=400)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['user_external_id'] == ['This field is required.']
resp = app.post_json(fillslots_url, params={'user_external_id': 'a'}, status=400)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['slots'] == ['This field is required.']
resp = app.post_json(fillslots_url, params={'user_external_id': 'a', 'slots': 'foo-bar@a:a'}, status=400)
assert resp.json['err'] == 1
assert resp.json['errors']['slots'] == ['invalid slot: foo-bar@a:a']
resp = app.post_json(fillslots_url, params={'user_external_id': 'a', 'slots': 'foo-bar@a:1'}, status=400)
assert resp.json['err'] == 1
assert resp.json['errors']['slots'] == ['event a of agenda foo-bar is not bookable']
resp = app.post_json(fillslots_url, params={'user_external_id': 'a', 'slots': 'foo-bar'}, status=400)
assert resp.json['err'] == 1
assert resp.json['errors']['slots'] == ['Invalid format for slot foo-bar']
missing_action_url = '/api/agendas/recurring-events/fillslots/?agendas=%s' % agenda.slug
resp = app.post_json(missing_action_url, params=params, status=400)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['action'] == ['This field is required.']
resp = app.post_json(missing_action_url + '&action=invalid', params=params, status=400)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'invalid payload'
assert resp.json['errors']['action'] == ['"invalid" is not a valid choice.']
def test_recurring_events_api_fillslots_waiting_list(app, user, freezer):
freezer.move_to('2021-09-06 12:00')
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=0
)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
label='Event',
start_datetime=now(),
recurrence_days=[0],
places=2,
waiting_list_places=2,
agenda=agenda,
recurrence_end_date=now() + datetime.timedelta(days=30),
)
event.create_all_recurrences()
app.authorization = ('Basic', ('john.doe', 'password'))
# create bookings in waiting list
for recurrence in event.recurrences.all():
Booking.objects.create(event=recurrence, in_waiting_list=True)
events = Event.objects.filter(primary_event__isnull=False)
assert events.filter(booked_waiting_list_places=1).count() == 5
# check that new bookings are put in waiting list despite free slots on main list
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event:0'}
resp = app.post_json(
'/api/agendas/recurring-events/fillslots/?agendas=%s&action=update' % agenda.slug, params=params
)
assert resp.json['booking_count'] == 5
assert events.filter(booked_waiting_list_places=2).count() == 5
def test_recurring_events_api_fillslots_book_with_cancelled(app, user, freezer):
freezer.move_to('2021-09-06 12:00')
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
label='Event',
start_datetime=now(),
recurrence_days=[0, 1], # Monday, Tuesday
places=1,
waiting_list_places=1,
agenda=agenda,
recurrence_end_date=now() + datetime.timedelta(days=21),
)
event.create_all_recurrences()
# create cancelled bookings for the user
event_1_0 = Event.objects.get(primary_event=event, start_datetime=event.start_datetime)
booking_1_0 = Booking.objects.create(event=event_1_0, user_external_id='user_id')
booking_1_0.cancel()
assert booking_1_0.cancellation_datetime is not None
event_1_1 = Event.objects.get(
primary_event=event, start_datetime=event.start_datetime + datetime.timedelta(days=1)
)
booking_1_1 = Booking.objects.create(event=event_1_1, user_external_id='user_id')
booking_1_1.cancel()
assert booking_1_1.cancellation_datetime is not None
# and non cancelled bookings for the user
event_2_0 = Event.objects.get(
primary_event=event, start_datetime=event.start_datetime + datetime.timedelta(days=7)
)
booking_2_0 = Booking.objects.create(event=event_2_0, user_external_id='user_id')
assert booking_2_0.cancellation_datetime is None
event_2_1 = Event.objects.get(
primary_event=event, start_datetime=event.start_datetime + datetime.timedelta(days=8)
)
booking_2_1 = Booking.objects.create(event=event_2_1, user_external_id='user_id')
assert booking_2_1.cancellation_datetime is None
# and bookings for another user
Booking.objects.create(event=event_1_0, user_external_id='user_id_foobar')
other_booking = Booking.objects.create(event=event_2_0, user_external_id='user_id_foobar')
other_booking.cancel()
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s&action=book' % agenda.slug
params = {'user_external_id': 'user_id'}
# Book Monday
params['slots'] = 'foo-bar@event:0'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 2
assert resp.json['cancelled_booking_count'] == 0
assert Booking.objects.filter(user_external_id='user_id').count() == 5
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 4
assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=2).count() == 3
assert (
Booking.objects.filter(
user_external_id='user_id',
event__start_datetime__week_day=2,
cancellation_datetime__isnull=True,
).count()
== 3
)
assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=3).count() == 2
assert (
Booking.objects.filter(
user_external_id='user_id',
event__start_datetime__week_day=3,
cancellation_datetime__isnull=True,
).count()
== 1
)
assert Booking.objects.filter(pk=booking_1_0.pk).exists() is False # cancelled booking deleted
booking_1_1.refresh_from_db()
booking_2_0.refresh_from_db()
booking_2_1.refresh_from_db()
assert booking_1_1.cancellation_datetime is not None
assert booking_2_0.cancellation_datetime is None
assert booking_2_1.cancellation_datetime is None
# Book Tuesday
params['slots'] = 'foo-bar@event:1'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 2
assert resp.json['cancelled_booking_count'] == 0
assert Booking.objects.filter(user_external_id='user_id').count() == 6
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 6
assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=2).count() == 3
assert (
Booking.objects.filter(
user_external_id='user_id',
event__start_datetime__week_day=2,
cancellation_datetime__isnull=True,
).count()
== 3
)
assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=3).count() == 3
assert (
Booking.objects.filter(
user_external_id='user_id',
event__start_datetime__week_day=3,
cancellation_datetime__isnull=True,
).count()
== 3
)
assert Booking.objects.filter(pk=booking_1_0.pk).exists() is False # cancelled booking deleted
assert Booking.objects.filter(pk=booking_1_1.pk).exists() is False # cancelled booking deleted
booking_2_0.refresh_from_db()
booking_2_1.refresh_from_db()
assert booking_2_0.cancellation_datetime is None
assert booking_2_1.cancellation_datetime is None
def test_recurring_events_api_fillslots_update(app, user, freezer):
freezer.move_to('2021-09-06 12:00')
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=0
)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
label='Event',
start_datetime=now(),
recurrence_days=[0, 1, 3, 4], # Monday, Tuesday, Thursday, Friday
places=1,
waiting_list_places=1,
agenda=agenda,
recurrence_end_date=now() + datetime.timedelta(days=28), # 4 weeks
)
event.create_all_recurrences()
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s&action=update' % agenda.slug
params = {'user_external_id': 'user_id'}
# Book Monday and Thursday
params['slots'] = 'foo-bar@event:0,foo-bar@event:3'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 8
assert resp.json['cancelled_booking_count'] == 0
assert Booking.objects.count() == 8
assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 4
assert Booking.objects.filter(event__start_datetime__week_day=5).count() == 4
# Book Friday without changing other bookings
params['slots'] = 'foo-bar@event:4'
resp = app.post_json(fillslots_url.replace('update', 'book'), params=params)
assert resp.json['booking_count'] == 4
assert resp.json['cancelled_booking_count'] == 0
assert Booking.objects.count() == 12
assert Booking.objects.filter(event__start_datetime__week_day=2).count() == 4
assert Booking.objects.filter(event__start_datetime__week_day=5).count() == 4
assert Booking.objects.filter(event__start_datetime__week_day=6).count() == 4
# set booking delays - only 2 weeks bookable
agenda.minimal_booking_delay = 7
agenda.maximal_booking_delay = 21
agenda.save()
# Change booking to Monday and Tuesday
params['slots'] = 'foo-bar@event:0,foo-bar@event:1'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 2
assert resp.json['cancelled_booking_count'] == 4
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 10
assert (
Booking.objects.filter(
event__start_datetime__week_day=2,
cancellation_datetime__isnull=True,
).count()
== 4
)
assert (
Booking.objects.filter(
event__start_datetime__week_day=3,
cancellation_datetime__isnull=True,
).count()
== 2
)
assert (
Booking.objects.filter(
event__start_datetime__week_day=5,
cancellation_datetime__isnull=True,
).count()
== 2
)
assert (
Booking.objects.filter(
event__start_datetime__week_day=6,
cancellation_datetime__isnull=True,
).count()
== 2
)
# remove delays
agenda.minimal_booking_delay = 0
agenda.maximal_booking_delay = 0
agenda.save()
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 2
assert resp.json['cancelled_booking_count'] == 4
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 8
assert (
Booking.objects.filter(
event__start_datetime__week_day=2,
cancellation_datetime__isnull=True,
).count()
== 4
)
assert (
Booking.objects.filter(
event__start_datetime__week_day=3,
cancellation_datetime__isnull=True,
).count()
== 4
)
# Booking again does nothing
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 0
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 8
params = {'user_external_id': 'user_id_2'}
params['slots'] = 'foo-bar@event:0,foo-bar@event:3'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 8
assert resp.json['cancelled_booking_count'] == 0
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 16
assert (
Booking.objects.filter(
event__start_datetime__week_day=2,
cancellation_datetime__isnull=True,
).count()
== 8
)
assert (
Booking.objects.filter(
event__start_datetime__week_day=3,
cancellation_datetime__isnull=True,
).count()
== 4
)
assert (
Booking.objects.filter(
event__start_datetime__week_day=5,
cancellation_datetime__isnull=True,
).count()
== 4
)
events = Event.objects.filter(primary_event__isnull=False)
assert events.filter(booked_places=1).count() == 12
assert events.filter(booked_waiting_list_places=1).count() == 4
params['slots'] = 'foo-bar@event:1,foo-bar@event:4'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 8
assert resp.json['cancelled_booking_count'] == 8
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 16
assert (
Booking.objects.filter(
event__start_datetime__week_day=2,
cancellation_datetime__isnull=True,
).count()
== 4
)
assert (
Booking.objects.filter(
event__start_datetime__week_day=3,
cancellation_datetime__isnull=True,
).count()
== 8
)
assert (
Booking.objects.filter(
event__start_datetime__week_day=6,
cancellation_datetime__isnull=True,
).count()
== 4
)
events = Event.objects.filter(primary_event__isnull=False)
assert events.filter(booked_places=1).count() == 12
assert events.filter(booked_waiting_list_places=1).count() == 4
# specifying end date cancels some bookings
resp = app.post_json(fillslots_url + '&date_end=2021-09-26', params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 2
assert (
Booking.objects.filter(
user_external_id='user_id_2',
cancellation_datetime__isnull=True,
).count()
== 6
)
# past bookings are left in place
freezer.move_to('2021-09-13 12:00')
resp = app.post_json(fillslots_url + '&date_start=2021-09-06&date_end=2021-09-19', params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 2 # only week from 13 to 19 of septembre was cancelled
assert (
Booking.objects.filter(
user_external_id='user_id_2',
cancellation_datetime__isnull=True,
).count()
== 4
)
# passing empty slots cancels all bookings
params['slots'] = ''
resp = app.post_json(fillslots_url, params=params)
assert resp.json['cancelled_booking_count'] == 2
assert (
Booking.objects.filter(
user_external_id='user_id_2',
cancellation_datetime__isnull=True,
).count()
== 2
)
assert (
Booking.objects.filter(
user_external_id='user_id_2',
event__start_datetime__gt=now(),
cancellation_datetime__isnull=True,
).count()
== 0
)
# only recurring events are impacted
normal_event = Event.objects.create(
start_datetime=now() + datetime.timedelta(days=1), places=2, agenda=agenda
)
Booking.objects.create(event=normal_event, user_external_id='user_id')
resp = app.post_json(fillslots_url, params={'user_external_id': 'user_id', 'slots': 'foo-bar@event:0'})
assert resp.json['cancelled_booking_count'] == 3
assert Booking.objects.filter(user_external_id='user_id', event=normal_event).count() == 1
def test_recurring_events_api_fillslots_update_with_cancelled(app, user, freezer):
freezer.move_to('2021-09-06 12:00')
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
label='Event',
start_datetime=now(),
recurrence_days=[0, 1], # Monday, Tuesday
places=1,
waiting_list_places=1,
agenda=agenda,
recurrence_end_date=now() + datetime.timedelta(days=21),
)
event.create_all_recurrences()
# create cancelled bookings for the user
event_1_0 = Event.objects.get(primary_event=event, start_datetime=event.start_datetime)
booking_1_0 = Booking.objects.create(event=event_1_0, user_external_id='user_id')
booking_1_0.cancel()
assert booking_1_0.cancellation_datetime is not None
event_1_1 = Event.objects.get(
primary_event=event, start_datetime=event.start_datetime + datetime.timedelta(days=1)
)
booking_1_1 = Booking.objects.create(event=event_1_1, user_external_id='user_id')
booking_1_1.cancel()
assert booking_1_1.cancellation_datetime is not None
# and non cancelled bookings for the user
event_2_0 = Event.objects.get(
primary_event=event, start_datetime=event.start_datetime + datetime.timedelta(days=7)
)
booking_2_0 = Booking.objects.create(event=event_2_0, user_external_id='user_id')
assert booking_2_0.cancellation_datetime is None
event_2_1 = Event.objects.get(
primary_event=event, start_datetime=event.start_datetime + datetime.timedelta(days=8)
)
booking_2_1 = Booking.objects.create(event=event_2_1, user_external_id='user_id')
assert booking_2_1.cancellation_datetime is None
# secondary booking for this one
booking_2_1_secondary = Booking.objects.create(event=event_2_1, primary_booking=booking_2_1)
# and bookings for another user
Booking.objects.create(event=event_1_0, user_external_id='user_id_foobar')
other_booking = Booking.objects.create(event=event_2_0, user_external_id='user_id_foobar')
other_booking.cancel()
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s&action=update' % agenda.slug
params = {'user_external_id': 'user_id'}
# Book Monday
params['slots'] = 'foo-bar@event:0'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 2
assert resp.json['cancelled_booking_count'] == 1
assert Booking.objects.filter(user_external_id='user_id').count() == 5
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 3
assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=2).count() == 3
assert (
Booking.objects.filter(
user_external_id='user_id',
event__start_datetime__week_day=2,
cancellation_datetime__isnull=True,
).count()
== 3
)
assert (
Booking.objects.filter(event__start_datetime__week_day=3, primary_booking__isnull=True).count() == 2
)
assert (
Booking.objects.filter(
user_external_id='user_id',
event__start_datetime__week_day=3,
cancellation_datetime__isnull=True,
).count()
== 0
)
assert Booking.objects.filter(pk=booking_1_0.pk).exists() is False # cancelled booking deleted
booking_1_1.refresh_from_db()
booking_2_0.refresh_from_db()
booking_2_1.refresh_from_db()
booking_2_1_secondary.refresh_from_db()
assert booking_1_1.cancellation_datetime is not None
assert booking_2_0.cancellation_datetime is None
assert booking_2_1.cancellation_datetime is not None
assert booking_2_1_secondary.cancellation_datetime is not None
# Book Tuesday
params['slots'] = 'foo-bar@event:1'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 3
assert resp.json['cancelled_booking_count'] == 3
assert Booking.objects.filter(user_external_id='user_id').count() == 6
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 3
assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=2).count() == 3
assert (
Booking.objects.filter(
user_external_id='user_id',
event__start_datetime__week_day=2,
cancellation_datetime__isnull=True,
).count()
== 0
)
assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=3).count() == 3
assert (
Booking.objects.filter(
user_external_id='user_id',
event__start_datetime__week_day=3,
cancellation_datetime__isnull=True,
).count()
== 3
)
assert Booking.objects.filter(pk=booking_1_0.pk).exists() is False # cancelled booking deleted
assert Booking.objects.filter(pk=booking_1_1.pk).exists() is False # cancelled booking deleted
assert Booking.objects.filter(pk=booking_2_1.pk).exists() is False # cancelled booking deleted
assert Booking.objects.filter(pk=booking_2_1_secondary.pk).exists() is False # cancelled booking deleted
booking_2_0.refresh_from_db()
assert booking_2_0.cancellation_datetime is not None
def test_recurring_events_api_fillslots_unbook(app, user, freezer):
freezer.move_to('2021-09-06 12:00')
agenda = Agenda.objects.create(
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=0
)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
label='Event',
start_datetime=now(),
recurrence_days=[0, 1, 3, 4], # Monday, Tuesday, Thursday, Friday
places=2,
waiting_list_places=1,
agenda=agenda,
recurrence_end_date=now() + datetime.timedelta(days=28), # 4 weeks
)
event.create_all_recurrences()
sunday_event = Event.objects.create(
label='Sunday Event',
start_datetime=now(),
recurrence_days=[6],
places=2,
waiting_list_places=1,
agenda=agenda,
recurrence_end_date=now() + datetime.timedelta(days=28), # 4 weeks
)
sunday_event.create_all_recurrences()
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s' % agenda.slug
params = {'user_external_id': 'user_id'}
params['slots'] = 'foo-bar@event:0,foo-bar@event:3,foo-bar@sunday-event:6'
resp = app.post_json(fillslots_url + '&action=book', params=params)
assert resp.json['booking_count'] == 12
assert Booking.objects.count() == 12
assert Booking.objects.filter(event__primary_event=event).count() == 8
assert Booking.objects.filter(event__primary_event=sunday_event).count() == 4
# set booking delays - only 2 weeks bookable
agenda.minimal_booking_delay = 7
agenda.maximal_booking_delay = 21
agenda.save()
params['slots'] = 'foo-bar@event:0'
resp = app.post_json(fillslots_url + '&action=unbook', params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 2
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 10
assert (
Booking.objects.filter(
event__primary_event=event,
cancellation_datetime__isnull=True,
).count()
== 6
)
assert (
Booking.objects.filter(
event__primary_event=sunday_event,
cancellation_datetime__isnull=True,
).count()
== 4
)
# remove delays
agenda.minimal_booking_delay = 0
agenda.maximal_booking_delay = 0
agenda.save()
resp = app.post_json(fillslots_url + '&action=unbook', params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 2
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 8
assert (
Booking.objects.filter(
event__primary_event=event,
cancellation_datetime__isnull=True,
).count()
== 4
)
assert (
Booking.objects.filter(
event__primary_event=event,
cancellation_datetime__isnull=False,
).count()
== 4
)
assert (
Booking.objects.filter(
event__primary_event=sunday_event,
cancellation_datetime__isnull=True,
).count()
== 4
)
params['slots'] = 'foo-bar@sunday-event:6'
resp = app.post_json(
fillslots_url + '&action=unbook&date_start=2021-09-13&date_end=2021-09-20', params=params
)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 1
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 7
assert (
Booking.objects.filter(
event__primary_event=event,
cancellation_datetime__isnull=True,
).count()
== 4
)
assert (
Booking.objects.filter(
event__primary_event=event,
cancellation_datetime__isnull=False,
).count()
== 4
)
assert (
Booking.objects.filter(
event__primary_event=sunday_event,
cancellation_datetime__isnull=True,
).count()
== 3
)
assert (
Booking.objects.filter(
event__primary_event=sunday_event,
cancellation_datetime__isnull=False,
).count()
== 1
)
assert not Booking.objects.filter(
event__primary_event=sunday_event,
event__start_datetime__range=(
datetime.date(year=2021, month=9, day=13),
datetime.date(year=2021, month=9, day=20),
),
cancellation_datetime__isnull=True,
).exists()
freezer.move_to('2021-09-13 12:00')
# old bookings are not unbooked
params['slots'] = 'foo-bar@event:3'
resp = app.post_json(fillslots_url + '&action=unbook', params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 3
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 4
assert (
Booking.objects.filter(
event__primary_event=event,
cancellation_datetime__isnull=True,
).count()
== 1
)
assert not Booking.objects.filter(
event__primary_event=event,
event__start_datetime__gt=datetime.date(year=2021, month=9, day=13),
cancellation_datetime__isnull=True,
).exists()
assert (
Booking.objects.filter(
event__primary_event=sunday_event,
cancellation_datetime__isnull=True,
).count()
== 3
)
# unbooking when there are no bookings does nothing
params['user_external_id'] = 'user_id_2'
resp = app.post_json(fillslots_url + '&action=unbook', params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 0
def test_recurring_events_api_fillslots_unbook_with_cancelled(app, user, freezer):
freezer.move_to('2021-09-06 12:00')
agenda = Agenda.objects.create(label='Foo bar', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
label='Event',
start_datetime=now(),
recurrence_days=[0, 1], # Monday, Tuesday
places=1,
waiting_list_places=1,
agenda=agenda,
recurrence_end_date=now() + datetime.timedelta(days=21),
)
event.create_all_recurrences()
# create cancelled bookings for the user
event_1_0 = Event.objects.get(primary_event=event, start_datetime=event.start_datetime)
booking_1_0 = Booking.objects.create(event=event_1_0, user_external_id='user_id')
booking_1_0.cancel()
assert booking_1_0.cancellation_datetime is not None
event_1_1 = Event.objects.get(
primary_event=event, start_datetime=event.start_datetime + datetime.timedelta(days=1)
)
booking_1_1 = Booking.objects.create(event=event_1_1, user_external_id='user_id')
booking_1_1.cancel()
assert booking_1_1.cancellation_datetime is not None
# and non cancelled bookings for the user
event_2_0 = Event.objects.get(
primary_event=event, start_datetime=event.start_datetime + datetime.timedelta(days=7)
)
booking_2_0 = Booking.objects.create(event=event_2_0, user_external_id='user_id')
assert booking_2_0.cancellation_datetime is None
event_2_1 = Event.objects.get(
primary_event=event, start_datetime=event.start_datetime + datetime.timedelta(days=8)
)
booking_2_1 = Booking.objects.create(event=event_2_1, user_external_id='user_id')
assert booking_2_1.cancellation_datetime is None
# and bookings for another user
Booking.objects.create(event=event_1_0, user_external_id='user_id_foobar')
other_booking = Booking.objects.create(event=event_2_0, user_external_id='user_id_foobar')
other_booking.cancel()
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?agendas=%s&action=unbook' % agenda.slug
params = {'user_external_id': 'user_id'}
# unbook Monday
params['slots'] = 'foo-bar@event:0'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 1
assert Booking.objects.filter(user_external_id='user_id').count() == 4
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 1
assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=2).count() == 2
assert (
Booking.objects.filter(
user_external_id='user_id',
event__start_datetime__week_day=2,
cancellation_datetime__isnull=True,
).count()
== 0
)
assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=3).count() == 2
assert (
Booking.objects.filter(
user_external_id='user_id',
event__start_datetime__week_day=3,
cancellation_datetime__isnull=True,
).count()
== 1
)
booking_1_0.refresh_from_db()
booking_1_1.refresh_from_db()
booking_2_0.refresh_from_db()
booking_2_1.refresh_from_db()
assert booking_1_0.cancellation_datetime is not None
assert booking_1_1.cancellation_datetime is not None
assert booking_2_0.cancellation_datetime is not None
assert booking_2_1.cancellation_datetime is None
# unbook Tuesday
params['slots'] = 'foo-bar@event:1'
resp = app.post_json(fillslots_url, params=params)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 1
assert Booking.objects.filter(user_external_id='user_id').count() == 4
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 0
assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=2).count() == 2
assert (
Booking.objects.filter(
user_external_id='user_id',
event__start_datetime__week_day=2,
cancellation_datetime__isnull=True,
).count()
== 0
)
assert Booking.objects.filter(user_external_id='user_id', event__start_datetime__week_day=3).count() == 2
assert (
Booking.objects.filter(
user_external_id='user_id',
event__start_datetime__week_day=3,
cancellation_datetime__isnull=True,
).count()
== 0
)
booking_1_0.refresh_from_db()
booking_1_1.refresh_from_db()
booking_2_0.refresh_from_db()
booking_2_1.refresh_from_db()
assert booking_1_0.cancellation_datetime is not None
assert booking_1_1.cancellation_datetime is not None
assert booking_2_0.cancellation_datetime is not None
assert booking_2_1.cancellation_datetime is not None
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_recurring_events_api_fillslots_subscribed(app, user):
category = Category.objects.create(label='Category A')
first_agenda = Agenda.objects.create(
label='First agenda',
kind='events',
category=category,
minimal_booking_delay=0,
maximal_booking_delay=0,
)
Desk.objects.create(agenda=first_agenda, slug='_exceptions_holder')
category = Category.objects.create(label='Category B')
second_agenda = Agenda.objects.create(
label='Second agenda',
kind='events',
category=category,
minimal_booking_delay=0,
maximal_booking_delay=0,
)
Desk.objects.create(agenda=second_agenda, slug='_exceptions_holder')
event = Event.objects.create(
slug='event',
start_datetime=now(),
recurrence_days=[0, 1, 3, 4], # Monday, Tuesday, Thursday, Friday
places=2,
waiting_list_places=1,
agenda=first_agenda,
recurrence_end_date=now() + datetime.timedelta(days=364),
)
event.create_all_recurrences()
sunday_event = Event.objects.create(
slug='sunday-event',
start_datetime=now(),
recurrence_days=[6],
places=2,
waiting_list_places=1,
agenda=second_agenda,
recurrence_end_date=now() + datetime.timedelta(days=364),
)
sunday_event.create_all_recurrences()
subscription = Subscription.objects.create(
agenda=first_agenda,
user_external_id='xxx',
date_start=now() + datetime.timedelta(days=14), # Monday 20/09
date_end=now() + datetime.timedelta(days=45), # Thursday 21/10
)
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?action=update&subscribed=%s'
params = {'user_external_id': 'xxx'}
# book Monday and Thursday of first event, in subscription range
params['slots'] = 'first-agenda@event:0,first-agenda@event:3'
resp = app.post_json(fillslots_url % 'category-a', params=params)
assert resp.json['booking_count'] == 9
assert Booking.objects.count() == 9
assert Booking.objects.order_by('event__start_datetime').filter(event__primary_event=event).count() == 9
assert (
Booking.objects.order_by('event__start_datetime').first().event.start_datetime.strftime('%d/%m')
== '20/09'
) # first subscription's day
assert Booking.objects.last().event.start_datetime.strftime('%d/%m') == '18/10' # last subscription's day
subscription.date_end = now() + datetime.timedelta(days=46) # Friday 22/10
subscription.save()
resp = app.post_json(fillslots_url % 'category-a', params=params)
assert resp.json['booking_count'] == 1
assert Booking.objects.count() == 10
assert Booking.objects.filter(event__primary_event=event).count() == 10
assert (
Booking.objects.order_by('event__start_datetime').first().event.start_datetime.strftime('%d/%m')
== '20/09'
) # first subscription's day
assert (
Booking.objects.order_by('event__start_datetime').last().event.start_datetime.strftime('%d/%m')
== '21/10'
) # last subscription's day
# wrong category
resp = app.post_json(fillslots_url % 'category-b', params=params, status=400)
# book Monday and Thursday of first event, in subscription range, but with date_start and date_end params
resp = app.post_json(
fillslots_url % 'category-a' + '&date_start=2021-09-21&date_end=2021-10-21', params=params
)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 2
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 8
assert (
Booking.objects.filter(cancellation_datetime__isnull=True)
.order_by('event__start_datetime')
.first()
.event.start_datetime.strftime('%d/%m')
== '23/09'
) # first subscription's day
assert (
Booking.objects.filter(cancellation_datetime__isnull=True)
.order_by('event__start_datetime')
.last()
.event.start_datetime.strftime('%d/%m')
== '18/10'
)
resp = app.post_json(
fillslots_url % 'category-a' + '&date_start=2021-09-01&date_end=2021-10-31', params=params
)
assert resp.json['booking_count'] == 2
assert resp.json['cancelled_booking_count'] == 0
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 10
assert (
Booking.objects.filter(cancellation_datetime__isnull=True)
.order_by('event__start_datetime')
.first()
.event.start_datetime.strftime('%d/%m')
== '20/09'
) # first subscription's day
assert (
Booking.objects.filter(cancellation_datetime__isnull=True)
.order_by('event__start_datetime')
.last()
.event.start_datetime.strftime('%d/%m')
== '21/10'
)
# not subscribed category
params['slots'] = 'second-agenda@sunday-event:6'
resp = app.post_json(fillslots_url % 'category-b', params=params, status=400)
# update bookings
Subscription.objects.create(
agenda=second_agenda,
user_external_id='xxx',
date_start=now() + datetime.timedelta(days=100), # Wednesday 15/12
date_end=now() + datetime.timedelta(days=150), # Thursday 03/02
)
params['slots'] = 'first-agenda@event:1,second-agenda@sunday-event:6'
resp = app.post_json(fillslots_url % 'all', params=params)
assert resp.json['booking_count'] == 12
assert resp.json['cancelled_booking_count'] == 10
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 12
booked_events_first_agenda = Event.objects.filter(
primary_event=event,
booking__isnull=False,
booking__cancellation_datetime__isnull=True,
)
assert [
x.strftime('%d/%m/%Y') for x in booked_events_first_agenda.values_list('start_datetime', flat=True)
] == ['21/09/2021', '28/09/2021', '05/10/2021', '12/10/2021', '19/10/2021']
booked_events_second_agenda = Event.objects.filter(
primary_event=sunday_event,
booking__isnull=False,
booking__cancellation_datetime__isnull=True,
)
assert [
x.strftime('%d/%m/%Y') for x in booked_events_second_agenda.values_list('start_datetime', flat=True)
] == ['19/12/2021', '26/12/2021', '02/01/2022', '09/01/2022', '16/01/2022', '23/01/2022', '30/01/2022']
# other user
Subscription.objects.create(
agenda=second_agenda,
user_external_id='yyy',
date_start=now(),
date_end=now() + datetime.timedelta(days=10),
)
# disjoint subscription
Subscription.objects.create(
agenda=second_agenda,
user_external_id='yyy',
date_start=now() + datetime.timedelta(days=60),
date_end=now() + datetime.timedelta(days=70),
)
params = {'user_external_id': 'yyy', 'slots': 'second-agenda@sunday-event:6'}
resp = app.post_json(fillslots_url % 'category-b', params=params)
assert resp.json['booking_count'] == 3
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 15
booked_events_user_yyy = Event.objects.filter(
primary_event=sunday_event,
booking__user_external_id='yyy',
booking__cancellation_datetime__isnull=True,
)
assert [
x.strftime('%d/%m/%Y') for x in booked_events_user_yyy.values_list('start_datetime', flat=True)
] == ['12/09/2021', '07/11/2021', '14/11/2021']
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_recurring_events_api_fillslots_multiple_agendas(app, user):
agenda = Agenda.objects.create(
label='First Agenda', kind='events', minimal_booking_delay=0, maximal_booking_delay=0
)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
start, end = now(), now() + datetime.timedelta(days=30)
event_a = Event.objects.create(
label='A',
start_datetime=start,
places=2,
recurrence_end_date=end,
recurrence_days=[0, 2, 5],
agenda=agenda,
)
event_a.create_all_recurrences()
event_b = Event.objects.create(
label='B', start_datetime=start, places=2, recurrence_end_date=end, recurrence_days=[1], agenda=agenda
)
event_b.create_all_recurrences()
agenda2 = Agenda.objects.create(label='Second Agenda', kind='events')
Desk.objects.create(agenda=agenda2, slug='_exceptions_holder')
event_c = Event.objects.create(
label='C',
start_datetime=start,
places=2,
recurrence_end_date=end,
recurrence_days=[2, 3],
agenda=agenda2,
)
event_c.create_all_recurrences()
resp = app.get('/api/agendas/recurring-events/?agendas=first-agenda,second-agenda')
assert len(resp.json['data']) == 6
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?action=%s&agendas=%s'
params = {'user_external_id': 'user_id', 'slots': 'first-agenda@a:0,first-agenda@a:5,second-agenda@c:3'}
resp = app.post_json(fillslots_url % ('update', 'first-agenda,second-agenda'), params=params)
assert resp.json['booking_count'] == 13
assert Booking.objects.count() == 13
assert Booking.objects.filter(event__primary_event=event_a).count() == 9
assert Booking.objects.filter(event__primary_event=event_b).count() == 0
assert Booking.objects.filter(event__primary_event=event_c).count() == 4
# add bookings
params = {'user_external_id': 'user_id', 'slots': 'first-agenda@a:2,second-agenda@c:2'}
resp = app.post_json(fillslots_url % ('book', 'first-agenda,second-agenda'), params=params)
assert resp.json['booking_count'] == 8
assert resp.json['cancelled_booking_count'] == 0
assert Booking.objects.count() == 21
assert Booking.objects.filter(event__primary_event=event_a).count() == 13
assert Booking.objects.filter(event__primary_event=event_b).count() == 0
assert Booking.objects.filter(event__primary_event=event_c).count() == 8
# unbook last week bookings
params = {'user_external_id': 'user_id', 'slots': 'first-agenda@a:2,second-agenda@c:2'}
date_start_param = '&date_start=%s' % (end - datetime.timedelta(days=7)).strftime('%Y-%m-%d')
resp = app.post_json(
(fillslots_url % ('unbook', 'first-agenda,second-agenda')) + date_start_param, params=params
)
assert resp.json['booking_count'] == 0
assert resp.json['cancelled_booking_count'] == 2
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 19
assert (
Booking.objects.filter(
event__primary_event=event_a,
cancellation_datetime__isnull=True,
).count()
== 12
)
assert (
Booking.objects.filter(
event__primary_event=event_b,
cancellation_datetime__isnull=True,
).count()
== 0
)
assert (
Booking.objects.filter(
event__primary_event=event_c,
cancellation_datetime__isnull=True,
).count()
== 7
)
# update bookings
params = {'user_external_id': 'user_id', 'slots': 'first-agenda@b:1'}
resp = app.post_json(fillslots_url % ('update', 'first-agenda,second-agenda'), params=params)
assert resp.json['booking_count'] == 5
assert resp.json['cancelled_booking_count'] == 19
assert (
Booking.objects.filter(
event__primary_event=event_a,
cancellation_datetime__isnull=True,
).count()
== 0
)
assert (
Booking.objects.filter(
event__primary_event=event_b,
cancellation_datetime__isnull=True,
).count()
== 5
)
assert (
Booking.objects.filter(
event__primary_event=event_c,
cancellation_datetime__isnull=True,
).count()
== 0
)
# error if slot's agenda is not in querystring
resp = app.post_json(fillslots_url % ('update', 'second-agenda'), params=params, status=400)
assert resp.json['err'] == 1
assert resp.json['errors']['slots'] == [
'Events from the following agendas cannot be booked: first-agenda'
]
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_recurring_events_api_fillslots_multiple_agendas_queries(app, user):
events_type = EventsType.objects.create(label='Foo')
for i in range(20):
agenda = Agenda.objects.create(slug=f'{i}', kind='events', events_type=events_type)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
start, end = now(), now() + datetime.timedelta(days=30)
event = Event.objects.create(
start_datetime=start, places=2, recurrence_end_date=end, recurrence_days=[1, 2], agenda=agenda
)
event.create_all_recurrences()
agenda_slugs = ','.join(str(i) for i in range(20))
resp = app.get('/api/agendas/recurring-events/?action=update&agendas=%s' % agenda_slugs)
events_to_book = [x['id'] for x in resp.json['data']]
app.authorization = ('Basic', ('john.doe', 'password'))
with CaptureQueriesContext(connection) as ctx:
resp = app.post_json(
'/api/agendas/recurring-events/fillslots/?action=update&agendas=%s' % agenda_slugs,
params={
'slots': events_to_book,
'user_external_id': 'user',
'include_booked_events_detail': True,
'check_overlaps': True,
},
)
assert resp.json['booking_count'] == 180
assert len(ctx.captured_queries) == 14
father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe')
mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe')
child = Person.objects.create(user_external_id='xxx', first_name='James', last_name='Doe')
agenda = SharedCustodyAgenda.objects.create(
first_guardian=father, second_guardian=mother, child=child, date_start=now()
)
SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(7)), weeks='even')
SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=list(range(7)), weeks='odd')
with CaptureQueriesContext(connection) as ctx:
resp = app.post_json(
'/api/agendas/recurring-events/fillslots/?action=update&agendas=%s&guardian_external_id=father_id'
% agenda_slugs,
params={'slots': events_to_book, 'user_external_id': 'xxx'},
)
assert resp.json['booking_count'] == 100
assert len(ctx.captured_queries) == 14
@pytest.mark.freeze_time('2022-03-07 14:00') # Monday of 10th week
def test_recurring_events_api_fillslots_shared_custody(app, user, freezer):
agenda = Agenda.objects.create(label='Foo bar', kind='events', minimal_booking_delay=0)
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
event = Event.objects.create(
label='Event',
start_datetime=now(),
recurrence_days=list(range(7)),
places=2,
waiting_list_places=1,
agenda=agenda,
recurrence_end_date=now() + datetime.timedelta(days=14), # 2 weeks
)
event.create_all_recurrences()
father = Person.objects.create(user_external_id='father_id', first_name='John', last_name='Doe')
mother = Person.objects.create(user_external_id='mother_id', first_name='Jane', last_name='Doe')
child = Person.objects.create(user_external_id='child_id', first_name='James', last_name='Doe')
agenda = SharedCustodyAgenda.objects.create(
first_guardian=father, second_guardian=mother, child=child, date_start=now()
)
SharedCustodyRule.objects.create(agenda=agenda, guardian=father, weeks='odd', days=[0, 1, 2])
SharedCustodyRule.objects.create(agenda=agenda, guardian=father, weeks='even', days=[3, 4, 5])
SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, weeks='even', days=[0, 1, 2])
SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, weeks='odd', days=[3, 4, 5])
SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=[6])
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = (
'/api/agendas/recurring-events/fillslots/?agendas=foo-bar&action=update&guardian_external_id=%s'
)
params = {
'user_external_id': 'child_id',
'slots': ','.join('foo-bar@event:%s' % i for i in range(7)), # book every days
'include_booked_events_detail': True,
}
resp = app.post_json(fillslots_url % 'father_id', params=params)
assert resp.json['booking_count'] == 6
assert [x['date'] for x in resp.json['booked_events']] == [
'2022-03-10',
'2022-03-11',
'2022-03-12',
'2022-03-14',
'2022-03-15',
'2022-03-16',
]
resp = app.post_json(fillslots_url % 'mother_id', params=params)
assert resp.json['booking_count'] == 8
assert [x['date'] for x in resp.json['booked_events']] == [
'2022-03-07',
'2022-03-08',
'2022-03-09',
'2022-03-13',
'2022-03-17',
'2022-03-18',
'2022-03-19',
'2022-03-20',
]
# give father full custody from 14/03/2022
agenda2 = SharedCustodyAgenda.objects.create(
first_guardian=father,
second_guardian=mother,
child=child,
date_start=datetime.date(year=2022, month=3, day=14),
)
SharedCustodyRule.objects.create(agenda=agenda2, guardian=father, days=list(range(7)))
Booking.objects.all().delete()
resp = app.post_json(fillslots_url % 'father_id', params=params)
assert [x['date'] for x in resp.json['booked_events']] == [
'2022-03-10',
'2022-03-11',
'2022-03-12',
'2022-03-14',
'2022-03-15',
'2022-03-16',
'2022-03-17',
'2022-03-18',
'2022-03-19',
'2022-03-20',
]
resp = app.post_json(fillslots_url % 'mother_id', params=params)
assert [x['date'] for x in resp.json['booked_events']] == [
'2022-03-07',
'2022-03-08',
'2022-03-09',
'2022-03-13', # last date before new agenda rules apply
]
# check date_start/date_end params
Booking.objects.all().delete()
resp = app.post_json(
(fillslots_url + '&date_start=2022-03-11&date_end=2022-03-18') % 'father_id', params=params
)
assert [x['date'] for x in resp.json['booked_events']] == [
'2022-03-11',
'2022-03-12',
'2022-03-14',
'2022-03-15',
'2022-03-16',
'2022-03-17',
]
@pytest.mark.freeze_time('2021-09-06 12:00')
def test_recurring_events_api_fillslots_overlapping_events(app, user):
agenda = Agenda.objects.create(label='First Agenda', kind='events')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
start, end = now(), now() + datetime.timedelta(days=30)
Event.objects.create(
label='Event 12-14',
start_datetime=start,
duration=120,
places=2,
recurrence_end_date=end,
recurrence_days=[1],
agenda=agenda,
).create_all_recurrences()
Event.objects.create(
label='Event 14-15',
start_datetime=start + datetime.timedelta(hours=2),
duration=60,
places=2,
recurrence_end_date=end,
recurrence_days=[1],
agenda=agenda,
).create_all_recurrences()
Event.objects.create(
label='Event 15-17',
start_datetime=start + datetime.timedelta(hours=3),
duration=120,
places=2,
recurrence_end_date=end,
recurrence_days=[1, 3, 5],
agenda=agenda,
).create_all_recurrences()
agenda2 = Agenda.objects.create(label='Second Agenda', kind='events')
Desk.objects.create(agenda=agenda2, slug='_exceptions_holder')
Event.objects.create(
label='Event 12-18',
start_datetime=start,
duration=360,
places=2,
recurrence_end_date=end,
recurrence_days=[1, 5],
agenda=agenda2,
).create_all_recurrences()
Event.objects.create(
label='No duration',
start_datetime=start,
places=2,
recurrence_end_date=end,
recurrence_days=[5],
agenda=agenda2,
).create_all_recurrences()
app.authorization = ('Basic', ('john.doe', 'password'))
fillslots_url = '/api/agendas/recurring-events/fillslots/?action=%s&agendas=%s'
# booking without overlap
params = {
'user_external_id': 'user_id',
'check_overlaps': True,
'slots': 'first-agenda@event-12-14:1,first-agenda@event-14-15:1,second-agenda@event-12-18:5',
}
resp = app.post_json(fillslots_url % ('update', 'first-agenda,second-agenda'), params=params)
assert resp.json['booking_count'] == 14
# book again
resp = app.post_json(fillslots_url % ('update', 'first-agenda,second-agenda'), params=params)
assert resp.json['booking_count'] == 0
# change bookings
params = {'user_external_id': 'user_id', 'check_overlaps': True, 'slots': 'second-agenda@event-12-18:1'}
resp = app.post_json(fillslots_url % ('update', 'first-agenda,second-agenda'), params=params)
assert resp.json['booking_count'] == 5
assert resp.json['cancelled_booking_count'] == 14
# booking overlapping events is allowed if one has no duration
params = {
'user_external_id': 'user_id',
'check_overlaps': True,
'slots': 'second-agenda@event-12-18:5,second-agenda@no-duration:5',
}
resp = app.post_json(fillslots_url % ('update', 'first-agenda,second-agenda'), params=params)
assert resp.json['booking_count'] == 8
assert resp.json['cancelled_booking_count'] == 5
# booking overlapping events with durations is forbidden
params = {
'user_external_id': 'user_id',
'check_overlaps': True,
'slots': 'first-agenda@event-12-14:1,second-agenda@event-12-18:1',
}
resp = app.post_json(fillslots_url % ('update', 'first-agenda,second-agenda'), params=params)
assert resp.json['err'] == 1
assert (
resp.json['err_desc']
== 'Some events occur at the same time: first-agenda@event-12-14:1 / second-agenda@event-12-18:1'
)
params = {
'user_external_id': 'user_id',
'check_overlaps': True,
'slots': (
'first-agenda@event-12-14:1,first-agenda@event-15-17:1,first-agenda@event-15-17:3,first-agenda@event-15-17:5,second-agenda@event-12-18:1,'
'second-agenda@event-12-18:5,second-agenda@no-duration:5'
),
}
resp = app.post_json(fillslots_url % ('update', 'first-agenda,second-agenda'), params=params)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == (
'Some events occur at the same time: first-agenda@event-12-14:1 / second-agenda@event-12-18:1, '
'first-agenda@event-15-17:1 / second-agenda@event-12-18:1, first-agenda@event-15-17:5 / second-agenda@event-12-18:5'
)
# overlaps check is disabled by default
params = {
'user_external_id': 'user_id',
'slots': 'first-agenda@event-12-14:1,second-agenda@event-12-18:1',
}
resp = app.post_json(fillslots_url % ('update', 'first-agenda,second-agenda'), params=params)
assert resp.json['err'] == 0
assert resp.json['booking_count'] == 10