fix: block duration-aware slot overlaps, not just exact time matches

booked_slots previously recorded only (date, time), so availability
checking excluded an exact time match but not a real overlap — a
4-hour service booked at 9:00 AM didn't block 11:00 AM or 1:00 PM.
Add duration_minutes to booked_slots (populated by sync_booked_slot()
from the booked service's own duration) and switch client-side
computeAvailableSlots to a half-open-interval overlap check, also
rejecting candidates that wouldn't finish before closing time.

Claude-Session: https://claude.ai/code/session_01NhxA4npAXAxmCDEJt6kxSH
This commit is contained in:
belviskhoremk
2026-08-12 23:15:05 +02:00
parent 4ca40bf2b5
commit 43f4f5f46e
8 changed files with 214 additions and 35 deletions

View File

@@ -1,5 +1,5 @@
begin;
select plan(47);
select plan(53);
-- Seed reference data as service_role (bypasses RLS)
set local role service_role;
@@ -350,5 +350,46 @@ select lives_ok(
'the unique index can be created after the self-heal dedupe removes the duplicate'
);
-- Duration-aware booked_slots: the mirror row now carries the booked
-- service's duration, so client-side availability computation can detect
-- overlap (not just an exact time match). knotless has duration_minutes = 240
-- (seeded at the top of this file).
set local role service_role;
select lives_ok(
$$ insert into public.bookings (service_id, date, time, contact_name, contact_phone, contact_email, contact_notes)
values ('knotless', '2026-12-01', '9:00 AM', 'Duration Client', '555-0301', 'duration@example.com', '') $$,
'confirmed booking for the duration test succeeds'
);
select results_eq(
$$ select duration_minutes from public.booked_slots where date = '2026-12-01' and time = '9:00 AM' $$,
$$ values (240) $$,
'booked_slots mirror row carries the booked services duration_minutes'
);
select lives_ok(
$$ update public.bookings set status = 'completed'
where date = '2026-12-01' and time = '9:00 AM' and status = 'confirmed' $$,
'marking the duration-test booking completed succeeds'
);
select results_eq(
$$ select duration_minutes from public.booked_slots where date = '2026-12-01' and time = '9:00 AM' $$,
$$ values (240) $$,
'booked_slots keeps the correct duration_minutes after confirmed -> completed'
);
select lives_ok(
$$ update public.bookings set status = 'cancelled'
where date = '2026-12-01' and time = '9:00 AM' and status = 'completed' $$,
'cancelling the duration-test booking succeeds'
);
select is_empty(
$$ select * from public.booked_slots where date = '2026-12-01' and time = '9:00 AM' $$,
'booked_slots mirror row is removed after the duration-test booking is cancelled, regardless of duration'
);
select * from finish();
rollback;