From ddc8ac5be1d7f8f3a4119ecff3e67b1f3208256b Mon Sep 17 00:00:00 2001 From: amit sharma Date: Wed, 4 Nov 2026 19:15:19 +0530 Subject: [PATCH 1/3] Fixed: Slow loading of Admin Orders and Book Now pages with large order volumes --- controllers/admin/AdminOrdersController.php | 50 +++++++++++++++---- .../classes/HotelBookingDetail.php | 48 +++++++++++++----- .../AdminHotelRoomsBookingController.php | 2 +- 3 files changed, 76 insertions(+), 24 deletions(-) diff --git a/controllers/admin/AdminOrdersController.php b/controllers/admin/AdminOrdersController.php index b667515ae..a37008923 100644 --- a/controllers/admin/AdminOrdersController.php +++ b/controllers/admin/AdminOrdersController.php @@ -61,20 +61,20 @@ class AdminOrdersControllerCore extends AdminController CONCAT(c.`firstname`, \' \', c.`lastname`) AS `customer`, osl.`name` AS `osname`, os.`color`, cu.iso_code AS currency, - IF((SELECT so.id_order FROM `'._DB_PREFIX_.'orders` so WHERE so.id_customer = a.id_customer AND so.id_order < a.id_order LIMIT 1) > 0, 0, 1) as new, + IF(fo.`first_order` IS NULL OR fo.`first_order` = a.`id_order`, 1, 0) as new, IF(a.valid, 1, 0) badge_success, hbil.`hotel_name`, - (SELECT COUNT(hbd.`id`) FROM `'._DB_PREFIX_.'htl_booking_detail` hbd WHERE hbd.`id_order` = a.`id_order`) as num_rooms, - (SELECT SUM(hbd.`adults`) + SUM(hbd.`children`) FROM `'._DB_PREFIX_.'htl_booking_detail` hbd WHERE hbd.`id_order` = a.`id_order`) as total_guests_count, - (SELECT CONCAT( - SUM(hbd.`adults`), + hba.`num_rooms` AS num_rooms, + hba.`total_guests_count` AS total_guests_count, + CONCAT( + hba.`adults`, \' '.$this->l('Adult(s)').' \', - IF(SUM(hbd.`children`), CONCAT(SUM(hbd.`children`), \' '.$this->l('Children').'\'), \'\') - ) FROM `'._DB_PREFIX_.'htl_booking_detail` hbd WHERE hbd.`id_order` = a.`id_order`) as total_guests, - (SELECT SUM(DATEDIFF(hbd.`date_to`, hbd.`date_from`)) FROM `'._DB_PREFIX_.'htl_booking_detail` hbd WHERE hbd.`id_order` = a.`id_order`) as los, + IF(hba.`children`, CONCAT(hba.`children`, \' '.$this->l('Children').'\'), \'\') + ) AS total_guests, + hba.`los` AS los, hbd.`id_room` AS id_room_information, - (SELECT GROUP_CONCAT(CONCAT(ps.period, \'~\', ps.cnt) ORDER BY ps.period SEPARATOR \'::\') FROM (SELECT CONCAT(hbd.`date_from`, \'|\', hbd.`date_to`) AS period, COUNT(*) AS cnt FROM `'._DB_PREFIX_.'htl_booking_detail` hbd WHERE hbd.`id_order` = a.`id_order` GROUP BY hbd.`date_from`, hbd.`date_to`) AS ps) AS stay_periods, - (SELECT COUNT(spod.`id_service_product_order_detail`) FROM `'._DB_PREFIX_.'service_product_order_detail` spod WHERE spod.`id_order` = a.`id_order` AND spod.`id_htl_booking_detail`=0) as num_products'; + hsp.`stay_periods` AS stay_periods, + IFNULL(spn.`num_products`, 0) AS num_products'; $this->_select .= ', (SELECT sl.`name` FROM `'._DB_PREFIX_.'source` s LEFT JOIN `'._DB_PREFIX_.'source_lang` sl ON (sl.`id_source` = s.`id_source` AND sl.`id_lang` = '.(int)$this->context->language->id.') WHERE s.`id_source` = a.`id_source`) AS `booking_source_name`, @@ -89,8 +89,36 @@ class AdminOrdersControllerCore extends AdminController LEFT JOIN `'._DB_PREFIX_.'service_product_order_detail` spod ON (spod.`id_order` = a.`id_order`) LEFT JOIN `'._DB_PREFIX_.'htl_branch_info_lang` hbil ON (IF(hbd.`id_hotel`, (hbil.`id` = hbd.`id_hotel`), (hbil.`id` = spod.`id_hotel`))) LEFT JOIN ( - SELECT hbd2.`id`, IF(hbd2.`id` IN ('.OrderReturn::getRefundedBookingIdsSubquery().'), 1, 0) AS is_room_refunded + SELECT so.`id_customer`, MIN(so.`id_order`) AS first_order + FROM `'._DB_PREFIX_.'orders` so + GROUP BY so.`id_customer` + ) fo ON (fo.`id_customer` = a.`id_customer`) + LEFT JOIN ( + SELECT hbd3.`id_order`, COUNT(hbd3.`id`) AS num_rooms, SUM(hbd3.`adults`) AS adults, SUM(hbd3.`children`) AS children, + SUM(hbd3.`adults`) + SUM(hbd3.`children`) AS total_guests_count, + SUM(DATEDIFF(hbd3.`date_to`, hbd3.`date_from`)) AS los + FROM `'._DB_PREFIX_.'htl_booking_detail` hbd3 + GROUP BY hbd3.`id_order` + ) hba ON (hba.`id_order` = a.`id_order`) + LEFT JOIN ( + SELECT sp.`id_order`, GROUP_CONCAT(CONCAT(sp.`period`, \'~\', sp.`cnt`) ORDER BY sp.`period` SEPARATOR \'::\') AS stay_periods + FROM ( + SELECT hbd4.`id_order`, CONCAT(hbd4.`date_from`, \'|\', hbd4.`date_to`) AS period, COUNT(*) AS cnt + FROM `'._DB_PREFIX_.'htl_booking_detail` hbd4 + GROUP BY hbd4.`id_order`, hbd4.`date_from`, hbd4.`date_to` + ) sp + GROUP BY sp.`id_order` + ) hsp ON (hsp.`id_order` = a.`id_order`) + LEFT JOIN ( + SELECT spod2.`id_order`, COUNT(spod2.`id_service_product_order_detail`) AS num_products + FROM `'._DB_PREFIX_.'service_product_order_detail` spod2 + WHERE spod2.`id_htl_booking_detail` = 0 + GROUP BY spod2.`id_order` + ) spn ON (spn.`id_order` = a.`id_order`) + LEFT JOIN ( + SELECT hbd2.`id`, IF(rb.`id_htl_booking` IS NULL, 0, 1) AS is_room_refunded FROM `'._DB_PREFIX_.'htl_booking_detail` hbd2 + LEFT JOIN ('.OrderReturn::getRefundedBookingIdsSubquery().' GROUP BY ord.`id_htl_booking`) rb ON (rb.`id_htl_booking` = hbd2.`id`) ) rrb ON (rrb.`id` = hbd.`id`)'; $this->_orderBy = 'id_order'; diff --git a/modules/hotelreservationsystem/classes/HotelBookingDetail.php b/modules/hotelreservationsystem/classes/HotelBookingDetail.php index 2672d223a..2911c2870 100644 --- a/modules/hotelreservationsystem/classes/HotelBookingDetail.php +++ b/modules/hotelreservationsystem/classes/HotelBookingDetail.php @@ -1919,8 +1919,14 @@ class HotelBookingDetail extends ObjectModel return $bookingData; } - public function getAvailableRoomsForReallocation($dateFrom, $dateTo, $idRoomType, $idHotel, $roomTypesToUpgrade = 0) + public function getAvailableRoomsForReallocation($dateFrom, $dateTo, $idRoomType, $idHotel, $roomTypesToUpgrade = 0, $cache = false) { + // When $cache is true, reuse the result for bookings sharing the same dates and hotel + $cacheKey = 'HotelBookingDetail::getAvailableRoomsForReallocation_'.(int) $idHotel.'_'.(int) $idRoomType.'_'.(int) $roomTypesToUpgrade.'_'.$dateFrom.'_'.$dateTo; + if ($cache && Cache::isStored($cacheKey)) { + return Cache::retrieve($cacheKey); + } + $context = Context::getContext(); $bookingParams = array( 'date_from' => $dateFrom, @@ -1938,6 +1944,10 @@ class HotelBookingDetail extends ObjectModel $searchRoomsInfo = $this->getBookingData($bookingParams); if (empty($searchRoomsInfo['rm_data']) || !is_array($searchRoomsInfo['rm_data'])) { + if ($cache) { + Cache::store($cacheKey, false); + } + return false; } @@ -1974,11 +1984,13 @@ class HotelBookingDetail extends ObjectModel } } - if ($roomTypesToUpgrade) { - return $availableRoomTypes ? $availableRoomTypes : false; + $result = $roomTypesToUpgrade ? $availableRoomTypes : $availableRooms; + $result = $result ? $result : false; + if ($cache) { + Cache::store($cacheKey, $result); } - return $availableRooms ? $availableRooms : false; + return $result; } /** @@ -1992,13 +2004,18 @@ class HotelBookingDetail extends ObjectModel */ public function getAvailableRoomsForSwapping($date_from, $date_to, $id_room_type, $hotel_id, $id_room) { - $sql = 'SELECT `id` as `id_hotel_booking`, `id_room`, `id_product`, `id_hotel`, `room_num`, `comment` AS `room_comment` - FROM `'._DB_PREFIX_.'htl_booking_detail` - WHERE `id_hotel` = '.(int)$hotel_id.' AND `id_product` = '.(int)$id_room_type.' - AND `date_from` = \''.pSQL($date_from).'\' AND `date_to` = \''.pSQL($date_to).'\' - AND `id_room`!='.(int)$id_room.' AND `id` NOT IN ('.OrderReturn::getRefundedBookingIdsSubquery().') AND `id_status` NOT IN ('.HotelBookingDetail::STATUS_CANCELLED.', '.HotelBookingDetail::STATUS_NO_SHOW.') AND `is_back_order` = 0'; + $cacheKey = 'HotelBookingDetail::getAvailableRoomsForSwapping_'.(int) $hotel_id.'_'.(int) $id_room_type.'_'.(int) $id_room.'_'.$date_from.'_'.$date_to; + if (!Cache::isStored($cacheKey)) { + $sql = 'SELECT `id` as `id_hotel_booking`, `id_room`, `id_product`, `id_hotel`, `room_num`, `comment` AS `room_comment` + FROM `'._DB_PREFIX_.'htl_booking_detail` + WHERE `id_hotel` = '.(int)$hotel_id.' AND `id_product` = '.(int)$id_room_type.' + AND `date_from` = \''.pSQL($date_from).'\' AND `date_to` = \''.pSQL($date_to).'\' + AND `id_room`!='.(int)$id_room.' AND `id` NOT IN ('.OrderReturn::getRefundedBookingIdsSubquery().') AND `id_status` NOT IN ('.HotelBookingDetail::STATUS_CANCELLED.', '.HotelBookingDetail::STATUS_NO_SHOW.') AND `is_back_order` = 0'; - return Db::getInstance()->executeS($sql); + Cache::store($cacheKey, Db::getInstance()->executeS($sql)); + } + + return Cache::retrieve($cacheKey); } /** @@ -2039,6 +2056,8 @@ class HotelBookingDetail extends ObjectModel */ public function reallocateBooking($idHotelBooking, $idRoom, $priceDiffTaxExcl = 0) { + // Rooms availability changes with this, so remove the cached availability lists + Cache::clean('HotelBookingDetail::getAvailableRoomsFor*'); $result = true; $reallocatedBookingId = 0; // get the cart booking data for the given booking @@ -2551,10 +2570,13 @@ class HotelBookingDetail extends ObjectModel AND id_room = '.(int) $objOldHotelBooking->id_room.' AND `id_order` = '.(int) $objOldHotelBooking->id_order ); $objCartBookingData = new HotelCartBookingData($idHotelCartBookingData); - $objCartBookingData->id_room = $idRoom; + // update only the existing row, else saving would insert an empty cart booking row + if (Validate::isLoadedObject($objCartBookingData)) { + $objCartBookingData->id_room = $idRoom; + $objCartBookingData->save(); + } // set backorder to 0 as available reallocate rooms will always be free $objOldHotelBooking->is_back_order = 0; - $objCartBookingData->save(); // update in the hotel booking detail table $objOldHotelBooking->id_room = $idRoom; @@ -2633,6 +2655,8 @@ class HotelBookingDetail extends ObjectModel public function swapBooking($idHotelBookingFrom, $idHotelBookingTo) { + // Rooms availability changes with this, so remove the cached availability lists + Cache::clean('HotelBookingDetail::getAvailableRoomsFor*'); $result = true; // get the cart booking data for the given booking if (Validate::isLoadedObject($objHotelBookingFrom = new HotelBookingDetail($idHotelBookingFrom)) diff --git a/modules/hotelreservationsystem/controllers/admin/AdminHotelRoomsBookingController.php b/modules/hotelreservationsystem/controllers/admin/AdminHotelRoomsBookingController.php index 2c6a9795e..c1792b3be 100644 --- a/modules/hotelreservationsystem/controllers/admin/AdminHotelRoomsBookingController.php +++ b/modules/hotelreservationsystem/controllers/admin/AdminHotelRoomsBookingController.php @@ -997,7 +997,7 @@ public function ajaxProcessGetCalenderData() $booking_data['rm_data'][$key_bk_data]['data']['booked'][$booked_k1]['detail'][$kDtl]['currency_sign'] = (new Currency($objOrder->id_currency))->sign; } - $booking_data['rm_data'][$key_bk_data]['data']['booked'][$booked_k1]['detail'][$kDtl]['avail_room_types_to_realloc'] = $obj_booking_dtl->getAvailableRoomsForReallocation($booked_v1['detail'][$kDtl]['date_from'], $booked_v1['detail'][$kDtl]['date_to'], 0, $booked_v1['id_hotel'], 1); + $booking_data['rm_data'][$key_bk_data]['data']['booked'][$booked_k1]['detail'][$kDtl]['avail_room_types_to_realloc'] = $obj_booking_dtl->getAvailableRoomsForReallocation($booked_v1['detail'][$kDtl]['date_from'], $booked_v1['detail'][$kDtl]['date_to'], 0, $booked_v1['id_hotel'], 1, true); $booking_data['rm_data'][$key_bk_data]['data']['booked'][$booked_k1]['detail'][$kDtl]['avail_rooms_to_swap'] = $obj_booking_dtl->getAvailableRoomsForSwapping($booked_v1['detail'][$kDtl]['date_from'], $booked_v1['detail'][$kDtl]['date_to'], $booked_v1['id_product'], $booked_v1['id_hotel'], $booked_v1['id_room']); } } From 07c2e2c6606719d2250535b58773bae12856d0fc Mon Sep 17 00:00:00 2001 From: amit sharma Date: Wed, 4 Nov 2026 19:32:56 +0530 Subject: [PATCH 2/3] Fixed: review changes --- .../classes/HotelBookingDetail.php | 26 ++++++++++++------- .../AdminHotelRoomsBookingController.php | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/modules/hotelreservationsystem/classes/HotelBookingDetail.php b/modules/hotelreservationsystem/classes/HotelBookingDetail.php index 2911c2870..014691594 100644 --- a/modules/hotelreservationsystem/classes/HotelBookingDetail.php +++ b/modules/hotelreservationsystem/classes/HotelBookingDetail.php @@ -2002,20 +2002,26 @@ class HotelBookingDetail extends ObjectModel * * @return [array|false] [Returs array of the available rooms for swapping if rooms found else returnss false] */ - public function getAvailableRoomsForSwapping($date_from, $date_to, $id_room_type, $hotel_id, $id_room) + public function getAvailableRoomsForSwapping($date_from, $date_to, $id_room_type, $hotel_id, $id_room, $cache = false) { + // When $cache is true, reuse the result for the same room, dates and hotel $cacheKey = 'HotelBookingDetail::getAvailableRoomsForSwapping_'.(int) $hotel_id.'_'.(int) $id_room_type.'_'.(int) $id_room.'_'.$date_from.'_'.$date_to; - if (!Cache::isStored($cacheKey)) { - $sql = 'SELECT `id` as `id_hotel_booking`, `id_room`, `id_product`, `id_hotel`, `room_num`, `comment` AS `room_comment` - FROM `'._DB_PREFIX_.'htl_booking_detail` - WHERE `id_hotel` = '.(int)$hotel_id.' AND `id_product` = '.(int)$id_room_type.' - AND `date_from` = \''.pSQL($date_from).'\' AND `date_to` = \''.pSQL($date_to).'\' - AND `id_room`!='.(int)$id_room.' AND `id` NOT IN ('.OrderReturn::getRefundedBookingIdsSubquery().') AND `id_status` NOT IN ('.HotelBookingDetail::STATUS_CANCELLED.', '.HotelBookingDetail::STATUS_NO_SHOW.') AND `is_back_order` = 0'; - - Cache::store($cacheKey, Db::getInstance()->executeS($sql)); + if ($cache && Cache::isStored($cacheKey)) { + return Cache::retrieve($cacheKey); } - return Cache::retrieve($cacheKey); + $sql = 'SELECT `id` as `id_hotel_booking`, `id_room`, `id_product`, `id_hotel`, `room_num`, `comment` AS `room_comment` + FROM `'._DB_PREFIX_.'htl_booking_detail` + WHERE `id_hotel` = '.(int)$hotel_id.' AND `id_product` = '.(int)$id_room_type.' + AND `date_from` = \''.pSQL($date_from).'\' AND `date_to` = \''.pSQL($date_to).'\' + AND `id_room`!='.(int)$id_room.' AND `id` NOT IN ('.OrderReturn::getRefundedBookingIdsSubquery().') AND `id_status` NOT IN ('.HotelBookingDetail::STATUS_CANCELLED.', '.HotelBookingDetail::STATUS_NO_SHOW.') AND `is_back_order` = 0'; + + $result = Db::getInstance()->executeS($sql); + if ($cache) { + Cache::store($cacheKey, $result); + } + + return $result; } /** diff --git a/modules/hotelreservationsystem/controllers/admin/AdminHotelRoomsBookingController.php b/modules/hotelreservationsystem/controllers/admin/AdminHotelRoomsBookingController.php index c1792b3be..c5b85a0ae 100644 --- a/modules/hotelreservationsystem/controllers/admin/AdminHotelRoomsBookingController.php +++ b/modules/hotelreservationsystem/controllers/admin/AdminHotelRoomsBookingController.php @@ -998,7 +998,7 @@ public function ajaxProcessGetCalenderData() } $booking_data['rm_data'][$key_bk_data]['data']['booked'][$booked_k1]['detail'][$kDtl]['avail_room_types_to_realloc'] = $obj_booking_dtl->getAvailableRoomsForReallocation($booked_v1['detail'][$kDtl]['date_from'], $booked_v1['detail'][$kDtl]['date_to'], 0, $booked_v1['id_hotel'], 1, true); - $booking_data['rm_data'][$key_bk_data]['data']['booked'][$booked_k1]['detail'][$kDtl]['avail_rooms_to_swap'] = $obj_booking_dtl->getAvailableRoomsForSwapping($booked_v1['detail'][$kDtl]['date_from'], $booked_v1['detail'][$kDtl]['date_to'], $booked_v1['id_product'], $booked_v1['id_hotel'], $booked_v1['id_room']); + $booking_data['rm_data'][$key_bk_data]['data']['booked'][$booked_k1]['detail'][$kDtl]['avail_rooms_to_swap'] = $obj_booking_dtl->getAvailableRoomsForSwapping($booked_v1['detail'][$kDtl]['date_from'], $booked_v1['detail'][$kDtl]['date_to'], $booked_v1['id_product'], $booked_v1['id_hotel'], $booked_v1['id_room'], true); } } } From d7d2a76c6bf90091904bd7125cfcb8682cb46950 Mon Sep 17 00:00:00 2001 From: amit sharma Date: Wed, 4 Nov 2026 19:51:07 +0530 Subject: [PATCH 3/3] Fixed: added key logic in the admin hotel crontroller --- .../admin/AdminHotelRoomsBookingController.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/hotelreservationsystem/controllers/admin/AdminHotelRoomsBookingController.php b/modules/hotelreservationsystem/controllers/admin/AdminHotelRoomsBookingController.php index c5b85a0ae..b93c07dec 100644 --- a/modules/hotelreservationsystem/controllers/admin/AdminHotelRoomsBookingController.php +++ b/modules/hotelreservationsystem/controllers/admin/AdminHotelRoomsBookingController.php @@ -971,6 +971,8 @@ public function ajaxProcessGetCalenderData() if ($booking_data) { $objHotelRoomType = new HotelRoomType(); + $availRoomTypesToRealloc = array(); + $availRoomsToSwap = array(); foreach ($booking_data['rm_data'] as $key_bk_data => $value_bk_data) { $booking_data['rm_data'][$key_bk_data]['room_type_info'] = $objHotelRoomType->getRoomTypeInfoByIdProduct($value_bk_data['id_product']); if (isset($value_bk_data['data']['booked']) && $value_bk_data['data']['booked']) { @@ -997,8 +999,18 @@ public function ajaxProcessGetCalenderData() $booking_data['rm_data'][$key_bk_data]['data']['booked'][$booked_k1]['detail'][$kDtl]['currency_sign'] = (new Currency($objOrder->id_currency))->sign; } - $booking_data['rm_data'][$key_bk_data]['data']['booked'][$booked_k1]['detail'][$kDtl]['avail_room_types_to_realloc'] = $obj_booking_dtl->getAvailableRoomsForReallocation($booked_v1['detail'][$kDtl]['date_from'], $booked_v1['detail'][$kDtl]['date_to'], 0, $booked_v1['id_hotel'], 1, true); - $booking_data['rm_data'][$key_bk_data]['data']['booked'][$booked_k1]['detail'][$kDtl]['avail_rooms_to_swap'] = $obj_booking_dtl->getAvailableRoomsForSwapping($booked_v1['detail'][$kDtl]['date_from'], $booked_v1['detail'][$kDtl]['date_to'], $booked_v1['id_product'], $booked_v1['id_hotel'], $booked_v1['id_room'], true); + // rooms available for reallocation are same for the same dates and hotel, so get them only once + $reallocKey = $booked_v1['detail'][$kDtl]['date_from'].'_'.$booked_v1['detail'][$kDtl]['date_to'].'_0_'.$booked_v1['id_hotel']; + if (!isset($availRoomTypesToRealloc[$reallocKey])) { + $availRoomTypesToRealloc[$reallocKey] = $obj_booking_dtl->getAvailableRoomsForReallocation($booked_v1['detail'][$kDtl]['date_from'], $booked_v1['detail'][$kDtl]['date_to'], 0, $booked_v1['id_hotel'], 1, true); + } + $booking_data['rm_data'][$key_bk_data]['data']['booked'][$booked_k1]['detail'][$kDtl]['avail_room_types_to_realloc'] = !empty($availRoomTypesToRealloc[$reallocKey]) ? $availRoomTypesToRealloc[$reallocKey] : false; + // rooms available for swapping are same for the same room, dates and hotel, so get them only once + $swapKey = $booked_v1['detail'][$kDtl]['date_from'].'_'.$booked_v1['detail'][$kDtl]['date_to'].'_'.$booked_v1['id_product'].'_'.$booked_v1['id_hotel'].'_'.$booked_v1['id_room']; + if (!isset($availRoomsToSwap[$swapKey])) { + $availRoomsToSwap[$swapKey] = $obj_booking_dtl->getAvailableRoomsForSwapping($booked_v1['detail'][$kDtl]['date_from'], $booked_v1['detail'][$kDtl]['date_to'], $booked_v1['id_product'], $booked_v1['id_hotel'], $booked_v1['id_room'], true); + } + $booking_data['rm_data'][$key_bk_data]['data']['booked'][$booked_k1]['detail'][$kDtl]['avail_rooms_to_swap'] = !empty($availRoomsToSwap[$swapKey]) ? $availRoomsToSwap[$swapKey] : false; } } }