Compare commits
5 Commits
f7c87dd0c9
...
5108f50129
Author | SHA1 | Date |
---|---|---|
Valentin Deniaud | 5108f50129 | |
Thomas NOËL | 6a411b1859 | |
Lauréline Guérin | 4291cc73db | |
Lauréline Guérin | e4864ea95b | |
Lauréline Guérin | 9d1c33970c |
|
@ -1621,23 +1621,22 @@ class Agenda(models.Model):
|
|||
self.refresh_booking_computed_times()
|
||||
|
||||
def refresh_booking_computed_times(self):
|
||||
bookings_queryset = Booking.objects.filter(
|
||||
event__agenda__kind='events',
|
||||
event__agenda__partial_bookings=True,
|
||||
event__agenda=self,
|
||||
event__check_locked=False,
|
||||
event__invoiced=False,
|
||||
event__cancelled=False,
|
||||
cancellation_datetime__isnull=True,
|
||||
)
|
||||
booking_checks = BookingCheck.objects.filter(booking__in=bookings_queryset).select_related(
|
||||
'booking', 'booking__event__agenda'
|
||||
bookings_queryset = (
|
||||
Booking.objects.filter(
|
||||
event__agenda__kind='events',
|
||||
event__agenda__partial_bookings=True,
|
||||
event__agenda=self,
|
||||
event__check_locked=False,
|
||||
event__invoiced=False,
|
||||
event__cancelled=False,
|
||||
cancellation_datetime__isnull=True,
|
||||
)
|
||||
.prefetch_related('user_checks')
|
||||
.select_related('event__agenda')
|
||||
)
|
||||
to_update = []
|
||||
for booking_check in booking_checks:
|
||||
changed = booking_check.refresh_computed_times()
|
||||
if changed:
|
||||
to_update.append(booking_check)
|
||||
for booking in bookings_queryset:
|
||||
to_update += booking.refresh_computed_times()
|
||||
if to_update:
|
||||
BookingCheck.objects.bulk_update(to_update, ['computed_start_time', 'computed_end_time'])
|
||||
|
||||
|
@ -2249,14 +2248,11 @@ class Event(models.Model):
|
|||
event__invoiced=False,
|
||||
event__cancelled=False,
|
||||
cancellation_datetime__isnull=True,
|
||||
)
|
||||
booking_checks = BookingCheck.objects.filter(booking__in=bookings_queryset).select_related('booking')
|
||||
).prefetch_related('user_checks')
|
||||
to_update = []
|
||||
for booking_check in booking_checks:
|
||||
booking_check.booking.event = self # to avoid lots of querysets
|
||||
changed = booking_check.refresh_computed_times()
|
||||
if changed:
|
||||
to_update.append(booking_check)
|
||||
for booking in bookings_queryset:
|
||||
booking.event = self # to avoid lots of querysets
|
||||
to_update += booking.refresh_computed_times()
|
||||
if to_update:
|
||||
BookingCheck.objects.bulk_update(to_update, ['computed_start_time', 'computed_end_time'])
|
||||
|
||||
|
@ -2949,6 +2945,49 @@ class Booking(models.Model):
|
|||
self.save()
|
||||
self.event.set_is_checked()
|
||||
|
||||
def refresh_computed_times(self, commit=False):
|
||||
to_update = []
|
||||
user_checks = sorted(self.user_checks.all(), key=lambda a: a.start_time or datetime.time(0))
|
||||
many_user_checks = len(user_checks) > 1
|
||||
assert len(user_checks) < 3 # 2 user_checks max !
|
||||
|
||||
if user_checks and not many_user_checks:
|
||||
# only one user_check
|
||||
user_check = user_checks[0]
|
||||
changed = user_check._refresh_computed_times()
|
||||
if changed:
|
||||
to_update.append(user_check)
|
||||
|
||||
elif many_user_checks:
|
||||
# two user_checks
|
||||
user_check1 = user_checks[0]
|
||||
user_check2 = user_checks[1]
|
||||
compute_one_first = True
|
||||
if user_check1.presence != user_check2.presence:
|
||||
# one user check is presence, the other is absence
|
||||
if user_check1.presence is False:
|
||||
# the second check is presence, it should be computed first
|
||||
compute_one_first = False
|
||||
|
||||
if compute_one_first:
|
||||
changed = user_check1._refresh_computed_times(adjust_end_to_booking=False)
|
||||
if changed:
|
||||
to_update.append(user_check1)
|
||||
changed = user_check2._refresh_computed_times(other_user_check=user_check1)
|
||||
if changed:
|
||||
to_update.append(user_check2)
|
||||
else:
|
||||
changed = user_check2._refresh_computed_times(adjust_start_to_booking=False)
|
||||
if changed:
|
||||
to_update.append(user_check2)
|
||||
changed = user_check1._refresh_computed_times(other_user_check=user_check2)
|
||||
if changed:
|
||||
to_update.append(user_check1)
|
||||
|
||||
if commit and to_update:
|
||||
BookingCheck.objects.bulk_update(to_update, ['computed_start_time', 'computed_end_time'])
|
||||
return to_update
|
||||
|
||||
def get_user_block(self):
|
||||
template_vars = Context(settings.TEMPLATE_VARS, autoescape=False)
|
||||
template_vars.update(
|
||||
|
@ -3067,13 +3106,19 @@ class BookingCheck(models.Model):
|
|||
next_slot = datetime.time(*divmod(previous_slot_minutes + minutes, 60))
|
||||
return previous_slot, next_slot
|
||||
|
||||
def get_computed_start_time(self):
|
||||
def get_computed_start_time(self, other_user_check=None, adjust_to_booking=True):
|
||||
if self.start_time is None:
|
||||
return None
|
||||
|
||||
start_time = self.start_time
|
||||
if self.booking.start_time:
|
||||
if self.booking.start_time and adjust_to_booking:
|
||||
# adjust start_time to the start of the booking if requested
|
||||
start_time = min(self.start_time, self.booking.start_time)
|
||||
if other_user_check and other_user_check.computed_start_time and other_user_check.computed_end_time:
|
||||
# if other user_check exists and is completely computed
|
||||
if other_user_check.start_time < self.start_time:
|
||||
# if other user_check is the first of the day, start_time is the end of other user_check
|
||||
start_time = other_user_check.computed_end_time
|
||||
if self.booking.event.agenda.invoicing_unit == 'minute':
|
||||
return start_time
|
||||
|
||||
|
@ -3089,13 +3134,19 @@ class BookingCheck(models.Model):
|
|||
# else take previous_slot
|
||||
return previous_slot
|
||||
|
||||
def get_computed_end_time(self):
|
||||
def get_computed_end_time(self, other_user_check=None, adjust_to_booking=True):
|
||||
if self.end_time is None:
|
||||
return None
|
||||
|
||||
end_time = self.end_time
|
||||
if self.booking.end_time:
|
||||
if self.booking.end_time and adjust_to_booking:
|
||||
# adjust end_time to the end of the booking if requested
|
||||
end_time = max(self.end_time, self.booking.end_time)
|
||||
if other_user_check and other_user_check.computed_start_time and other_user_check.computed_end_time:
|
||||
# if other user_check exists and is completely computed
|
||||
if other_user_check.start_time > self.start_time:
|
||||
# if other user_check is the second of the day, end_time is the start of other user_check
|
||||
end_time = other_user_check.computed_start_time
|
||||
if self.booking.event.agenda.invoicing_unit == 'minute':
|
||||
return end_time
|
||||
|
||||
|
@ -3111,11 +3162,17 @@ class BookingCheck(models.Model):
|
|||
# else take next_slot
|
||||
return next_slot
|
||||
|
||||
def refresh_computed_times(self):
|
||||
def _refresh_computed_times(
|
||||
self, other_user_check=None, adjust_start_to_booking=True, adjust_end_to_booking=True
|
||||
):
|
||||
old_computed_start_time = self.computed_start_time
|
||||
old_computed_end_time = self.computed_end_time
|
||||
self.computed_start_time = self.get_computed_start_time()
|
||||
self.computed_end_time = self.get_computed_end_time()
|
||||
self.computed_start_time = self.get_computed_start_time(
|
||||
other_user_check=other_user_check, adjust_to_booking=adjust_start_to_booking
|
||||
)
|
||||
self.computed_end_time = self.get_computed_end_time(
|
||||
other_user_check=other_user_check, adjust_to_booking=adjust_end_to_booking
|
||||
)
|
||||
# return True if changed, else False
|
||||
if (
|
||||
old_computed_start_time == self.computed_start_time
|
||||
|
|
|
@ -323,6 +323,10 @@ class BookingSerializer(serializers.ModelSerializer):
|
|||
'cancellation_datetime',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user_check = kwargs.pop('user_check', None)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
if 'color' in data:
|
||||
# legacy
|
||||
|
@ -341,19 +345,17 @@ class BookingSerializer(serializers.ModelSerializer):
|
|||
ret.pop('user_absence_reason', None)
|
||||
ret.pop('user_presence_reason', None)
|
||||
else:
|
||||
user_was_present = self.instance.user_check.presence if self.instance.user_check else None
|
||||
user_was_present = self.user_check.presence if self.user_check else None
|
||||
ret['user_was_present'] = user_was_present
|
||||
ret['user_absence_reason'] = (
|
||||
self.instance.user_check.type_slug if user_was_present is False else None
|
||||
)
|
||||
ret['user_presence_reason'] = (
|
||||
self.instance.user_check.type_slug if user_was_present is True else None
|
||||
)
|
||||
ret['user_absence_reason'] = self.user_check.type_slug if user_was_present is False else None
|
||||
ret['user_presence_reason'] = self.user_check.type_slug if user_was_present is True else None
|
||||
if self.instance.event.agenda.kind == 'events' and self.instance.event.agenda.partial_bookings:
|
||||
self.instance.user_check_start_time = self.instance.user_check.start_time
|
||||
self.instance.user_check_end_time = self.instance.user_check.end_time
|
||||
self.instance.computed_start_time = self.instance.user_check.computed_start_time
|
||||
self.instance.computed_end_time = self.instance.user_check.computed_end_time
|
||||
self.instance.user_check_start_time = self.user_check.start_time if self.user_check else None
|
||||
self.instance.user_check_end_time = self.user_check.end_time if self.user_check else None
|
||||
self.instance.computed_start_time = (
|
||||
self.user_check.computed_start_time if self.user_check else None
|
||||
)
|
||||
self.instance.computed_end_time = self.user_check.computed_end_time if self.user_check else None
|
||||
for key in ['', 'user_check_', 'computed_']:
|
||||
start_key, end_key, minutes_key = (
|
||||
'%sstart_time' % key,
|
||||
|
|
|
@ -2204,7 +2204,7 @@ class MultipleAgendasEventsCheckStatus(APIView):
|
|||
booking_queryset = Booking.objects.filter(
|
||||
event__in=events,
|
||||
user_external_id=user_external_id,
|
||||
).prefetch_related('user_checks')
|
||||
).prefetch_related(Prefetch('user_checks', queryset=BookingCheck.objects.order_by('start_time')))
|
||||
bookings_by_event_id = collections.defaultdict(list)
|
||||
for booking in booking_queryset:
|
||||
bookings_by_event_id[booking.event_id].append(booking)
|
||||
|
@ -2212,33 +2212,61 @@ class MultipleAgendasEventsCheckStatus(APIView):
|
|||
data = []
|
||||
for event in events:
|
||||
event.agenda = agendas_by_id[event.agenda_id] # agenda is already fetched, reuse it
|
||||
|
||||
check_status = {}
|
||||
booking = None
|
||||
if not event.checked:
|
||||
check_status = {'status': 'error', 'error_reason': 'event-not-checked'}
|
||||
elif not bookings_by_event_id[event.pk]:
|
||||
check_status = {'status': 'not-booked'}
|
||||
elif len(bookings_by_event_id[event.pk]) > 1:
|
||||
check_status = {'status': 'error', 'error_reason': 'too-many-bookings-found'}
|
||||
else:
|
||||
booking = bookings_by_event_id[event.pk][0]
|
||||
booking.event = event # prevent db calls
|
||||
if booking.cancellation_datetime is not None:
|
||||
check_status = {'status': 'cancelled'}
|
||||
elif not booking.user_check:
|
||||
check_status = {'status': 'error', 'error_reason': 'booking-not-checked'}
|
||||
else:
|
||||
check_status = {
|
||||
'status': 'presence' if booking.user_check.presence else 'absence',
|
||||
'check_type': booking.user_check.type_slug,
|
||||
|
||||
if check_status:
|
||||
data.append(
|
||||
{
|
||||
'event': serializers.EventSerializer(event).data,
|
||||
'check_status': check_status,
|
||||
'booking': {},
|
||||
}
|
||||
data.append(
|
||||
{
|
||||
'event': serializers.EventSerializer(event).data,
|
||||
'check_status': check_status,
|
||||
'booking': serializers.BookingSerializer(booking).data if booking else {},
|
||||
)
|
||||
continue
|
||||
|
||||
booking = bookings_by_event_id[event.pk][0]
|
||||
booking.event = event # prevent db calls
|
||||
if booking.cancellation_datetime is not None:
|
||||
check_status = {'status': 'cancelled'}
|
||||
data.append(
|
||||
{
|
||||
'event': serializers.EventSerializer(event).data,
|
||||
'check_status': check_status,
|
||||
'booking': serializers.BookingSerializer(booking).data,
|
||||
}
|
||||
)
|
||||
continue
|
||||
user_check = None
|
||||
for user_check in booking.user_checks.all():
|
||||
# user_checks are prefetched and ordered
|
||||
check_status = {
|
||||
'status': 'presence' if user_check.presence else 'absence',
|
||||
'check_type': user_check.type_slug,
|
||||
}
|
||||
)
|
||||
data.append(
|
||||
{
|
||||
'event': serializers.EventSerializer(event).data,
|
||||
'check_status': check_status,
|
||||
'booking': serializers.BookingSerializer(booking, user_check=user_check).data,
|
||||
}
|
||||
)
|
||||
if user_check is None:
|
||||
# no user_check object
|
||||
check_status = {'status': 'error', 'error_reason': 'booking-not-checked'}
|
||||
data.append(
|
||||
{
|
||||
'event': serializers.EventSerializer(event).data,
|
||||
'check_status': check_status,
|
||||
'booking': serializers.BookingSerializer(booking).data,
|
||||
}
|
||||
)
|
||||
|
||||
return Response({'err': 0, 'data': data})
|
||||
|
||||
|
@ -2564,7 +2592,12 @@ class BookingsAPI(ListAPIView):
|
|||
|
||||
data = []
|
||||
for booking in bookings:
|
||||
serialized_booking = self.serializer_class(booking).data
|
||||
user_checks = booking.user_checks.all()
|
||||
user_check = None
|
||||
if len(user_checks) == 1:
|
||||
# XXX which behavior for partial bookings, and multi-check ?
|
||||
user_check = user_checks[0]
|
||||
serialized_booking = self.serializer_class(booking, user_check=user_check).data
|
||||
if booking.event.agenda.kind == 'events':
|
||||
serialized_booking['event'] = get_event_detail(request, booking.event)
|
||||
data.append(serialized_booking)
|
||||
|
@ -2627,7 +2660,12 @@ class BookingAPI(APIView):
|
|||
def get(self, request, *args, **kwargs):
|
||||
self.check_booking()
|
||||
|
||||
serializer = self.serializer_class(self.booking)
|
||||
user_checks = self.booking.user_checks.all()
|
||||
user_check = None
|
||||
if len(user_checks) == 1:
|
||||
# XXX which behavior for partial bookings, and multi-check ?
|
||||
user_check = user_checks[0]
|
||||
serializer = self.serializer_class(self.booking, user_check=user_check)
|
||||
response = serializer.data
|
||||
response.update(
|
||||
{
|
||||
|
|
|
@ -661,13 +661,15 @@ class PartialBookingCheckForm(forms.ModelForm):
|
|||
)
|
||||
|
||||
def save(self):
|
||||
booking = self.instance.booking
|
||||
if self.cleaned_data['presence'] is None:
|
||||
self.instance.delete()
|
||||
booking.refresh_computed_times(commit=True)
|
||||
return self.instance
|
||||
|
||||
self.instance.refresh_computed_times()
|
||||
|
||||
return super().save()
|
||||
super().save()
|
||||
booking.refresh_computed_times(commit=True)
|
||||
return self.instance
|
||||
|
||||
|
||||
class EventsTimesheetForm(forms.Form):
|
||||
|
|
|
@ -61,6 +61,7 @@ buffer-size = 32768
|
|||
|
||||
py-tracebacker = /run/chrono/py-tracebacker.sock.
|
||||
stats = /run/chrono/stats.sock
|
||||
memory-report = true
|
||||
|
||||
ignore-sigpipe = true
|
||||
|
||||
|
|
|
@ -153,6 +153,7 @@ def test_bookings_api(app, user):
|
|||
label='Event A', agenda=events_agenda, start_datetime=now() + datetime.timedelta(days=3), places=10
|
||||
)
|
||||
events_booking1 = Booking.objects.create(event=events_event, user_external_id='enfant-1234')
|
||||
events_booking1.mark_user_presence()
|
||||
events_booking2 = Booking.objects.create(event=events_event, user_external_id='enfant-1234')
|
||||
Booking.objects.create(event=events_event, user_external_id='foobar')
|
||||
Booking.objects.create(event=events_event)
|
||||
|
@ -214,7 +215,7 @@ def test_bookings_api(app, user):
|
|||
'user_last_name': '',
|
||||
'user_email': '',
|
||||
'user_phone_number': '',
|
||||
'user_was_present': None,
|
||||
'user_was_present': True,
|
||||
'user_absence_reason': None,
|
||||
'user_presence_reason': None,
|
||||
'use_color_for': None,
|
||||
|
|
|
@ -5,7 +5,7 @@ import pytest
|
|||
from django.db import connection
|
||||
from django.test.utils import CaptureQueriesContext
|
||||
|
||||
from chrono.agendas.models import Agenda, Booking, Event, EventsType, Subscription
|
||||
from chrono.agendas.models import Agenda, Booking, BookingCheck, Event, EventsType, Subscription
|
||||
from chrono.utils.timezone import localtime, make_aware, now
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
@ -1398,8 +1398,8 @@ def test_events_check_status_events(app, user, partial_bookings):
|
|||
booking1.mark_user_presence(
|
||||
check_type_slug='foo-reason', start_time=datetime.time(7, 55), end_time=datetime.time(17, 15)
|
||||
)
|
||||
booking1.user_check.refresh_computed_times()
|
||||
booking1.user_check.save()
|
||||
booking1.refresh_computed_times(commit=True)
|
||||
booking1.user_check.refresh_from_db()
|
||||
assert booking1.user_check.computed_start_time == datetime.time(8, 0)
|
||||
assert booking1.user_check.computed_end_time == datetime.time(17, 30)
|
||||
booking2 = Booking.objects.create(event=event, user_external_id='child:42')
|
||||
|
@ -1590,6 +1590,94 @@ def test_events_check_status_events(app, user, partial_bookings):
|
|||
'label': '',
|
||||
}
|
||||
|
||||
if partial_bookings:
|
||||
booking1.mark_user_presence(
|
||||
check_type_slug='foo-reason', start_time=datetime.time(7, 55), end_time=datetime.time(12, 15)
|
||||
)
|
||||
BookingCheck.objects.create(
|
||||
booking=booking1,
|
||||
start_time=datetime.time(13, 00),
|
||||
end_time=datetime.time(17, 15),
|
||||
presence=False,
|
||||
)
|
||||
booking1.refresh_computed_times(commit=True)
|
||||
user_check1, user_check2 = booking1.user_checks.all().order_by('start_time')
|
||||
assert user_check1.computed_start_time == datetime.time(8, 0)
|
||||
assert user_check1.computed_end_time == datetime.time(12, 30)
|
||||
assert user_check2.computed_start_time == datetime.time(12, 30)
|
||||
assert user_check2.computed_end_time == datetime.time(17, 30)
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
resp = app.get(url, params=params)
|
||||
assert len(ctx.captured_queries) == 6
|
||||
assert resp.json['err'] == 0
|
||||
assert len(resp.json['data']) == 4
|
||||
assert resp.json['data'][0]['event']['slug'] == 'notchecked-event-slug'
|
||||
assert resp.json['data'][0]['check_status'] == {
|
||||
'error_reason': 'event-not-checked',
|
||||
'status': 'error',
|
||||
}
|
||||
assert resp.json['data'][0]['booking'] == {}
|
||||
assert resp.json['data'][1]['event']['slug'] == 'event-slug'
|
||||
assert resp.json['data'][1]['check_status'] == {
|
||||
'check_type': 'foo-reason',
|
||||
'status': 'presence',
|
||||
}
|
||||
assert {
|
||||
'id': booking2.pk,
|
||||
'user_absence_reason': None,
|
||||
'user_presence_reason': 'foo-reason',
|
||||
'user_was_present': True,
|
||||
'start_time': None,
|
||||
'end_time': None,
|
||||
'duration': None,
|
||||
'user_check_start_time': None,
|
||||
'user_check_end_time': None,
|
||||
'user_check_duration': None,
|
||||
'computed_start_time': None,
|
||||
'computed_end_time': None,
|
||||
'computed_duration': None,
|
||||
}.items() <= resp.json['data'][1]['booking'].items()
|
||||
assert resp.json['data'][2]['event']['slug'] == 'recurring-event-slug--2022-05-30-1600'
|
||||
assert resp.json['data'][2]['check_status'] == {
|
||||
'check_type': 'foo-reason',
|
||||
'status': 'presence',
|
||||
}
|
||||
assert {
|
||||
'id': booking1.pk,
|
||||
'user_absence_reason': None,
|
||||
'user_presence_reason': 'foo-reason',
|
||||
'user_was_present': True,
|
||||
'start_time': '08:00:00',
|
||||
'end_time': '17:00:00',
|
||||
'duration': 540,
|
||||
'user_check_start_time': '07:55:00',
|
||||
'user_check_end_time': '12:15:00',
|
||||
'user_check_duration': 260,
|
||||
'computed_start_time': '08:00:00',
|
||||
'computed_end_time': '12:30:00',
|
||||
'computed_duration': 270,
|
||||
}.items() <= resp.json['data'][2]['booking'].items()
|
||||
assert resp.json['data'][3]['event']['slug'] == 'recurring-event-slug--2022-05-30-1600'
|
||||
assert resp.json['data'][3]['check_status'] == {
|
||||
'check_type': None,
|
||||
'status': 'absence',
|
||||
}
|
||||
assert {
|
||||
'id': booking1.pk,
|
||||
'user_absence_reason': None,
|
||||
'user_presence_reason': None,
|
||||
'user_was_present': False,
|
||||
'start_time': '08:00:00',
|
||||
'end_time': '17:00:00',
|
||||
'duration': 540,
|
||||
'user_check_start_time': '13:00:00',
|
||||
'user_check_end_time': '17:15:00',
|
||||
'user_check_duration': 255,
|
||||
'computed_start_time': '12:30:00',
|
||||
'computed_end_time': '17:30:00',
|
||||
'computed_duration': 300,
|
||||
}.items() <= resp.json['data'][3]['booking'].items()
|
||||
|
||||
|
||||
@pytest.mark.freeze_time('2022-05-30 14:00')
|
||||
def test_events_check_status_agendas_filter(app, user):
|
||||
|
|
|
@ -11,6 +11,7 @@ from django.core.management import call_command
|
|||
from django.db import IntegrityError, connection, transaction
|
||||
from django.db.models import Q
|
||||
from django.test import override_settings
|
||||
from django.test.utils import CaptureQueriesContext
|
||||
|
||||
from chrono.agendas.models import (
|
||||
Agenda,
|
||||
|
@ -4034,6 +4035,197 @@ def test_booking_get_computed_end_time(end_time, user_check_end_time, tolerance,
|
|||
assert booking.user_check.get_computed_end_time() == expected
|
||||
|
||||
|
||||
def test_booking_refresh_computed_times_only_one_user_check():
|
||||
agenda = Agenda.objects.create(
|
||||
label='Agenda', kind='events', invoicing_unit='half_hour', invoicing_tolerance=10
|
||||
)
|
||||
event = Event.objects.create(
|
||||
agenda=agenda,
|
||||
start_datetime=make_aware(datetime.datetime(2023, 5, 1, 14, 00)),
|
||||
places=10,
|
||||
)
|
||||
booking = Booking.objects.create(
|
||||
event=event, start_time=datetime.time(8, 0), end_time=datetime.time(17, 0)
|
||||
)
|
||||
|
||||
# only one user_check, presence or absence, same behavior
|
||||
booking_check = BookingCheck.objects.create(
|
||||
booking=booking, presence=True, start_time=datetime.time(7, 45), end_time=datetime.time(17, 15)
|
||||
)
|
||||
booking.refresh_computed_times(commit=True)
|
||||
booking_check.refresh_from_db()
|
||||
assert booking_check.computed_start_time == datetime.time(7, 30)
|
||||
assert booking_check.computed_end_time == datetime.time(17, 30)
|
||||
|
||||
booking_check.presence = False
|
||||
booking.refresh_computed_times(commit=True)
|
||||
booking_check.refresh_from_db()
|
||||
assert booking_check.computed_start_time == datetime.time(7, 30)
|
||||
assert booking_check.computed_end_time == datetime.time(17, 30)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'presence1, start_time1, end_time1, presence2, start_time2, end_time2, expected_start1, expected_end1, expected_start2, expected_end2',
|
||||
[
|
||||
# first presence, then absence
|
||||
(
|
||||
True,
|
||||
datetime.time(7, 45),
|
||||
datetime.time(12, 15),
|
||||
False,
|
||||
datetime.time(13, 0), # no overlapping
|
||||
datetime.time(15, 0),
|
||||
datetime.time(7, 30),
|
||||
datetime.time(12, 30),
|
||||
datetime.time(12, 30),
|
||||
datetime.time(17, 0),
|
||||
),
|
||||
(
|
||||
True,
|
||||
datetime.time(7, 45),
|
||||
datetime.time(12, 15),
|
||||
False,
|
||||
datetime.time(12, 15), # computed start should be 12H00
|
||||
datetime.time(15, 0),
|
||||
datetime.time(7, 30),
|
||||
datetime.time(12, 30),
|
||||
datetime.time(12, 30), # but it is adjusted to 12H30
|
||||
datetime.time(17, 0),
|
||||
),
|
||||
# first absence, then presence
|
||||
(
|
||||
False,
|
||||
datetime.time(7, 45),
|
||||
datetime.time(12, 0), # no overlapping
|
||||
True,
|
||||
datetime.time(13, 15),
|
||||
datetime.time(15, 0),
|
||||
datetime.time(7, 30),
|
||||
datetime.time(13, 0),
|
||||
datetime.time(13, 0),
|
||||
datetime.time(17, 0),
|
||||
),
|
||||
(
|
||||
False,
|
||||
datetime.time(7, 45),
|
||||
datetime.time(12, 45), # computed end should be 13H00
|
||||
True,
|
||||
datetime.time(12, 45),
|
||||
datetime.time(15, 0),
|
||||
datetime.time(7, 30),
|
||||
datetime.time(12, 30), # but it is adjusted to 12H30
|
||||
datetime.time(12, 30),
|
||||
datetime.time(17, 0),
|
||||
),
|
||||
# both absence
|
||||
(
|
||||
True,
|
||||
datetime.time(7, 45),
|
||||
datetime.time(12, 15),
|
||||
True,
|
||||
datetime.time(13, 0), # no overlapping
|
||||
datetime.time(15, 0),
|
||||
datetime.time(7, 30),
|
||||
datetime.time(12, 30),
|
||||
datetime.time(12, 30),
|
||||
datetime.time(17, 0),
|
||||
),
|
||||
(
|
||||
True,
|
||||
datetime.time(7, 45),
|
||||
datetime.time(12, 15),
|
||||
True,
|
||||
datetime.time(12, 15), # computed start should be 12H00
|
||||
datetime.time(15, 0),
|
||||
datetime.time(7, 30),
|
||||
datetime.time(12, 30),
|
||||
datetime.time(12, 30), # but it is adjusted to 12H30
|
||||
datetime.time(17, 0),
|
||||
),
|
||||
# both presence
|
||||
(
|
||||
False,
|
||||
datetime.time(7, 45),
|
||||
datetime.time(12, 15),
|
||||
False,
|
||||
datetime.time(13, 0), # no overlapping
|
||||
datetime.time(15, 0),
|
||||
datetime.time(7, 30),
|
||||
datetime.time(12, 30),
|
||||
datetime.time(12, 30),
|
||||
datetime.time(17, 0),
|
||||
),
|
||||
(
|
||||
False,
|
||||
datetime.time(7, 45),
|
||||
datetime.time(12, 15),
|
||||
False,
|
||||
datetime.time(12, 15), # computed start should be 12H00
|
||||
datetime.time(15, 0),
|
||||
datetime.time(7, 30),
|
||||
datetime.time(12, 30),
|
||||
datetime.time(12, 30), # but it is adjusted to 12H30
|
||||
datetime.time(17, 0),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_booking_refresh_computed_times_two_user_check(
|
||||
presence1,
|
||||
start_time1,
|
||||
end_time1,
|
||||
presence2,
|
||||
start_time2,
|
||||
end_time2,
|
||||
expected_start1,
|
||||
expected_end1,
|
||||
expected_start2,
|
||||
expected_end2,
|
||||
):
|
||||
agenda = Agenda.objects.create(
|
||||
label='Agenda', kind='events', invoicing_unit='half_hour', invoicing_tolerance=10
|
||||
)
|
||||
event = Event.objects.create(
|
||||
agenda=agenda,
|
||||
start_datetime=make_aware(datetime.datetime(2023, 5, 1, 14, 00)),
|
||||
places=10,
|
||||
)
|
||||
booking = Booking.objects.create(
|
||||
event=event, start_time=datetime.time(8, 0), end_time=datetime.time(17, 0)
|
||||
)
|
||||
user_check1 = BookingCheck.objects.create(
|
||||
booking=booking, presence=presence1, start_time=start_time1, end_time=end_time1
|
||||
)
|
||||
user_check2 = BookingCheck.objects.create(
|
||||
booking=booking, presence=presence2, start_time=start_time2, end_time=end_time2
|
||||
)
|
||||
booking.refresh_computed_times(commit=True)
|
||||
user_check1.refresh_from_db()
|
||||
user_check2.refresh_from_db()
|
||||
assert user_check1.computed_start_time == expected_start1
|
||||
assert user_check1.computed_end_time == expected_end1
|
||||
assert user_check2.computed_start_time == expected_start2
|
||||
assert user_check2.computed_end_time == expected_end2
|
||||
|
||||
|
||||
def test_booking_refresh_computed_times_more_than_two_user_check():
|
||||
agenda = Agenda.objects.create(
|
||||
label='Agenda', kind='events', invoicing_unit='half_hour', invoicing_tolerance=10
|
||||
)
|
||||
event = Event.objects.create(
|
||||
agenda=agenda,
|
||||
start_datetime=make_aware(datetime.datetime(2023, 5, 1, 14, 00)),
|
||||
places=10,
|
||||
)
|
||||
booking = Booking.objects.create(
|
||||
event=event, start_time=datetime.time(8, 0), end_time=datetime.time(17, 0)
|
||||
)
|
||||
BookingCheck.objects.create(booking=booking, presence=True)
|
||||
BookingCheck.objects.create(booking=booking, presence=True)
|
||||
BookingCheck.objects.create(booking=booking, presence=True)
|
||||
with pytest.raises(AssertionError):
|
||||
booking.refresh_computed_times(commit=True)
|
||||
|
||||
|
||||
def test_agenda_refresh_booking_computed_times():
|
||||
agenda = Agenda.objects.create(
|
||||
label='Agenda',
|
||||
|
@ -4061,7 +4253,9 @@ def test_agenda_refresh_booking_computed_times():
|
|||
booking.user_check.save()
|
||||
|
||||
def test_booking(success):
|
||||
agenda.refresh_booking_computed_times()
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
agenda.refresh_booking_computed_times()
|
||||
assert len(ctx.captured_queries) in [1, 3]
|
||||
booking.refresh_from_db()
|
||||
if success is True:
|
||||
assert booking.user_check.computed_start_time == datetime.time(7, 0)
|
||||
|
@ -4158,7 +4352,9 @@ def test_event_refresh_booking_computed_times():
|
|||
booking.user_check.save()
|
||||
|
||||
def test_booking(success):
|
||||
event.refresh_booking_computed_times()
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
event.refresh_booking_computed_times()
|
||||
assert len(ctx.captured_queries) in [1, 3]
|
||||
booking.refresh_from_db()
|
||||
if success is True:
|
||||
assert booking.user_check.computed_start_time == datetime.time(7, 0)
|
||||
|
|
Loading…
Reference in New Issue