1189 lines
48 KiB
Python
1189 lines
48 KiB
Python
import datetime
|
|
import uuid
|
|
|
|
import pytest
|
|
from django.db import connection
|
|
from django.test.utils import CaptureQueriesContext
|
|
|
|
from chrono.agendas.models import (
|
|
Agenda,
|
|
Booking,
|
|
Category,
|
|
Desk,
|
|
Event,
|
|
EventsType,
|
|
Person,
|
|
SharedCustodyAgenda,
|
|
SharedCustodyRule,
|
|
Subscription,
|
|
)
|
|
from chrono.utils.timezone import localtime, make_aware, now
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_api_events_fillslots_preserve_past_bookings_multiple_agendas(app, user, freezer):
|
|
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
|
event = Event.objects.create(
|
|
label='Event', start_datetime=now() + datetime.timedelta(days=5), places=2, agenda=agenda
|
|
)
|
|
second_event = Event.objects.create(
|
|
label='Event 2', start_datetime=now() + datetime.timedelta(days=10), places=2, agenda=agenda
|
|
)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event,foo-bar@event-2'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
|
|
assert resp.json['booking_count'] == 2
|
|
assert resp.json['cancelled_booking_count'] == 0
|
|
|
|
# book only second event while first event is in the past
|
|
freezer.move_to('2021-09-12')
|
|
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event-2'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert resp.json['cancelled_booking_count'] == 0
|
|
assert event.booking_set.count() == 1
|
|
assert second_event.booking_set.count() == 1
|
|
|
|
# cancel all future bookings
|
|
params = {'user_external_id': 'user_id', 'slots': ''}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert resp.json['cancelled_booking_count'] == 1
|
|
assert event.booking_set.count() == 1
|
|
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_api_events_fillslots_preserve_out_of_delays_bookings_multiple_agendas(app, user, freezer):
|
|
agenda = Agenda.objects.create(
|
|
label='Foo bar', kind='events', minimal_booking_delay=2, maximal_booking_delay=10
|
|
)
|
|
event = Event.objects.create(
|
|
label='Event', start_datetime=now() + datetime.timedelta(days=5), places=2, agenda=agenda
|
|
)
|
|
second_event = Event.objects.create(
|
|
label='Event 2', start_datetime=now() + datetime.timedelta(days=9), places=2, agenda=agenda
|
|
)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event,foo-bar@event-2'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
|
|
assert resp.json['booking_count'] == 2
|
|
assert resp.json['cancelled_booking_count'] == 0
|
|
assert event.booking_set.get().out_of_min_delay is False
|
|
assert second_event.booking_set.get().out_of_min_delay is False
|
|
|
|
# book only second event while first event is out of delay with multiple agendas API
|
|
freezer.move_to('2021-09-10')
|
|
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event-2'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert resp.json['cancelled_booking_count'] == 0
|
|
assert event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
|
|
assert event.booking_set.get().out_of_min_delay is False
|
|
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
|
|
assert second_event.booking_set.get().out_of_min_delay is False
|
|
booking = event.booking_set.get()
|
|
# except if we want to bypass delays
|
|
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event-2', 'bypass_delays': True}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert resp.json['cancelled_booking_count'] == 1
|
|
assert event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
|
|
assert event.booking_set.get().out_of_min_delay is True
|
|
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
|
|
assert second_event.booking_set.get().out_of_min_delay is False
|
|
booking.save() # reset
|
|
|
|
# cancel all bookings in delays
|
|
params = {'user_external_id': 'user_id', 'slots': ''}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert resp.json['cancelled_booking_count'] == 1
|
|
assert event.booking_set.count() == 1
|
|
assert event.booking_set.get().out_of_min_delay is False
|
|
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
|
|
assert second_event.booking_set.get().out_of_min_delay is False
|
|
# bypass delays
|
|
params = {'user_external_id': 'user_id', 'slots': '', 'bypass_delays': True}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert resp.json['cancelled_booking_count'] == 1
|
|
assert event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
|
|
assert event.booking_set.get().out_of_min_delay is True
|
|
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
|
|
assert second_event.booking_set.get().out_of_min_delay is False
|
|
booking.save() # reset
|
|
|
|
# book only first event while second event is out of delay with multiple agendas API
|
|
freezer.move_to('2021-09-04')
|
|
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert resp.json['cancelled_booking_count'] == 0
|
|
assert event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
|
|
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
|
|
# bypass_delays has no effect on maximal_booking_delay
|
|
params = {'user_external_id': 'user_id', 'slots': 'foo-bar@event', 'bypass_delays': True}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=foo-bar', params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert resp.json['cancelled_booking_count'] == 0
|
|
assert event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
|
|
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_api_events_fillslots_multiple_agendas(app, user):
|
|
events_type = EventsType.objects.create(label='Foo')
|
|
first_agenda = Agenda.objects.create(label='First agenda', kind='events', events_type=events_type)
|
|
Desk.objects.create(agenda=first_agenda, slug='_exceptions_holder')
|
|
first_event = Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now() + datetime.timedelta(days=5),
|
|
places=2,
|
|
agenda=first_agenda,
|
|
)
|
|
second_agenda = Agenda.objects.create(label='Second agenda', kind='events')
|
|
Desk.objects.create(agenda=second_agenda, slug='_exceptions_holder')
|
|
second_event = Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now() + datetime.timedelta(days=6),
|
|
places=2,
|
|
agenda=second_agenda,
|
|
)
|
|
|
|
agenda_slugs = '%s,%s' % (first_agenda.slug, second_agenda.slug)
|
|
resp = app.get('/api/agendas/datetimes/', params={'agendas': agenda_slugs})
|
|
event_slugs = ','.join((resp.json['data'][0]['id'], resp.json['data'][1]['id']))
|
|
assert event_slugs == 'first-agenda@event,second-agenda@event'
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
params = {'user_external_id': 'user_id', 'check_overlaps': True, 'slots': event_slugs}
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params)
|
|
assert len(ctx.captured_queries) == 18
|
|
assert resp.json['booking_count'] == 2
|
|
assert len(resp.json['booked_events']) == 2
|
|
assert resp.json['booked_events'][0]['id'] == 'first-agenda@event'
|
|
assert (
|
|
resp.json['booked_events'][0]['booking']['id']
|
|
== Booking.objects.filter(event__agenda=first_agenda, event=first_event).latest('pk').pk
|
|
)
|
|
assert resp.json['booked_events'][1]['id'] == 'second-agenda@event'
|
|
assert (
|
|
resp.json['booked_events'][1]['booking']['id']
|
|
== Booking.objects.filter(event__agenda=second_agenda, event=second_event).latest('pk').pk
|
|
)
|
|
assert first_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
|
|
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
|
|
assert resp.json['bookings_ics_url'] == 'http://testserver/api/bookings/ics/?user_external_id=user_id'
|
|
request_uuid = first_event.booking_set.get().request_uuid
|
|
assert request_uuid is not None
|
|
assert second_event.booking_set.get().request_uuid == request_uuid
|
|
assert first_event.booking_set.get().previous_state == 'unbooked'
|
|
assert second_event.booking_set.get().previous_state == 'unbooked'
|
|
assert (
|
|
resp.json['revert_url'] == 'http://testserver/api/agendas/events/fillslots/%s/revert/' % request_uuid
|
|
)
|
|
|
|
# booking modification
|
|
params = {'user_external_id': 'user_id', 'slots': 'first-agenda@event'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert len(resp.json['booked_events']) == 0
|
|
assert resp.json['cancelled_booking_count'] == 1
|
|
assert first_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
|
|
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 0
|
|
request_uuid = second_event.booking_set.get().request_uuid
|
|
assert request_uuid is not None
|
|
assert second_event.booking_set.get().previous_state == 'booked'
|
|
assert (
|
|
resp.json['revert_url'] == 'http://testserver/api/agendas/events/fillslots/%s/revert/' % request_uuid
|
|
)
|
|
|
|
params = {'user_external_id': 'user_id_2', 'slots': event_slugs}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params)
|
|
assert resp.json['booking_count'] == 2
|
|
assert len(resp.json['booked_events']) == 2
|
|
assert resp.json['booked_events'][0]['id'] == 'first-agenda@event'
|
|
assert (
|
|
resp.json['booked_events'][0]['booking']['id']
|
|
== Booking.objects.filter(event__agenda=first_agenda, event=first_event).latest('pk').pk
|
|
)
|
|
assert resp.json['booked_events'][1]['id'] == 'second-agenda@event'
|
|
assert (
|
|
resp.json['booked_events'][1]['booking']['id']
|
|
== Booking.objects.filter(event__agenda=second_agenda, event=second_event).latest('pk').pk
|
|
)
|
|
assert first_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 2
|
|
assert second_event.booking_set.filter(cancellation_datetime__isnull=True).count() == 1
|
|
|
|
params = {'user_external_id': 'user_id_3', 'slots': event_slugs}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'some events are full: Event'
|
|
|
|
# invalid agenda slugs in querystring
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?agendas=first-agenda,xxx,yyy', params=params, status=400
|
|
)
|
|
assert resp.json['errors']['agendas'][0] == 'invalid slugs: xxx, yyy'
|
|
|
|
# invalid agenda slugs in payload
|
|
params = {'user_external_id': 'user_id_3', 'slots': 'first-agenda@event,xxx@event,yyy@event'}
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params, status=400
|
|
)
|
|
assert resp.json['errors']['slots'] == ['Events from the following agendas cannot be booked: xxx, yyy']
|
|
|
|
# missing agendas parameter
|
|
resp = app.post_json('/api/agendas/events/fillslots/', params=params, status=400)
|
|
assert resp.json['errors']['non_field_errors'] == [
|
|
'Either "agendas" or "subscribed" parameter is required.'
|
|
]
|
|
|
|
# valid agendas parameter and event slugs, but mismatch between the two
|
|
params = {'user_external_id': 'user_id_3', 'slots': event_slugs}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=first-agenda', params=params, status=400)
|
|
assert resp.json['errors']['slots'] == [
|
|
'Events from the following agendas cannot be booked: second-agenda'
|
|
]
|
|
|
|
# missing @ in slot
|
|
params['slots'] = 'first-agenda'
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=first-agenda', params=params, status=400)
|
|
assert resp.json['errors']['slots'] == ['Invalid format for slot first-agenda']
|
|
|
|
# empty event slug
|
|
params['slots'] = 'first-agenda@'
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=first-agenda', params=params, status=400)
|
|
assert resp.json['errors']['slots'] == ['Missing event slug in slot first-agenda@']
|
|
|
|
# empty agenda slug
|
|
params['slots'] = '@event'
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=first-agenda', params=params, status=400)
|
|
assert resp.json['errors']['slots'] == ['Missing agenda slug in slot @event']
|
|
|
|
params = {'user_external_id': 'user_id', 'slots': event_slugs, 'foo': 'bar'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params)
|
|
assert resp.json['booking_count'] == 1
|
|
assert Booking.objects.order_by('-pk')[0].extra_data == {'foo': 'bar'}
|
|
|
|
params.update({'foo': ['bar', 'baz']})
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params, status=400
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'wrong type for extra_data foo value'
|
|
|
|
params.update({'foo': {'bar': 'baz'}})
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?agendas=%s' % agenda_slugs, params=params, status=400
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'wrong type for extra_data foo value'
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_api_events_fillslots_multiple_agendas_with_cancelled(app, user):
|
|
agenda_1 = Agenda.objects.create(label='Agenda 1', kind='events')
|
|
Desk.objects.create(agenda=agenda_1, slug='_exceptions_holder')
|
|
event_1 = Event.objects.create(
|
|
label='Event 1',
|
|
start_datetime=now() + datetime.timedelta(days=1),
|
|
places=2,
|
|
agenda=agenda_1,
|
|
)
|
|
agenda_2 = Agenda.objects.create(label='Agenda 2', kind='events')
|
|
Desk.objects.create(agenda=agenda_2, slug='_exceptions_holder')
|
|
event_2 = Event.objects.create(
|
|
label='Event 2',
|
|
start_datetime=now() + datetime.timedelta(days=2),
|
|
places=2,
|
|
agenda=agenda_2,
|
|
)
|
|
event_3 = Event.objects.create(
|
|
label='Event 3',
|
|
start_datetime=now() + datetime.timedelta(days=3),
|
|
places=2,
|
|
agenda=agenda_2,
|
|
)
|
|
|
|
# create cancelled booking for the user
|
|
booking_1 = Booking.objects.create(event=event_1, user_external_id='user_id')
|
|
booking_1.cancel()
|
|
assert booking_1.cancellation_datetime is not None
|
|
# and non cancelled booking for the user
|
|
booking_2 = Booking.objects.create(event=event_2, user_external_id='user_id')
|
|
assert booking_2.cancellation_datetime is None
|
|
# secondary booking for this one
|
|
booking_2_secondary = Booking.objects.create(event=event_2, primary_booking=booking_2)
|
|
# and bookings for another user
|
|
Booking.objects.create(event=event_1, user_external_id='user_id_foobar')
|
|
other_booking = Booking.objects.create(event=event_2, user_external_id='user_id_foobar')
|
|
other_booking.cancel()
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
fillslots_url = '/api/agendas/events/fillslots/?agendas=%s,%s' % (agenda_1.slug, agenda_2.slug)
|
|
|
|
params = {'user_external_id': 'user_id', 'slots': 'agenda-1@event-1,agenda-2@event-2,agenda-2@event-3'}
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 2
|
|
assert len(resp.json['booked_events']) == 2
|
|
assert resp.json['cancelled_booking_count'] == 0
|
|
assert resp.json['booked_events'][0]['id'] == 'agenda-1@event-1'
|
|
assert (
|
|
resp.json['booked_events'][0]['booking']['id']
|
|
== Booking.objects.filter(event__agenda=agenda_1, event=event_1).latest('pk').pk
|
|
)
|
|
assert resp.json['booked_events'][1]['id'] == 'agenda-2@event-3'
|
|
assert (
|
|
resp.json['booked_events'][1]['booking']['id']
|
|
== Booking.objects.filter(event__agenda=agenda_2, event=event_3).latest('pk').pk
|
|
)
|
|
assert Booking.objects.filter(user_external_id='user_id').count() == 3
|
|
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 3
|
|
new_booking_1 = Booking.objects.get(event__agenda=agenda_1, event=event_1, user_external_id='user_id')
|
|
request_uuid = new_booking_1.request_uuid
|
|
assert request_uuid is not None
|
|
booking_3 = Booking.objects.get(event__agenda=agenda_2, event=event_3, user_external_id='user_id')
|
|
assert booking_3.request_uuid == request_uuid
|
|
assert new_booking_1.previous_state == 'cancelled'
|
|
assert booking_3.previous_state == 'unbooked'
|
|
assert (
|
|
resp.json['revert_url'] == 'http://testserver/api/agendas/events/fillslots/%s/revert/' % request_uuid
|
|
)
|
|
|
|
assert Booking.objects.filter(pk=booking_1.pk).exists() is False # cancelled booking deleted
|
|
booking_2.refresh_from_db()
|
|
booking_2_secondary.refresh_from_db()
|
|
assert booking_2.cancellation_datetime is None
|
|
assert booking_2_secondary.cancellation_datetime is None
|
|
|
|
params = {'user_external_id': 'user_id', 'slots': 'agenda-2@event-3'}
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert resp.json['cancelled_booking_count'] == 2
|
|
assert Booking.objects.filter(user_external_id='user_id').count() == 3
|
|
assert Booking.objects.filter(user_external_id='user_id', cancellation_datetime__isnull=True).count() == 1
|
|
|
|
assert Booking.objects.filter(pk=booking_1.pk).exists() is False # cancelled booking deleted
|
|
booking_2.refresh_from_db()
|
|
booking_2_secondary.refresh_from_db()
|
|
assert booking_2.cancellation_datetime is not None
|
|
assert booking_2_secondary.cancellation_datetime is not None
|
|
|
|
|
|
def test_api_events_fillslots_multiple_agendas_check_delays(app, user):
|
|
agenda = Agenda.objects.create(
|
|
label='Foo bar', kind='events', minimal_booking_delay=0, maximal_booking_delay=7
|
|
)
|
|
Event.objects.create(
|
|
slug='event-slug',
|
|
start_datetime=localtime() + datetime.timedelta(days=5),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
resp = app.post(
|
|
'/api/agendas/events/fillslots/?agendas=foo-bar',
|
|
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug'},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
booking = Booking.objects.latest('pk')
|
|
assert booking.out_of_min_delay is False
|
|
booking.delete()
|
|
|
|
# test minimal_booking_delay
|
|
agenda.minimal_booking_delay = 6
|
|
agenda.save()
|
|
resp = app.post(
|
|
'/api/agendas/events/fillslots/?agendas=foo-bar',
|
|
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug'},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'event not bookable'
|
|
agenda.save()
|
|
resp = app.post(
|
|
'/api/agendas/events/fillslots/?agendas=foo-bar',
|
|
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug', 'bypass_delays': True},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
booking = Booking.objects.latest('pk')
|
|
assert booking.out_of_min_delay is True
|
|
booking.delete()
|
|
|
|
# test maximal_booking_delay
|
|
agenda.minimal_booking_delay = 0
|
|
agenda.maximal_booking_delay = 3
|
|
agenda.save()
|
|
resp = app.post(
|
|
'/api/agendas/events/fillslots/?agendas=foo-bar',
|
|
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug'},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_class'] == 'event not bookable'
|
|
agenda.save()
|
|
resp = app.post(
|
|
'/api/agendas/events/fillslots/?agendas=foo-bar',
|
|
params={'user_external_id': 'user_id', 'slots': 'foo-bar@event-slug', 'bypass_delays': True},
|
|
)
|
|
assert resp.json['err'] == 0
|
|
booking = Booking.objects.latest('pk')
|
|
assert booking.out_of_min_delay is False
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_api_events_fillslots_multiple_agendas_subscribed(app, user):
|
|
category = Category.objects.create(label='Category A')
|
|
first_agenda = Agenda.objects.create(label='First agenda', kind='events', category=category)
|
|
second_agenda = Agenda.objects.create(label='Second agenda', kind='events', category=category)
|
|
category = Category.objects.create(label='Category B')
|
|
third_agenda = Agenda.objects.create(label='Third agenda', kind='events', category=category)
|
|
for agenda in Agenda.objects.all():
|
|
Event.objects.create(
|
|
slug='event',
|
|
start_datetime=now() + datetime.timedelta(days=10),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
Event.objects.create(
|
|
slug='event-2',
|
|
start_datetime=now() + datetime.timedelta(days=20),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
|
|
# add subscriptions to first and second agenda
|
|
for agenda in (first_agenda, second_agenda):
|
|
Subscription.objects.create(
|
|
agenda=agenda,
|
|
user_external_id='xxx',
|
|
date_start=now(),
|
|
date_end=now() + datetime.timedelta(days=10), # too soon
|
|
)
|
|
|
|
# book events
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
params = {'user_external_id': 'xxx', 'slots': 'first-agenda@event,second-agenda@event'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?subscribed=category-a', params=params, status=400)
|
|
# first event on the last subscription's day
|
|
Subscription.objects.all().update(date_end=now() + datetime.timedelta(days=11))
|
|
resp = app.post_json('/api/agendas/events/fillslots/?subscribed=category-a', params=params)
|
|
assert resp.json['booking_count'] == 2
|
|
assert (
|
|
Event.objects.get(agenda=first_agenda, slug='event')
|
|
.booking_set.filter(cancellation_datetime__isnull=True)
|
|
.count()
|
|
== 1
|
|
)
|
|
assert (
|
|
Event.objects.get(agenda=second_agenda, slug='event')
|
|
.booking_set.filter(cancellation_datetime__isnull=True)
|
|
.count()
|
|
== 1
|
|
)
|
|
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 2
|
|
|
|
# update bookings for category-a
|
|
params = {'user_external_id': 'xxx', 'slots': 'second-agenda@event'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?subscribed=category-a', params=params)
|
|
assert resp.json['booking_count'] == 0
|
|
assert resp.json['cancelled_booking_count'] == 1
|
|
assert (
|
|
Event.objects.get(agenda=first_agenda, slug='event')
|
|
.booking_set.filter(cancellation_datetime__isnull=True)
|
|
.count()
|
|
== 0
|
|
)
|
|
assert (
|
|
Event.objects.get(agenda=second_agenda, slug='event')
|
|
.booking_set.filter(cancellation_datetime__isnull=True)
|
|
.count()
|
|
== 1
|
|
)
|
|
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 1
|
|
|
|
# try to book event from agenda with no subscription TODO messages
|
|
params = {'user_external_id': 'xxx', 'slots': 'third-agenda@event'}
|
|
for slug in ('all', 'category-a', 'category-b'):
|
|
resp = app.post_json('/api/agendas/events/fillslots/?subscribed=%s' % slug, params=params, status=400)
|
|
assert (
|
|
resp.json['errors']['slots'][0]
|
|
== 'Events from the following agendas cannot be booked: third-agenda'
|
|
)
|
|
|
|
# add subscription to third agenda
|
|
subscription = Subscription.objects.create(
|
|
agenda=third_agenda,
|
|
user_external_id='xxx',
|
|
date_start=now(),
|
|
date_end=now() + datetime.timedelta(days=10),
|
|
)
|
|
params = {'user_external_id': 'xxx', 'slots': 'third-agenda@event'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?subscribed=category-b', params=params, status=400)
|
|
subscription.date_end = now() + datetime.timedelta(days=11)
|
|
subscription.save()
|
|
resp = app.post_json('/api/agendas/events/fillslots/?subscribed=category-b', params=params)
|
|
assert resp.json['booking_count'] == 1
|
|
assert (
|
|
Event.objects.get(agenda=first_agenda, slug='event')
|
|
.booking_set.filter(cancellation_datetime__isnull=True)
|
|
.count()
|
|
== 0
|
|
)
|
|
assert (
|
|
Event.objects.get(agenda=second_agenda, slug='event')
|
|
.booking_set.filter(cancellation_datetime__isnull=True)
|
|
.count()
|
|
== 1
|
|
)
|
|
assert (
|
|
Event.objects.get(agenda=third_agenda, slug='event')
|
|
.booking_set.filter(cancellation_datetime__isnull=True)
|
|
.count()
|
|
== 1
|
|
)
|
|
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 2
|
|
|
|
# add subscription to first agenda (disjoint) spanning event-2
|
|
for agenda in (first_agenda, second_agenda):
|
|
Subscription.objects.create(
|
|
agenda=agenda,
|
|
user_external_id='xxx',
|
|
date_start=now() + datetime.timedelta(days=15),
|
|
date_end=now() + datetime.timedelta(days=25),
|
|
)
|
|
# book event-2 while updating all bookings
|
|
params = {'user_external_id': 'xxx', 'slots': 'first-agenda@event,second-agenda@event-2'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?subscribed=all', params=params)
|
|
assert resp.json['booking_count'] == 2
|
|
assert resp.json['cancelled_booking_count'] == 2
|
|
assert (
|
|
Event.objects.get(agenda=first_agenda, slug='event')
|
|
.booking_set.filter(cancellation_datetime__isnull=True)
|
|
.count()
|
|
== 1
|
|
)
|
|
assert (
|
|
Event.objects.get(agenda=second_agenda, slug='event-2')
|
|
.booking_set.filter(cancellation_datetime__isnull=True)
|
|
.count()
|
|
== 1
|
|
)
|
|
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 2
|
|
|
|
# other user
|
|
for agenda in (first_agenda, second_agenda):
|
|
Subscription.objects.create(
|
|
agenda=agenda,
|
|
user_external_id='yyy',
|
|
date_start=now(),
|
|
date_end=now() + datetime.timedelta(days=25),
|
|
)
|
|
params = {'user_external_id': 'yyy', 'slots': 'first-agenda@event,second-agenda@event-2'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?subscribed=all', params=params)
|
|
assert resp.json['booking_count'] == 2
|
|
assert resp.json['cancelled_booking_count'] == 0
|
|
assert (
|
|
Event.objects.get(agenda=first_agenda, slug='event')
|
|
.booking_set.filter(cancellation_datetime__isnull=True)
|
|
.count()
|
|
== 2
|
|
)
|
|
assert (
|
|
Event.objects.get(agenda=second_agenda, slug='event-2')
|
|
.booking_set.filter(cancellation_datetime__isnull=True)
|
|
.count()
|
|
== 2
|
|
)
|
|
assert Booking.objects.filter(cancellation_datetime__isnull=True).count() == 4
|
|
|
|
# try to book event outside subscription date range
|
|
params = {'user_external_id': 'xxx', 'slots': 'third-agenda@event-2'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?subscribed=all', params=params, status=400)
|
|
assert resp.json['err_class'] == 'Some events are outside user subscriptions: third-agenda@event-2'
|
|
|
|
# mismatch between subscribed parameter and event
|
|
params = {'user_external_id': 'xxx', 'slots': 'third-agenda@event'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?subscribed=category-a', params=params, status=400)
|
|
assert (
|
|
resp.json['errors']['slots'][0] == 'Events from the following agendas cannot be booked: third-agenda'
|
|
)
|
|
|
|
# missing user_external_id
|
|
params = {'slots': 'third-agenda@event'}
|
|
resp = app.post_json('/api/agendas/events/fillslots/?subscribed=all', params=params, status=400)
|
|
assert 'required' in resp.json['errors']['user_external_id'][0]
|
|
|
|
|
|
@pytest.mark.freeze_time('2022-03-07 14:00') # Monday of 10th week
|
|
def test_api_events_fillslots_multiple_agendas_shared_custody(app, user):
|
|
agenda = Agenda.objects.create(label='First agenda', kind='events')
|
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
|
Event.objects.create(
|
|
slug='event-wednesday',
|
|
start_datetime=make_aware(datetime.datetime(year=2022, month=3, day=9, hour=14, minute=0)),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
Event.objects.create(
|
|
slug='event-thursday',
|
|
start_datetime=make_aware(datetime.datetime(year=2022, month=3, day=10, hour=14, minute=0)),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
Subscription.objects.create(
|
|
agenda=agenda,
|
|
user_external_id='child_id',
|
|
date_start=now(),
|
|
date_end=now() + datetime.timedelta(days=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='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, days=[1, 2, 3])
|
|
SharedCustodyRule.objects.create(agenda=agenda, guardian=mother, days=[4, 5, 6, 7])
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
params = {'user_external_id': 'child_id', 'slots': 'first-agenda@event-wednesday'}
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=father_id', params=params
|
|
)
|
|
assert resp.json['booking_count'] == 1
|
|
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=mother_id',
|
|
params=params,
|
|
status=400,
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'Some events are outside guardian custody: first-agenda@event-wednesday'
|
|
|
|
params['slots'] = 'first-agenda@event-thursday'
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=mother_id', params=params
|
|
)
|
|
assert resp.json['booking_count'] == 1
|
|
|
|
# unknown guardian
|
|
Booking.objects.all().delete()
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=unknown_id',
|
|
params=params,
|
|
status=400,
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'Some events are outside guardian custody: first-agenda@event-thursday'
|
|
|
|
# guardian_external_id parameter is ignored if there is no custody agenda for child
|
|
agenda.delete()
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=mother_id',
|
|
params=params,
|
|
)
|
|
assert resp.json['booking_count'] == 1
|
|
|
|
|
|
@pytest.mark.freeze_time('2022-03-07 14:00') # Monday of 10th week
|
|
def test_api_events_fillslots_multiple_agendas_shared_custody_date_start(app, user):
|
|
agenda = Agenda.objects.create(label='First agenda', kind='events')
|
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
|
Event.objects.create(
|
|
slug='event-wednesday',
|
|
start_datetime=make_aware(datetime.datetime(year=2022, month=3, day=9, hour=14, minute=0)),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
Event.objects.create(
|
|
slug='event-thursday',
|
|
start_datetime=make_aware(datetime.datetime(year=2022, month=3, day=10, hour=14, minute=0)),
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
Subscription.objects.create(
|
|
agenda=agenda,
|
|
user_external_id='child_id',
|
|
date_start=now(),
|
|
date_end=now() + datetime.timedelta(days=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='child_id', first_name='James', last_name='Doe')
|
|
|
|
agenda = SharedCustodyAgenda.objects.create(
|
|
first_guardian=father,
|
|
second_guardian=mother,
|
|
child=child,
|
|
date_start=now(),
|
|
date_end=datetime.date(year=2022, month=3, day=9),
|
|
)
|
|
SharedCustodyRule.objects.create(agenda=agenda, guardian=father, days=list(range(1, 8)))
|
|
|
|
agenda2 = SharedCustodyAgenda.objects.create(
|
|
first_guardian=father,
|
|
second_guardian=mother,
|
|
child=child,
|
|
date_start=datetime.date(year=2022, month=3, day=10),
|
|
)
|
|
SharedCustodyRule.objects.create(agenda=agenda2, guardian=mother, days=list(range(1, 8)))
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
params = {'user_external_id': 'child_id', 'slots': 'first-agenda@event-wednesday'}
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=father_id', params=params
|
|
)
|
|
assert resp.json['booking_count'] == 1
|
|
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=mother_id',
|
|
params=params,
|
|
status=400,
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'Some events are outside guardian custody: first-agenda@event-wednesday'
|
|
|
|
params['slots'] = 'first-agenda@event-thursday'
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=mother_id', params=params
|
|
)
|
|
assert resp.json['booking_count'] == 1
|
|
|
|
params['slots'] = 'first-agenda@event-thursday'
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=father_id',
|
|
params=params,
|
|
status=400,
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'Some events are outside guardian custody: first-agenda@event-thursday'
|
|
|
|
# check date_start/date_end params
|
|
Booking.objects.all().delete()
|
|
resp = app.post_json(
|
|
'/api/agendas/events/fillslots/?subscribed=all&guardian_external_id=mother_id',
|
|
params={'date_start': '2022-03-09', 'date_end': '2022-03-20', **params},
|
|
)
|
|
assert resp.json['booking_count'] == 1
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
|
def test_api_events_fillslots_multiple_agendas_overlapping_events(app, user, freezer):
|
|
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
|
Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now() + datetime.timedelta(days=5),
|
|
duration=120,
|
|
places=5,
|
|
agenda=agenda,
|
|
)
|
|
second_agenda = Agenda.objects.create(label='Foo bar 2', kind='events')
|
|
Event.objects.create(
|
|
label='Event 2',
|
|
start_datetime=now() + datetime.timedelta(days=5, hours=1),
|
|
duration=120,
|
|
places=5,
|
|
agenda=second_agenda,
|
|
)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
fillslots_url = '/api/agendas/events/fillslots/?agendas=%s'
|
|
resp = app.post_json(
|
|
fillslots_url % ','.join((agenda.slug, second_agenda.slug)),
|
|
params={
|
|
'user_external_id': 'user_id',
|
|
'check_overlaps': True,
|
|
'slots': 'foo-bar@event,foo-bar-2@event-2',
|
|
},
|
|
)
|
|
assert resp.json['err'] == 1
|
|
assert resp.json['err_desc'] == 'Some events occur at the same time: Event, Event 2'
|
|
|
|
# events can be booked separately
|
|
resp = app.post_json(
|
|
fillslots_url % agenda.slug,
|
|
params={'user_external_id': 'user_id', 'check_overlaps': True, 'slots': 'foo-bar@event'},
|
|
)
|
|
assert resp.json['booking_count'] == 1
|
|
|
|
resp = app.post_json(
|
|
fillslots_url % second_agenda.slug,
|
|
params={'user_external_id': 'user_id', 'check_overlaps': True, 'slots': 'foo-bar-2@event-2'},
|
|
)
|
|
assert resp.json['booking_count'] == 1
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-02-23 14:00')
|
|
def test_api_events_fillslots_multiple_agendas_partial_bookings(app, user):
|
|
agenda = Agenda.objects.create(label='Foo bar', kind='events', partial_bookings=True)
|
|
Event.objects.create(
|
|
label='Event',
|
|
start_datetime=make_aware(datetime.datetime(2021, 3, 1, 8, 0)),
|
|
end_time=datetime.time(18, 00),
|
|
duration=120,
|
|
places=1,
|
|
agenda=agenda,
|
|
)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
fillslots_url = '/api/agendas/events/fillslots/?agendas=foo-bar'
|
|
params = {
|
|
'user_external_id': 'user_id',
|
|
'start_time': '10:00',
|
|
'end_time': '15:00',
|
|
'slots': 'foo-bar@event',
|
|
}
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
|
|
booking = Booking.objects.get()
|
|
assert booking.start_time == datetime.time(10, 00)
|
|
assert booking.end_time == datetime.time(15, 00)
|
|
|
|
del params['start_time']
|
|
resp = app.post_json(fillslots_url, params=params, status=400)
|
|
assert (
|
|
resp.json['errors']['non_field_errors'][0]
|
|
== 'must include start_time and end_time for partial bookings agenda'
|
|
)
|
|
|
|
# start before opening time
|
|
params['user_external_id'] = 'user_id_2'
|
|
params['start_time'] = '07:59'
|
|
params['end_time'] = '18:00'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['err_desc'] == 'booking start must be after opening time'
|
|
|
|
# end after closing time
|
|
params['start_time'] = '08:00'
|
|
params['end_time'] = '18:01'
|
|
resp = app.post_json(fillslots_url, params=params)
|
|
assert resp.json['err_desc'] == 'booking end must be before closing time'
|
|
|
|
|
|
@pytest.mark.freeze_time('2021-02-23 14:00')
|
|
def test_api_events_fillslots_multiple_agendas_revert(app, user):
|
|
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
|
event = Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now() + datetime.timedelta(days=5),
|
|
duration=120,
|
|
places=1,
|
|
agenda=agenda,
|
|
)
|
|
|
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
|
request_uuid = uuid.uuid4()
|
|
|
|
# no corresponding booking
|
|
revert_url = '/api/agendas/events/fillslots/%s/revert/' % request_uuid
|
|
resp = app.post(revert_url)
|
|
assert resp.json == {
|
|
'err': 0,
|
|
'cancelled_booking_count': 0,
|
|
'cancelled_events': [],
|
|
'deleted_booking_count': 0,
|
|
'deleted_events': [],
|
|
'booked_booking_count': 0,
|
|
'booked_events': [],
|
|
}
|
|
|
|
booking1 = Booking.objects.create(event=event, request_uuid=uuid.uuid4())
|
|
resp = app.post(revert_url)
|
|
assert resp.json == {
|
|
'err': 0,
|
|
'cancelled_booking_count': 0,
|
|
'cancelled_events': [],
|
|
'deleted_booking_count': 0,
|
|
'deleted_events': [],
|
|
'booked_booking_count': 0,
|
|
'booked_events': [],
|
|
}
|
|
booking1.refresh_from_db()
|
|
assert booking1.cancellation_datetime is None
|
|
|
|
booking2 = Booking.objects.create(event=event, request_uuid=request_uuid)
|
|
resp = app.post(revert_url)
|
|
assert resp.json == {
|
|
'err': 0,
|
|
'cancelled_booking_count': 0,
|
|
'cancelled_events': [],
|
|
'deleted_booking_count': 0,
|
|
'deleted_events': [],
|
|
'booked_booking_count': 0,
|
|
'booked_events': [],
|
|
}
|
|
booking1.refresh_from_db()
|
|
assert booking1.cancellation_datetime is None
|
|
booking2.refresh_from_db()
|
|
assert booking2.cancellation_datetime is None
|
|
|
|
# booking was previously cancelled
|
|
booking = Booking.objects.create(event=event, request_uuid=request_uuid, previous_state='cancelled')
|
|
resp = app.post(revert_url)
|
|
assert resp.json == {
|
|
'err': 0,
|
|
'cancelled_booking_count': 1,
|
|
'cancelled_events': [
|
|
{
|
|
'agenda_label': 'Foo bar',
|
|
'check_locked': False,
|
|
'checked': False,
|
|
'date': '2021-02-28',
|
|
'datetime': '2021-02-28 15:00:00',
|
|
'description': None,
|
|
'duration': 120,
|
|
'end_datetime': '2021-02-28 17:00:00',
|
|
'id': 'foo-bar@event',
|
|
'invoiced': False,
|
|
'label': 'Event',
|
|
'pricing': None,
|
|
'slug': 'event',
|
|
'text': 'Event',
|
|
'url': None,
|
|
'primary_event': None,
|
|
}
|
|
],
|
|
'deleted_booking_count': 0,
|
|
'deleted_events': [],
|
|
'booked_booking_count': 0,
|
|
'booked_events': [],
|
|
}
|
|
booking1.refresh_from_db()
|
|
assert booking1.cancellation_datetime is None
|
|
booking2.refresh_from_db()
|
|
assert booking2.cancellation_datetime is None
|
|
booking.refresh_from_db()
|
|
assert booking.cancellation_datetime is not None
|
|
|
|
# again, but with a cancelled booking
|
|
resp = app.post(revert_url)
|
|
assert resp.json == {
|
|
'err': 0,
|
|
'cancelled_booking_count': 1,
|
|
'cancelled_events': [
|
|
{
|
|
'agenda_label': 'Foo bar',
|
|
'check_locked': False,
|
|
'checked': False,
|
|
'date': '2021-02-28',
|
|
'datetime': '2021-02-28 15:00:00',
|
|
'description': None,
|
|
'duration': 120,
|
|
'end_datetime': '2021-02-28 17:00:00',
|
|
'id': 'foo-bar@event',
|
|
'invoiced': False,
|
|
'label': 'Event',
|
|
'pricing': None,
|
|
'slug': 'event',
|
|
'text': 'Event',
|
|
'url': None,
|
|
'primary_event': None,
|
|
}
|
|
],
|
|
'deleted_booking_count': 0,
|
|
'deleted_events': [],
|
|
'booked_booking_count': 0,
|
|
'booked_events': [],
|
|
}
|
|
booking1.refresh_from_db()
|
|
assert booking1.cancellation_datetime is None
|
|
booking2.refresh_from_db()
|
|
assert booking2.cancellation_datetime is None
|
|
booking.refresh_from_db()
|
|
assert booking.cancellation_datetime is not None
|
|
|
|
# booking was previously not cancelled
|
|
booking.previous_state = 'booked'
|
|
booking.save()
|
|
resp = app.post(revert_url)
|
|
assert resp.json == {
|
|
'err': 0,
|
|
'cancelled_booking_count': 0,
|
|
'cancelled_events': [],
|
|
'deleted_booking_count': 0,
|
|
'deleted_events': [],
|
|
'booked_booking_count': 1,
|
|
'booked_events': [
|
|
{
|
|
'agenda_label': 'Foo bar',
|
|
'check_locked': False,
|
|
'checked': False,
|
|
'date': '2021-02-28',
|
|
'datetime': '2021-02-28 15:00:00',
|
|
'description': None,
|
|
'duration': 120,
|
|
'end_datetime': '2021-02-28 17:00:00',
|
|
'id': 'foo-bar@event',
|
|
'invoiced': False,
|
|
'label': 'Event',
|
|
'pricing': None,
|
|
'slug': 'event',
|
|
'text': 'Event',
|
|
'url': None,
|
|
'primary_event': None,
|
|
}
|
|
],
|
|
}
|
|
booking1.refresh_from_db()
|
|
assert booking1.cancellation_datetime is None
|
|
booking2.refresh_from_db()
|
|
assert booking2.cancellation_datetime is None
|
|
booking.refresh_from_db()
|
|
assert booking.cancellation_datetime is None
|
|
|
|
# again, but with a not cancelled booking
|
|
resp = app.post(revert_url)
|
|
assert resp.json == {
|
|
'err': 0,
|
|
'cancelled_booking_count': 0,
|
|
'cancelled_events': [],
|
|
'deleted_booking_count': 0,
|
|
'deleted_events': [],
|
|
'booked_booking_count': 1,
|
|
'booked_events': [
|
|
{
|
|
'agenda_label': 'Foo bar',
|
|
'check_locked': False,
|
|
'checked': False,
|
|
'date': '2021-02-28',
|
|
'datetime': '2021-02-28 15:00:00',
|
|
'description': None,
|
|
'duration': 120,
|
|
'end_datetime': '2021-02-28 17:00:00',
|
|
'id': 'foo-bar@event',
|
|
'invoiced': False,
|
|
'label': 'Event',
|
|
'pricing': None,
|
|
'slug': 'event',
|
|
'text': 'Event',
|
|
'url': None,
|
|
'primary_event': None,
|
|
}
|
|
],
|
|
}
|
|
booking1.refresh_from_db()
|
|
assert booking1.cancellation_datetime is None
|
|
booking2.refresh_from_db()
|
|
assert booking2.cancellation_datetime is None
|
|
booking.refresh_from_db()
|
|
assert booking.cancellation_datetime is None
|
|
|
|
# booking was previously unbooked
|
|
booking.previous_state = 'unbooked'
|
|
booking.save()
|
|
resp = app.post(revert_url)
|
|
assert resp.json == {
|
|
'err': 0,
|
|
'cancelled_booking_count': 0,
|
|
'cancelled_events': [],
|
|
'deleted_booking_count': 1,
|
|
'deleted_events': [
|
|
{
|
|
'agenda_label': 'Foo bar',
|
|
'check_locked': False,
|
|
'checked': False,
|
|
'date': '2021-02-28',
|
|
'datetime': '2021-02-28 15:00:00',
|
|
'description': None,
|
|
'duration': 120,
|
|
'end_datetime': '2021-02-28 17:00:00',
|
|
'id': 'foo-bar@event',
|
|
'invoiced': False,
|
|
'label': 'Event',
|
|
'pricing': None,
|
|
'slug': 'event',
|
|
'text': 'Event',
|
|
'url': None,
|
|
'primary_event': None,
|
|
}
|
|
],
|
|
'booked_booking_count': 0,
|
|
'booked_events': [],
|
|
}
|
|
booking1.refresh_from_db()
|
|
assert booking1.cancellation_datetime is None
|
|
booking2.refresh_from_db()
|
|
assert booking2.cancellation_datetime is None
|
|
assert Booking.objects.filter(pk=booking.pk).exists() is False
|
|
|
|
# again, but with a cancelled booking
|
|
booking = Booking.objects.create(
|
|
event=event, request_uuid=request_uuid, previous_state='unbooked', cancellation_datetime=now()
|
|
)
|
|
resp = app.post(revert_url)
|
|
assert resp.json == {
|
|
'err': 0,
|
|
'cancelled_booking_count': 0,
|
|
'cancelled_events': [],
|
|
'deleted_booking_count': 1,
|
|
'deleted_events': [
|
|
{
|
|
'agenda_label': 'Foo bar',
|
|
'check_locked': False,
|
|
'checked': False,
|
|
'date': '2021-02-28',
|
|
'datetime': '2021-02-28 15:00:00',
|
|
'description': None,
|
|
'duration': 120,
|
|
'end_datetime': '2021-02-28 17:00:00',
|
|
'id': 'foo-bar@event',
|
|
'invoiced': False,
|
|
'label': 'Event',
|
|
'pricing': None,
|
|
'slug': 'event',
|
|
'text': 'Event',
|
|
'url': None,
|
|
'primary_event': None,
|
|
}
|
|
],
|
|
'booked_booking_count': 0,
|
|
'booked_events': [],
|
|
}
|
|
booking1.refresh_from_db()
|
|
assert booking1.cancellation_datetime is None
|
|
booking2.refresh_from_db()
|
|
assert booking2.cancellation_datetime is None
|
|
assert Booking.objects.filter(pk=booking.pk).exists() is False
|
|
|
|
# check num queries
|
|
Booking.objects.create(
|
|
event=event, request_uuid=request_uuid, previous_state='cancelled', cancellation_datetime=now()
|
|
)
|
|
event = Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now() + datetime.timedelta(days=5),
|
|
duration=120,
|
|
places=1,
|
|
agenda=agenda,
|
|
)
|
|
Booking.objects.create(
|
|
event=event, request_uuid=request_uuid, previous_state='booked', cancellation_datetime=now()
|
|
)
|
|
event = Event.objects.create(
|
|
label='Event',
|
|
start_datetime=now() + datetime.timedelta(days=5),
|
|
duration=120,
|
|
places=1,
|
|
agenda=agenda,
|
|
recurrence_days=[7],
|
|
recurrence_end_date=now() + datetime.timedelta(days=14), # 2 weeks
|
|
)
|
|
event.create_all_recurrences()
|
|
event = event.recurrences.first()
|
|
Booking.objects.create(
|
|
event=event, request_uuid=request_uuid, previous_state='unbooked', cancellation_datetime=now()
|
|
)
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
resp = app.post(revert_url)
|
|
assert len(ctx.captured_queries) == 15
|