manager: detect partial booking checks overlap using only form data (#82231)
gitea/chrono/pipeline/head This commit looks good Details

This commit is contained in:
Valentin Deniaud 2023-11-14 16:12:08 +01:00
parent b7c5d4f675
commit 3161f47cd1
4 changed files with 37 additions and 21 deletions

View File

@ -3254,21 +3254,6 @@ class BookingCheck(models.Model):
return False
return True
def overlaps_existing_check(self, start_time, end_time):
booking_checks = BookingCheck.objects.filter(booking=self.booking).exclude(pk=self.pk)
if not booking_checks:
return False
if len(booking_checks) > 1:
raise ValueError('too many booking checks') # should not happen
booking_check = booking_checks[0]
if not start_time or not end_time:
return bool(booking_check.start_time < (start_time or end_time) < booking_check.end_time)
return bool(start_time < booking_check.end_time and end_time > booking_check.start_time)
OpeningHour = collections.namedtuple('OpeningHour', ['begin', 'end'])

View File

@ -648,9 +648,6 @@ class PartialBookingCheckForm(forms.ModelForm):
if start_time and end_time and end_time <= start_time:
raise ValidationError(_('Arrival must be before departure.'))
if self.instance.overlaps_existing_check(start_time, end_time):
raise ValidationError(_('Booking check hours overlap existing check.'))
if self.cleaned_data['presence'] is not None:
kind = 'presence' if self.cleaned_data['presence'] else 'absence'
if f'{kind}_check_type' in self.cleaned_data:

View File

@ -4637,13 +4637,34 @@ class PartialBookingCheckView(ViewableAgendaMixin, TemplateView):
forms = self.get_forms()
all_valid = all(form.is_valid() for form in forms)
if all_valid:
if all_valid and not self.checks_overlap(forms):
for form in forms:
form.save()
return HttpResponseRedirect(self.get_success_url())
return self.render_to_response(self.get_context_data(forms=forms))
def checks_overlap(self, forms):
Interval = collections.namedtuple('Interval', ['start', 'end', 'form'])
intervals = [
Interval(
# if check has no start/end time, take (time, time) as an interval
form.cleaned_data['start_time'] or form.cleaned_data['end_time'],
form.cleaned_data['end_time'] or form.cleaned_data['start_time'],
form,
)
for form in forms
if form.cleaned_data['presence'] is not None
]
intervals.sort(key=lambda x: x.start)
for i in range(1, len(intervals)):
if intervals[i - 1].end > intervals[i].start:
intervals[i].form.add_error(None, _('Booking check hours are overlapping.'))
return True
return False
def get_success_url(self):
date = self.event.start_datetime
return reverse(

View File

@ -552,11 +552,11 @@ def test_manager_partial_bookings_multiple_checks(app, admin_user):
resp.form['check-2-start_time'] = '11:30'
resp.form['check-2-end_time'] = ''
resp = resp.form.submit()
assert 'Booking check hours overlap existing check.' in resp.text
assert 'Booking check hours are overlapping.' in resp.text
resp.form['check-2-end_time'] = '17:30'
resp = resp.form.submit()
assert 'Booking check hours overlap existing check.' in resp.text
assert 'Booking check hours are overlapping.' in resp.text
resp.form['check-2-start_time'] = '12:30'
resp.form['check-2-presence'] = ''
@ -578,6 +578,19 @@ def test_manager_partial_bookings_multiple_checks(app, admin_user):
assert 'Arrival must be before departure.' in resp.text
assert 'gadjo-folded' not in resp.text
# overlap is detected when 2 checks are created at the same time
BookingCheck.objects.all().delete()
resp.form['presence'] = 'True'
resp.form['start_time'] = '10:00'
resp.form['end_time'] = '11:00'
resp.form['check-2-presence'] = 'False'
resp.form['check-2-start_time'] = '10:30'
resp.form['check-2-end_time'] = '11:30'
resp = resp.form.submit()
assert 'Booking check hours are overlapping.' in resp.text
@pytest.mark.parametrize('subscription_only', (True, False))
def test_manager_partial_bookings_incomplete_check(subscription_only, app, admin_user):