1548 lines
60 KiB
Python
1548 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
|
|
agenda.date_end = datetime.date(year=2022, month=3, day=13)
|
|
agenda.save()
|
|
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
|