api: allow partial booking in recurring events fillslots (#78056)
This commit is contained in:
parent
1b9bd2f428
commit
2a776007e0
|
@ -188,6 +188,18 @@ class RecurringFillslotsSerializer(MultipleAgendasEventsFillSlotsSerializer):
|
||||||
check_overlaps = CommaSeparatedStringField(
|
check_overlaps = CommaSeparatedStringField(
|
||||||
required=False, child=serializers.SlugField(max_length=160, allow_blank=False)
|
required=False, child=serializers.SlugField(max_length=160, allow_blank=False)
|
||||||
)
|
)
|
||||||
|
start_time = serializers.TimeField(required=False)
|
||||||
|
end_time = serializers.TimeField(required=False)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
super().validate(attrs)
|
||||||
|
use_partial_bookings = any(agenda.partial_bookings for agenda in self.context['agendas'])
|
||||||
|
if use_partial_bookings:
|
||||||
|
if not attrs.get('start_time') or not attrs.get('end_time'):
|
||||||
|
raise ValidationError(_('must include start_time and end_time for partial bookings agenda'))
|
||||||
|
if attrs['start_time'] > attrs['end_time']:
|
||||||
|
raise ValidationError(_('start_time must be before end_time'))
|
||||||
|
return attrs
|
||||||
|
|
||||||
def validate_slots(self, value):
|
def validate_slots(self, value):
|
||||||
super().validate_slots(value)
|
super().validate_slots(value)
|
||||||
|
@ -401,6 +413,14 @@ class AgendaOrSubscribedSlugsMixin(AgendaSlugsMixin):
|
||||||
attrs['agenda_slugs'] = [agenda.slug for agenda in agendas]
|
attrs['agenda_slugs'] = [agenda.slug for agenda in agendas]
|
||||||
else:
|
else:
|
||||||
attrs['agenda_slugs'] = self.agenda_slugs
|
attrs['agenda_slugs'] = self.agenda_slugs
|
||||||
|
|
||||||
|
if any(
|
||||||
|
agenda.partial_bookings != attrs['agendas'][0].partial_bookings for agenda in attrs['agendas']
|
||||||
|
):
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
{'agendas': _('Cannot mix partial bookings agendas with other kinds.')}
|
||||||
|
)
|
||||||
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def validate_agendas(self, value):
|
def validate_agendas(self, value):
|
||||||
|
|
|
@ -461,6 +461,8 @@ def make_booking(event, payload, extra_data, primary_booking=None, in_waiting_li
|
||||||
user_display_label=payload.get('user_display_label', ''),
|
user_display_label=payload.get('user_display_label', ''),
|
||||||
extra_emails=payload.get('extra_emails', []),
|
extra_emails=payload.get('extra_emails', []),
|
||||||
extra_phone_numbers=payload.get('extra_phone_numbers', []),
|
extra_phone_numbers=payload.get('extra_phone_numbers', []),
|
||||||
|
start_time=payload.get('start_time'),
|
||||||
|
end_time=payload.get('end_time'),
|
||||||
extra_data=extra_data,
|
extra_data=extra_data,
|
||||||
color=color,
|
color=color,
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,7 +16,7 @@ from chrono.agendas.models import (
|
||||||
SharedCustodyRule,
|
SharedCustodyRule,
|
||||||
Subscription,
|
Subscription,
|
||||||
)
|
)
|
||||||
from chrono.utils.timezone import now
|
from chrono.utils.timezone import make_aware, now
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
@ -1609,7 +1609,7 @@ def test_recurring_events_api_fillslots_overlapping_events(app, user):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.freeze_time('2021-09-06 12:00')
|
@pytest.mark.freeze_time('2021-09-06 12:00')
|
||||||
def test_recurring_events_api_fillslots_overlapping_events_partial_booking(app, user):
|
def test_recurring_events_api_fillslots_partly_overlapping_events(app, user):
|
||||||
agenda = Agenda.objects.create(label='First Agenda', kind='events')
|
agenda = Agenda.objects.create(label='First Agenda', kind='events')
|
||||||
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
||||||
start, end = now(), now() + datetime.timedelta(days=30)
|
start, end = now(), now() + datetime.timedelta(days=30)
|
||||||
|
@ -1677,3 +1677,63 @@ def test_recurring_events_api_fillslots_overlapping_events_partial_booking(app,
|
||||||
'2021-09-28',
|
'2021-09-28',
|
||||||
'2021-10-05',
|
'2021-10-05',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.freeze_time('2023-05-01 10:00')
|
||||||
|
def test_recurring_events_api_fillslots_partial_bookings(app, user):
|
||||||
|
agenda = Agenda.objects.create(label='Foo bar', kind='events', partial_bookings=True)
|
||||||
|
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
||||||
|
start_datetime = make_aware(datetime.datetime(2023, 5, 2, 8, 0))
|
||||||
|
event = Event.objects.create(
|
||||||
|
label='Event 08-18',
|
||||||
|
start_datetime=start_datetime,
|
||||||
|
end_time=datetime.time(18, 00),
|
||||||
|
places=2,
|
||||||
|
recurrence_end_date=start_datetime + datetime.timedelta(days=30),
|
||||||
|
recurrence_days=[1],
|
||||||
|
agenda=agenda,
|
||||||
|
)
|
||||||
|
event.create_all_recurrences()
|
||||||
|
|
||||||
|
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||||
|
|
||||||
|
params = {
|
||||||
|
'user_external_id': 'user_id',
|
||||||
|
'slots': 'foo-bar@event-08-18:1',
|
||||||
|
'start_time': '10:00',
|
||||||
|
'end_time': '15:00',
|
||||||
|
}
|
||||||
|
fillslots_url = '/api/agendas/recurring-events/fillslots/?action=update&agendas=%s' % agenda.slug
|
||||||
|
resp = app.post_json(fillslots_url, params=params)
|
||||||
|
|
||||||
|
assert Booking.objects.count() == 5
|
||||||
|
for booking in Booking.objects.all():
|
||||||
|
assert booking.start_time == datetime.time(10, 00)
|
||||||
|
assert booking.end_time == datetime.time(15, 00)
|
||||||
|
|
||||||
|
# mix with other kind
|
||||||
|
other_agenda = Agenda.objects.create(label='Not partial', kind='events')
|
||||||
|
resp = app.post_json(fillslots_url + ',%s' % other_agenda.slug, params=params, status=400)
|
||||||
|
assert resp.json['errors']['agendas'][0] == 'Cannot mix partial bookings agendas with other kinds.'
|
||||||
|
|
||||||
|
# missing start_time
|
||||||
|
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'
|
||||||
|
)
|
||||||
|
|
||||||
|
# missing end_time
|
||||||
|
params['start_time'] = '10:00'
|
||||||
|
del params['end_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'
|
||||||
|
)
|
||||||
|
|
||||||
|
# end before start
|
||||||
|
params['end_time'] = '09:00'
|
||||||
|
resp = app.post_json(fillslots_url, params=params, status=400)
|
||||||
|
assert resp.json['errors']['non_field_errors'][0] == 'start_time must be before end_time'
|
||||||
|
|
Loading…
Reference in New Issue