diff --git a/admin/themes/default/sass/controllers/_products.sass b/admin/themes/default/sass/controllers/_products.sass index b7302f7d13..ee8e97e75b 100644 --- a/admin/themes/default/sass/controllers/_products.sass +++ b/admin/themes/default/sass/controllers/_products.sass @@ -1,2 +1,117 @@ #leave_bprice margin-left: 0px + +#deactiveDatesModal + .fc-popover-body .fc-daygrid-event-harness + .fc-event-end, .fc-event-start + margin-left: unset + margin-right: unset + .fc-popover.fc-more-popover + max-width: 450px + .fc-daygrid-event + box-shadow: 0px 1px 2px 0px rgba(171, 171, 171, 0.3) + border-radius: 4px + .fc-daygrid-day-bg + min-height: 40px + .fc-h-event.fc-event + border-color: #FFFFFF + background: #FFFFFF + .fc-h-event .fc-event-main + color: #333333 + .fc-h-event.fc-event.calendar_hover_highlight + border-color: #CAE4FF + background: #CAE4FF + .fc-event-title-container + cursor: pointer + padding: 4px + .highlight-event-day + background: #F5F5F5 + .fc-event-title + text-overflow: ellipsis + white-space: nowrap + max-width: 65% + overflow: hidden + padding-left: 10px + .event_title_container, .fc-popover-body .fc-event-title + border-left: 2px solid red + #disable_dates_form + .date_from_container, .date_to_container + .ui-datepicker + position: absolute + top: 62px + left: 5px + position: relative + textarea + border-radius: 4px + .form-group + .control-label + margin-bottom: 5px + .btn + border-radius: 5px + .modal-title + .add_disable_dates, .close + margin-right: 10px + .remove_disable_dates + margin-right: 20px + .close + line-height: 2 + .disable_dates_title + .disable_dates_room_num + color: grey + display: inline-block + font-weight: 600 + line-height: 2 + .delete_disable_dates + margin-left: 10px + .delete_disable_dates:hover , .edit_disable_dates:hover + cursor: pointer + .tooltip_info_block + .tooltip_title + margin: 10px 5px + font-weight: 600 + min-width: 340px + .tooltip_container + .tooltip_title + .close + opacity: 0.6 + padding-bottom: 5px + font-weight: 600 + .tooltip_label + font-weight: 400 + color: #888888 + margin-bottom: 5px + .tooltip_event_title + border: 1px solid #D9D9D9 + border-radius: 6px + padding: 5px 10px + .tooltip_content + .tooltip_reason + word-wrap: break-word + max-height: 200px + overflow: auto + .tooltip_reason_container, .id_event + margin-top: 10px + &> div + display: inline-block + .row + margin-left: 0 + margin-right: 0 + ul.disable_dates_actions + .btn + text-align: left + display: block + border: none + padding-left: 0 + margin-bottom: 0 + list-style-type: none + font-size: 14px + z-index: 99999 + position: absolute + min-width: 450px + max-width: 450px + background: #fff + box-shadow: 0 8px 25px -2px rgba(0,0,0,0.20) + padding: 15px + border-radius: 6px + .tooltip_action_block + min-width: unset \ No newline at end of file diff --git a/admin/themes/default/sass/partials/_rtl.sass b/admin/themes/default/sass/partials/_rtl.sass index 7fd16f2065..c2e7a24bb0 100644 --- a/admin/themes/default/sass/partials/_rtl.sass +++ b/admin/themes/default/sass/partials/_rtl.sass @@ -124,3 +124,25 @@ html ul#ellipsis_submenu left: 0 right: unset + +#deactiveDatesModal + .tooltip_container + .disable_dates_actions + .btn + text-align: right !important + padding-inline-start: unset + .fc-event-title + border-left: unset !important + border-right: 2px solid red !important + padding-right: 10px !important + padding-left: unset !important + .delete_disable_dates + margin-left: unset !important + margin-right: 10px + .remove_disable_dates + margin-right: 10px !important + margin-left: 20px + .alert.alert-danger + text-align: right !important + .error_message_label + float: right !important diff --git a/admin/themes/default/template/controllers/products/booked_room_date_ranges_list.tpl b/admin/themes/default/template/controllers/products/booked_room_date_ranges_list.tpl index fc157a5b32..2645f8dd8c 100644 --- a/admin/themes/default/template/controllers/products/booked_room_date_ranges_list.tpl +++ b/admin/themes/default/template/controllers/products/booked_room_date_ranges_list.tpl @@ -17,21 +17,17 @@ * @license LICENSE.txt *} -{l s='This room already has bookings for highlighted date range(s):'} - - - - - - - - {foreach from=$booked_rows_list item=booked_row} - - - - - - {/foreach} -
{l s='Order ID'}{l s='Date From'}{l s='Date To'}
- #{$booked_row->id_order|intval} - {dateFormat date=$booked_row->date_from}{dateFormat date=$booked_row->date_to}
+{l s='This room already has a booking for the selected date range.'} +
+
+ {l s='Order ID'}: #{$orderDetails->id_order|intval} +
+
+ {l s='Date From'}: {dateFormat date=$orderDetails->date_from} +
+
+ {l s='Date To'}: {dateFormat date=$orderDetails->date_to} +
+
+
+
diff --git a/admin/themes/default/template/controllers/products/configuration.tpl b/admin/themes/default/template/controllers/products/configuration.tpl index cbb7eaa655..3efc999f22 100644 --- a/admin/themes/default/template/controllers/products/configuration.tpl +++ b/admin/themes/default/template/controllers/products/configuration.tpl @@ -92,10 +92,10 @@ - + - {if $room_info['id_status'] != $rm_status['STATUS_TEMPORARY_INACTIVE']['id'] }{l s='Add Dates'}{else}{l s='View Dates'}{/if} + {if $room_info['id_status'] != $rm_status['STATUS_TEMPORARY_INACTIVE']['id'] }{l s='Add Dates'}{else}{l s='View Dates'}{/if} @@ -175,51 +175,93 @@ {/if} + + {*Disable Dates Model*} - - diff --git a/classes/controller/AdminController.php b/classes/controller/AdminController.php index 0ddbf48942..8115f30b6a 100644 --- a/classes/controller/AdminController.php +++ b/classes/controller/AdminController.php @@ -2938,7 +2938,8 @@ public function init() 'current' => self::$currentIndex, 'token' => $this->token, 'host_mode' => defined('_PS_HOST_MODE_') ? 1 : 0, - 'stock_management' => (int)Configuration::get('PS_STOCK_MANAGEMENT') + 'stock_management' => (int)Configuration::get('PS_STOCK_MANAGEMENT'), + 'language_is_rtl' => $this->context->language->is_rtl )); if ($this->display_header) { diff --git a/controllers/admin/AdminProductsController.php b/controllers/admin/AdminProductsController.php index c0c47c9687..f964391453 100644 --- a/controllers/admin/AdminProductsController.php +++ b/controllers/admin/AdminProductsController.php @@ -502,6 +502,9 @@ public function setMedia() $this->addJs(__PS_BASE_URI__.$this->admin_webpath.'/themes/'.$bo_theme.'/js/jquery.fileupload-validate.js'); $this->addJs(__PS_BASE_URI__.'js/vendor/spin.js'); $this->addJs(__PS_BASE_URI__.'js/vendor/ladda.js'); + $this->addCSS(_PS_JS_DIR_.'fullcalendar/main.css'); + $this->addJs(_PS_JS_DIR_.'fullcalendar/main.js'); + $this->addJS(_PS_JS_DIR_.'/datatable/jquery.dataTables.min.js'); $this->addJS(_PS_JS_DIR_.'/datatable/dataTables.bootstrap.js'); } @@ -2964,8 +2967,8 @@ public function initFormConfiguration($obj) $room['booked_dates'] = json_encode($bookedDates); if ($room['id_status'] == HotelRoomInformation::STATUS_TEMPORARY_INACTIVE) { - $disabledDates = $objRoomDisableDates->getRoomDisableDates($room['id']); - $room['disable_dates_json'] = json_encode($disabledDates); + $disableDates = $objRoomDisableDates->getRoomDisableDates($room['id']); + $room['disable_dates_json'] = json_encode($disableDates); } } $data->assign('htl_room_info', $hotelRoomInfo); @@ -2977,6 +2980,7 @@ public function initFormConfiguration($obj) 'product' => $obj, 'htl_info' => $hotelInfo, 'rm_status' => $roomStatus, + 'locale' => $this->context->language->iso_code ) ); } else { @@ -3461,50 +3465,6 @@ public function ajaxProcessDeleteRoomTypeLengthOfStayRestriction() } } - public function validateDisableDateRanges($disableDates, $roomIndex, $idRoom) - { - if (is_array($disableDates) && count($disableDates)) { - foreach ($disableDates as $disable_key => $disableDate) { - if (!$disableDate['date_to'] && !$disableDate['date_from']) { - unset($disableDates[$disable_key]); - } elseif (!Validate::isDate($disableDate['date_from']) || !Validate::isDate($disableDate['date_to'])) { - $this->errors[] = sprintf( - Tools::displayError('Please add valid disable dates for room %s.'), - $roomIndex - ); - } elseif (($disableDate['date_from'] && !$disableDate['date_to']) || (!$disableDate['date_from'] && $disableDate['date_to'])) { - $this->errors[] = sprintf( - Tools::displayError('Please fill date from and date to for disable dates for room %s.'), - $roomIndex - ); - } else { - $objHotelBookingDetail = new HotelBookingDetail(); - foreach ($disableDates as $key => $disDate) { - if ($key != $disable_key) { - if ((($disableDate['date_from'] < $disDate['date_from']) && ($disableDate['date_to'] <= $disDate['date_from'])) || (($disableDate['date_from'] > $disDate['date_from']) && ($disableDate['date_from'] >= $disDate['date_to']))) { - // continue - } else { - $this->errors[] = sprintf( - Tools::displayError('Disable dates are conflicting for room %s. Please add non-conflicting dates.'), - $roomIndex - ); - } - } - // check if room has booking for current date range - if ($objHotelBookingDetail->chechRoomBooked($idRoom, $disDate['date_from'], $disDate['date_to'])) { - $this->errors[] = sprintf( - Tools::displayError('The room %s already has bookings for selected disable dates. Please reselect disable dates.'), - $roomIndex - ); - } - } - } - } - } else { - $this->errors[] = sprintf(Tools::displayError('Please add disable dates for room %s.'), $roomIndex); - } - } - public function processConfiguration() { // Check if save of configuration tab is submitted @@ -3536,31 +3496,12 @@ public function processConfiguration() $objHotelRoomInfo->id_status = $roomInfo['id_status']; $objHotelRoomInfo->floor = $roomInfo['floor']; $objHotelRoomInfo->comment = $roomInfo['comment']; - if ($objHotelRoomInfo->save()) { - $idRoom = $objHotelRoomInfo->id; - if ($roomInfo['id_status'] == HotelRoomInformation::STATUS_TEMPORARY_INACTIVE) { - $objHotelRoomDisableDates = new HotelRoomDisableDates(); - $objHotelRoomDisableDates->deleteRoomDisableDates($idRoom); - - $disableDates = json_decode($roomInfo['disable_dates_json'], true); - foreach ($disableDates as $disableDate) { - $objHotelRoomDisableDates = new HotelRoomDisableDates(); - $objHotelRoomDisableDates->id_room_type = $id_product; - $objHotelRoomDisableDates->id_room = $idRoom; - $objHotelRoomDisableDates->date_from = $disableDate['date_from']; - $objHotelRoomDisableDates->date_to = $disableDate['date_to']; - $objHotelRoomDisableDates->reason = $disableDate['reason']; - $objHotelRoomDisableDates->add(); - } - - Hook::exec( - 'actionRoomDisableDatesAddAfter', - array( - 'room_info' => $roomInfo, - 'disable_dates' => $disableDates - ) - ); - } + if ($objHotelRoomInfo->save() + && $objHotelRoomInfo->id_status != HotelRoomInformation::STATUS_TEMPORARY_INACTIVE + ) { + // directly deleting the dates since we are validating the dates using hooks before this. + $objRoomDisableDates = new HotelRoomDisableDates(); + $objRoomDisableDates->deleteRoomDisableDates($objHotelRoomInfo->id); } } } @@ -3597,7 +3538,6 @@ public function validateConfigurationPostData() if ($roomInfo['floor'] && !Validate::isGenericName($roomInfo['floor'])) { $this->errors[] = sprintf(Tools::displayError('Invalid floor for room %s.'), $roomIndex); } - if ($roomInfo['id_status'] == HotelRoomInformation::STATUS_INACTIVE) { $objHotelRoomInformation = new HotelRoomInformation(); if (count($objHotelRoomInformation->getFutureBookings($roomInfo['id']))) { @@ -3605,10 +3545,13 @@ public function validateConfigurationPostData() } } elseif ($roomInfo['id_status'] == HotelRoomInformation::STATUS_TEMPORARY_INACTIVE) { $disableDates = json_decode($roomInfo['disable_dates_json'], true); - if ($roomInfo['disable_dates_json'] !== 0) { - $this->validateDisableDateRanges($disableDates, $roomInfo['room_num'], $roomInfo['id']); + if (!is_array($disableDates) + || !count($disableDates) + ) { + $this->errors[] = sprintf(Tools::displayError('Please add disable dates for room %s.'), $roomIndex); } } + Hook::exec('actionValidateRoomInformation', array('room_information', $roomInfo)); } } else { $this->errors[] = Tools::displayError('Please add at least one room.'); @@ -5024,76 +4967,196 @@ public function ajaxProcessGetIdHotelByIdProduct() die(json_encode($response)); } - public function ajaxProcessValidateDisableDates() + public function assignAlerts() { - $response = array('status' => false); + $this->context->smarty->assign(array( + 'errors' => $this->errors, + 'confirmations' => $this->confirmations, + 'warnings' => $this->warnings + )); + } + public function ajaxProcessSubmitDisableDates() + { + $response = array('status' => false); $idRoom = (int) Tools::getValue('id_room'); - $disableDates = Tools::getValue('disable_dates'); - - $rowsToHighlight = array(); - $bookedRows = array(); - if (is_array($disableDates) && count($disableDates)) { - foreach ($disableDates as $key => $dateRange) { - if (!Validate::isDate($dateRange['date_from']) || !Validate::isDate($dateRange['date_to'])) { - $this->errors[] = $this->l('Some dates are missing. Please select all the date ranges.'); - $rowsToHighlight[] = $key; + $dateFrom = Tools::getValue('date_from'); + $dateTo = Tools::getValue('date_to'); + $idProduct = Tools::getValue('id_product'); + if (!Validate::isDate($dateFrom) || !Validate::isDate($dateTo)) { + $this->errors[] = $this->l('Please select a valid date range to temporary disable this room'); + } else if ($idRoom && Validate::isLoadedObject($objHotelRoomInfo = new HotelRoomInformation((int) $idRoom))) { + $dateTo = date('Y-m-d', strtotime($dateTo)); + $objHotelBookingDetail = new HotelBookingDetail(); + if ($bookingRow = $objHotelBookingDetail->chechRoomBooked($idRoom, $dateFrom, $dateTo)) { + $orderDetails = new HotelBookingDetail($bookingRow['id']); + $this->context->smarty->assign(array( + 'link' => $this->context->link, + 'orderDetails' => $orderDetails, + )); + + $this->errors[] = $this->context->smarty->fetch('controllers/products/booked_room_date_ranges_list.tpl'); + } else { + if (!Tools::getValue('id_disable_date') + || !Validate::isLoadedObject($objHotelRoomDisableDates = new HotelRoomDisableDates((int) Tools::getValue('id_disable_date'))) + ) { + $objHotelRoomDisableDates = new HotelRoomDisableDates(); + $objHotelRoomDisableDates->id_room_type = $idProduct; + $objHotelRoomDisableDates->id_room = $idRoom; } - } - if (!count($this->errors)) { - foreach ($disableDates as $keyOuter => $dateRangeOuter) { - foreach ($disableDates as $keyInner => $dateRangeInner) { - if ($keyInner != $keyOuter) { - if ((($dateRangeOuter['date_from'] >= $dateRangeInner['date_from']) && ($dateRangeOuter['date_from'] < $dateRangeInner['date_to'])) - || (($dateRangeInner['date_from'] >= $dateRangeOuter['date_from']) && ($dateRangeInner['date_from'] < $dateRangeOuter['date_to'])) - ) { - $this->errors[] = $this->l('Some dates are conflicting with each other. Please check and reselect the date ranges.'); - $rowsToHighlight[] = $keyOuter; - $rowsToHighlight[] = $keyInner; - } - } + $objHotelRoomDisableDates->date_from = $dateFrom; + $objHotelRoomDisableDates->date_to = $dateTo; + $objHotelRoomDisableDates->reason = trim(Tools::getValue('reason')); + $objHotelRoomInfo->id_status = HotelRoomInformation::STATUS_TEMPORARY_INACTIVE; + if ($objHotelRoomInfo->save() + && $objHotelRoomDisableDates->save() + ) { + $response['id_disable_date'] = $objHotelRoomDisableDates->id; + $response['event_title'] = $this->l('Disabled'); + if (Tools::getValue('id_disable_date')) { + $this->confirmations[] = $this->l('Successfully updated.'); + } else { + $this->confirmations[] = $this->l('Disable date added successfully.'); } + } else { + $this->errors[] = Tools::displayError('An error occurred while trying to save the disable dates.'); } } + } else if (!$idRoom) { + $this->errors[] = Tools::displayError('You need to save this room first before adding any disabled dates.'); + } - if (!count($this->errors)) { - if ($idRoom) { - $objHotelBookingDetail = new HotelBookingDetail(); - foreach ($disableDates as $key => $dateRange) { - if ($bookingRow = $objHotelBookingDetail->chechRoomBooked($idRoom, $dateRange['date_from'], $dateRange['date_to'])) { - $bookedRows[] = new HotelBookingDetail($bookingRow['id']); - $rowsToHighlight[] = $key; - } + + if ($this->errors) { + $this->errors = array_unique($this->errors); + $response['status'] = false; + } else { + $response['status'] = true; + } + + $this->assignAlerts(); + $response['msg'] = $this->context->smarty->fetch('alerts.tpl'); + $this->ajaxDie(json_encode($response)); + } + + public function ajaxProcessRemoveDisableDatesInDateRange() + { + $response = array('status' => false); + $idRoom = (int) Tools::getValue('id_room'); + $dateFrom = date('Y-m-d', strtotime(Tools::getValue('date_from'))); + $dateTo = date('Y-m-d', strtotime(Tools::getValue('date_to'))); + if (!Tools::getValue('date_from') || !Tools::getValue('date_to')) { + $this->errors[] = Tools::displayError('Please select valid date range.'); + } else { + $objRoomDisableDates = new HotelRoomDisableDates(); + $params = array( + 'date_from' => $dateFrom, + 'date_to' => $dateTo, + 'id_room' => $idRoom + ); + Hook::exec('actionRoomDisableDatesRemoveBefore', array('disable_dates', $params)); + if (empty($this->errors) + && ($disableDates = $objRoomDisableDates->checkIfRoomAlreadyDisabled($params)) + ) { + $newDates = array(); + foreach ($disableDates as $disableDatesInfo) { + $objRoomDisableDates = new HotelRoomDisableDates($disableDatesInfo['id']); + $newDates[0]['date_from'] = $disableDatesInfo['date_from']; + $newDates[0]['date_to'] = $params['date_from']; + + $newDates[1]['date_from'] = $params['date_to']; + $newDates[1]['date_to'] = date('Y-m-d', strtotime($disableDatesInfo['date_to'])); + + if (strtotime($newDates[0]['date_from']) >= strtotime($newDates[0]['date_to'])) { + unset($newDates[0]); } + + if (strtotime($newDates[1]['date_from']) >= strtotime($newDates[1]['date_to'])) { + unset($newDates[1]); + } + + foreach ($newDates as $newDate) { + $objNewDisableDates = new HotelRoomDisableDates(); + $objNewDisableDates->id_room_type = $objRoomDisableDates->id_room_type; + $objNewDisableDates->id_room = $objRoomDisableDates->id_room; + $objNewDisableDates->date_from = $newDate['date_from']; + $objNewDisableDates->date_to = $newDate['date_to']; + $objNewDisableDates->reason = $objRoomDisableDates->reason; + $objNewDisableDates->save(); + } + + $objRoomDisableDates->delete(); } + } else if (!$disableDates) { + $this->errors[] = Tools::displayError('Room is not disabled for the selected date range.'); } } - if (count($bookedRows)) { - $this->context->smarty->assign(array( - 'link' => $this->context->link, - 'booked_rows_list' => $bookedRows, - )); + if (count($this->errors)) { + $response['status'] = false; + } else { + $response['status'] = true; + $this->confirmations[] = $this->l('Removed successfully.'); + if (!($disableDates = $objRoomDisableDates->getRoomDisableDates($idRoom))) { + $objRoomInfo = new HotelRoomInformation($idRoom); + $objRoomInfo->id_status = HotelRoomInformation::STATUS_ACTIVE; + $objRoomInfo->save(); + } - $this->errors[] = $this->context->smarty->fetch('controllers/products/booked_room_date_ranges_list.tpl'); + $response['disable_dates'] = $disableDates; } - $this->errors = array_unique($this->errors); - $rowsToHighlight = array_values(array_unique($rowsToHighlight)); + $this->assignAlerts(); + $response['msg'] = $this->context->smarty->fetch('alerts.tpl'); + $this->ajaxDie(json_encode($response)); + } - if (!count($this->errors)) { + public function ajaxProcessGetDisableDates() + { + $response['status'] = false; + if (($idRoom = Tools::getValue('id_room')) + && Validate::isLoadedObject(new HotelRoomInformation($idRoom)) + ) { + $objRoomDisableDates = new HotelRoomDisableDates(); + $disableDates = $objRoomDisableDates->getRoomDisableDates($idRoom); $response['status'] = true; + $response['disable_dates'] = $disableDates; + } + + $this->ajaxDie(json_encode($response)); + } + + public function ajaxProcessDeleteDisableDate() + { + $response = array('status' => false); + $idDisableDate = Tools::getValue('id_disable_date'); + Hook::exec('actionDisableDateDeleteBefore', array('id_disable_date', $idDisableDate)); + if (empty($this->errors) + && (int) $idDisableDate + && Validate::isLoadedObject($objRoomDisableDates = new HotelRoomDisableDates($idDisableDate)) + ) { + $idRoom = $objRoomDisableDates->id_room; + if (!$objRoomDisableDates->delete()) { + $this->errors[] = Tools::displayError('An error occurred while trying to perform this operation.'); + } else if (!$objRoomDisableDates->getRoomDisableDates($idRoom)) { + $objRoomInfo = new HotelRoomInformation($idRoom); + $objRoomInfo->id_status = HotelRoomInformation::STATUS_ACTIVE; + $objRoomInfo->save(); + } } else { - $this->context->smarty->assign(array( - 'errors' => $this->errors, - )); + $this->errors[] = Tools::displayError('Invalid request'); + } - $response['errors'] = $this->context->smarty->fetch('alerts.tpl'); - $response['rows_to_highlight'] = $rowsToHighlight; + if (count($this->errors)) { $response['status'] = false; + } else { + $response['status'] = true; + $this->confirmations[] = $this->l('Removed successfully'); } + $this->assignAlerts(); + $response['msg'] = $this->context->smarty->fetch('alerts.tpl'); $this->ajaxDie(json_encode($response)); } diff --git a/modules/hotelreservationsystem/classes/HotelRoomDisableDates.php b/modules/hotelreservationsystem/classes/HotelRoomDisableDates.php index 8890ef2e00..a2f90c4a7f 100644 --- a/modules/hotelreservationsystem/classes/HotelRoomDisableDates.php +++ b/modules/hotelreservationsystem/classes/HotelRoomDisableDates.php @@ -45,7 +45,21 @@ class HotelRoomDisableDates extends ObjectModel public function getRoomDisableDates($id_room) { - return Db::getInstance()->executeS('SELECT `date_from`, `date_to`, `reason` FROM `'._DB_PREFIX_.'htl_room_disable_dates` WHERE `id_room`='.(int)$id_room); + $objModule = Module::getInstanceByName('hotelreservationsystem'); + $disabledDates = Db::getInstance()->executeS('SELECT `id`, `date_from`, `date_to`, `reason`, `date_add`, + NULL AS `id_event`, NULL AS `event_url`, "'.$objModule->l('Disabled', 'HotelRoomDisableDates').'" AS `event_title`, + 1 AS `is_editable`, 1 AS `is_deletable` + FROM `'._DB_PREFIX_.'htl_room_disable_dates` WHERE `id_room`='.(int) $id_room + ); + + Hook::exec('actionRoomDisabledDatesModifier', + array( + 'disable_dates' => &$disabledDates, + 'id_room' => $id_room + ) + ); + + return $disabledDates; } public function checkIfRoomAlreadyDisabled($params)