Skip to content

Commit 11ba4b9

Browse files
committed
fix review
1 parent dfb93f9 commit 11ba4b9

File tree

3 files changed

+32
-15
lines changed

3 files changed

+32
-15
lines changed

appointment/forms.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,14 @@ class SlotForm(forms.Form):
2525
error_messages={'invalid_choice': _('Staff member does not exist')}
2626
)
2727
service_id = forms.ModelChoiceField(
28-
queryset=None,
28+
queryset=Service.objects.none(),
2929
required=False,
3030
error_messages={'invalid_choice': _('Service does not exist')}
3131
)
3232

3333
def __init__(self, *args, **kwargs):
3434
super().__init__(*args, **kwargs)
35-
from appointment.models import Service as ServiceModel
36-
self.fields['service_id'].queryset = ServiceModel.objects.all()
35+
self.fields['service_id'].queryset = Service.objects.all()
3736

3837

3938
class AppointmentRequestForm(forms.ModelForm):

appointment/tests/utils/test_db_helpers.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -629,12 +629,27 @@ def test_gap_time_blocks_slot_immediately_after_appointment(self):
629629
def test_gap_time_slot_after_gap_window_is_available(self):
630630
"""A slot starting after appointment_end + gap_time is available."""
631631
appt = self._make_appointment(datetime.time(9, 0), datetime.time(10, 0), service=self.service1)
632-
# 30-min gap → effective blocked range is [9:00, 10:30)
633-
# slot 10:30 is at the boundary: slot < (10:00 + 30min) is False → available
632+
# 30-min gap applied on both sides:
633+
# gap-after: slot 10:00-10:30 is blocked (10:00 < 10:00+30min)
634+
# slot 10:30: slot(10:30) < 10:30 is False → available
634635
result = exclude_booked_slots([appt], self.slots, self.slot_duration, gap_time=30)
635636
slot_10_30 = datetime.datetime.combine(self.today, datetime.time(10, 30))
636637
self.assertIn(slot_10_30, result)
637638

639+
def test_gap_time_blocks_slot_ending_within_gap_before_appointment(self):
640+
"""A slot whose end falls within the gap window before an appointment starts is blocked.
641+
Covers the pre-appointment side of the gap (reviewer comment).
642+
"""
643+
# Appointment starts at 10:30. slot_duration=30min, so slot 10:00→10:30 ends exactly at 10:30.
644+
# With a 30-min gap: slot_end(10:30) + gap(30min) = 11:00 > appointment_start(10:30) → blocked.
645+
appt = self._make_appointment(datetime.time(10, 30), datetime.time(11, 30), service=self.service1)
646+
result = exclude_booked_slots([appt], self.slots, self.slot_duration, gap_time=30)
647+
slot_10_00 = datetime.datetime.combine(self.today, datetime.time(10, 0))
648+
self.assertNotIn(slot_10_00, result)
649+
# slot 9:30 → slot_end 10:00 + gap 30min = 10:30; 10:30 < 10:30 is False → available
650+
slot_9_30 = datetime.datetime.combine(self.today, datetime.time(9, 30))
651+
self.assertIn(slot_9_30, result)
652+
638653
def test_gap_time_zero_no_effect(self):
639654
"""gap_time=0 has no effect compared to gap_time=None."""
640655
appt = self._make_appointment(datetime.time(9, 0), datetime.time(10, 0), service=self.service1)
@@ -644,21 +659,23 @@ def test_gap_time_zero_no_effect(self):
644659

645660
def test_combined_service_duration_and_gap_time(self):
646661
"""service_duration and gap_time can be used together."""
647-
# 1-hr appointment at 10:00-11:00, 1-hr service_duration, 30-min gap
662+
# 1-hr appointment at 10:00-11:00, 1-hr service_duration, 30-min gap (both sides)
648663
appt = self._make_appointment(datetime.time(10, 0), datetime.time(11, 0), service=self.service1)
649664
service_duration = datetime.timedelta(hours=1)
650-
# Effective: check_duration=1hr, gap=30min, so slot < 11:30 is blocked if slot+1hr > 10:00
651-
# slot 9:30 → [9:30, 10:30] > 10:00 → blocked
652-
# slot 10:00 → blocked (inside appointment)
653-
# slot 10:30 → slot(10:30) < 11:30 and [10:30,11:30] > 10:00 → blocked
654-
# slot 11:00 → slot(11:00) < 11:30 → blocked
655-
# slot 11:30 → slot(11:30) not < 11:30 → available
665+
# check_duration = max(30min, 1hr) = 1hr; gap = 30min
666+
# blocked if: appointment_start(10:00) < slot_end+gap AND slot < appointment_end(11:00)+gap(11:30)
667+
# slot 9:00 → slot_end=10:00; 10:00 < 10:30=True; 9:00 < 11:30=True → blocked (pre-gap)
668+
# slot 9:30 → slot_end=10:30; 10:00 < 11:00=True; 9:30 < 11:30=True → blocked
669+
# slot 10:00, 10:30, 11:00 → blocked (inside/near appointment)
670+
# slot 11:30 → slot(11:30) < 11:30 is False → available
656671
result = exclude_booked_slots([appt], self.slots, self.slot_duration,
657672
service_duration=service_duration, gap_time=30)
658673
slot_11_30 = datetime.datetime.combine(self.today, datetime.time(11, 30))
659674
self.assertIn(slot_11_30, result)
660675
slot_10_30 = datetime.datetime.combine(self.today, datetime.time(10, 30))
661676
self.assertNotIn(slot_10_30, result)
677+
slot_9_00 = datetime.datetime.combine(self.today, datetime.time(9, 0))
678+
self.assertNotIn(slot_9_00, result)
662679

663680

664681
class TestDayOffExistsForDateRange(BaseTest):

appointment/utils/db_helpers.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,9 @@ def exclude_booked_slots(appointments, slots, slot_duration=None, service_durati
357357
:param service_duration: The actual service duration (timedelta). When provided, the effective check
358358
window is max(slot_duration, service_duration), preventing overlaps for services longer than the
359359
configured slot step.
360-
:param gap_time: Required rest time in minutes between appointments. When provided, a slot is
361-
considered unavailable if it falls within gap_time minutes after an existing appointment ends.
360+
:param gap_time: Required rest time in minutes between appointments. Applied on both sides: a slot is
361+
unavailable if it ends within gap_time minutes before an appointment starts, or if it starts
362+
within gap_time minutes after an appointment ends.
362363
:return: The slots with the booked slots excluded.
363364
"""
364365
if service_duration is not None:
@@ -374,7 +375,7 @@ def exclude_booked_slots(appointments, slots, slot_duration=None, service_durati
374375
for appointment in appointments:
375376
appointment_start_time = appointment.get_start_time()
376377
appointment_end_time = appointment.get_end_time()
377-
if appointment_start_time < slot_end and slot < appointment_end_time + gap_delta:
378+
if appointment_start_time < slot_end + gap_delta and slot < appointment_end_time + gap_delta:
378379
is_available = False
379380
break
380381
if is_available:

0 commit comments

Comments
 (0)