api: allow partial booking in recurring events fillslots (#78056)

This commit is contained in:
Valentin Deniaud 2023-05-30 14:07:30 +02:00
parent 1b9bd2f428
commit 2a776007e0
3 changed files with 84 additions and 2 deletions

View File

@ -188,6 +188,18 @@ class RecurringFillslotsSerializer(MultipleAgendasEventsFillSlotsSerializer):
check_overlaps = CommaSeparatedStringField(
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):
super().validate_slots(value)
@ -401,6 +413,14 @@ class AgendaOrSubscribedSlugsMixin(AgendaSlugsMixin):
attrs['agenda_slugs'] = [agenda.slug for agenda in agendas]
else:
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
def validate_agendas(self, value):

View File

@ -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', ''),
extra_emails=payload.get('extra_emails', []),
extra_phone_numbers=payload.get('extra_phone_numbers', []),
start_time=payload.get('start_time'),
end_time=payload.get('end_time'),
extra_data=extra_data,
color=color,
)

View File

@ -16,7 +16,7 @@ from chrono.agendas.models import (
SharedCustodyRule,
Subscription,
)
from chrono.utils.timezone import now
from chrono.utils.timezone import make_aware, now
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')
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')
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
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-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'