generated from MokoConsulting/Template-Joomla
fix: review #19 — booking overlap check (FOR UPDATE), wait estimate excludes in-service
This commit is contained in:
@@ -82,18 +82,52 @@ class BookingHelper
|
||||
{
|
||||
$db = Factory::getContainer()->get(DatabaseInterface::class);
|
||||
|
||||
$booking = (object) [
|
||||
'client_contact_id' => $clientContactId,
|
||||
'stylist_id' => $stylistId,
|
||||
'service_id' => $serviceId,
|
||||
'start_time' => $datetime,
|
||||
'status' => 'confirmed',
|
||||
'created_at' => Factory::getDate()->toSql(),
|
||||
];
|
||||
// Get service duration for overlap check
|
||||
$db->setQuery($db->getQuery(true)->select('duration_minutes')->from('#__mokosuitebeauty_services')->where('id = ' . (int) $serviceId));
|
||||
$duration = (int) $db->loadResult();
|
||||
|
||||
$db->insertObject('#__mokosuitebeauty_bookings', $booking, 'id');
|
||||
if ($duration <= 0) {
|
||||
return (object) ['success' => false, 'error' => 'Invalid service'];
|
||||
}
|
||||
|
||||
return (object) ['success' => true, 'booking_id' => (int) $booking->id];
|
||||
$db->transactionStart();
|
||||
|
||||
try {
|
||||
// Check for overlapping bookings with row lock
|
||||
$db->setQuery(
|
||||
'SELECT COUNT(*) FROM #__mokosuitebeauty_bookings b'
|
||||
. ' JOIN #__mokosuitebeauty_services s ON s.id = b.service_id'
|
||||
. ' WHERE b.stylist_id = ' . (int) $stylistId
|
||||
. ' AND b.status NOT IN (' . $db->quote('cancelled') . ',' . $db->quote('no_show') . ')'
|
||||
. ' AND b.start_time < DATE_ADD(' . $db->quote($datetime) . ', INTERVAL ' . $duration . ' MINUTE)'
|
||||
. ' AND DATE_ADD(b.start_time, INTERVAL s.duration_minutes MINUTE) > ' . $db->quote($datetime)
|
||||
. ' FOR UPDATE'
|
||||
);
|
||||
$conflicts = (int) $db->loadResult();
|
||||
|
||||
if ($conflicts > 0) {
|
||||
$db->transactionRollback();
|
||||
return (object) ['success' => false, 'error' => 'Time slot is no longer available'];
|
||||
}
|
||||
|
||||
$booking = (object) [
|
||||
'client_contact_id' => $clientContactId,
|
||||
'stylist_id' => $stylistId,
|
||||
'service_id' => $serviceId,
|
||||
'start_time' => $datetime,
|
||||
'status' => 'confirmed',
|
||||
'created_at' => Factory::getDate()->toSql(),
|
||||
];
|
||||
|
||||
$db->insertObject('#__mokosuitebeauty_bookings', $booking, 'id');
|
||||
|
||||
$db->transactionCommit();
|
||||
|
||||
return (object) ['success' => true, 'booking_id' => (int) $booking->id];
|
||||
} catch (\Throwable $e) {
|
||||
$db->transactionRollback();
|
||||
return (object) ['success' => false, 'error' => 'Booking failed'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -77,7 +77,7 @@ class WalkInHelper
|
||||
->select('COALESCE(SUM(s.duration_minutes), 0)')
|
||||
->from($db->quoteName('#__mokosuitebeauty_walkins', 'w'))
|
||||
->join('LEFT', $db->quoteName('#__mokosuitebeauty_services', 's') . ' ON s.id = w.service_id')
|
||||
->where($db->quoteName('w.status') . ' IN (' . $db->quote('waiting') . ',' . $db->quote('in_service') . ')'));
|
||||
->where($db->quoteName('w.status') . ' = ' . $db->quote('waiting')));
|
||||
|
||||
$totalMinutes = (int) $db->loadResult();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user