File "booking_details.php"

Full Path: /home/romayxjt/public_html/wp-content/plugins/vikbooking/admin/helpers/widgets/booking_details.php
File size: 72.21 KB
MIME-type: text/x-php
Charset: utf-8

<?php
/**
 * @package     VikBooking
 * @subpackage  com_vikbooking
 * @author      Alessio Gaggii - E4J srl
 * @copyright   Copyright (C) 2023 E4J srl. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 * @link        https://vikwp.com
 */

defined('ABSPATH') or die('No script kiddies please!');

/**
 * Class handler for admin widget "booking details".
 * 
 * @since 	1.16.5 (J) - 1.6.5 (WP)
 */
class VikBookingAdminWidgetBookingDetails extends VikBookingAdminWidget
{
	/**
	 * The instance counter of this widget. Since we do not load individual parameters
	 * for each widget's instance, we use a static counter to determine its settings.
	 *
	 * @var 	int
	 */
	protected static $instance_counter = -1;

	/**
	 * Class constructor will define the widget name and identifier.
	 */
	public function __construct()
	{
		// call parent constructor
		parent::__construct();

		$this->widgetName = JText::translate('VBMAINORDEREDIT');
		$this->widgetDescr = JText::translate('JSEARCH_TOOLS');
		$this->widgetId = basename(__FILE__, '.php');

		// define widget and icon and style name
		$this->widgetIcon = '<i class="' . VikBookingIcons::i('address-card') . '"></i>';
		$this->widgetStyleName = 'light-blue';
	}

	/**
	 * Preload the necessary assets.
	 * 
	 * @return 	void
	 */
	public function preload()
	{
		// load assets for contextual menu
		$this->vbo_app->loadContextMenuAssets();

		// JS lang def
		JText::script('VBODASHSEARCHKEYS');
		JText::script('VIKLOADING');
		JText::script('VBDASHUPRESONE');
		JText::script('VBOCHANNEL');
		JText::script('VBCOUPON');
		JText::script('VBCUSTOMERNOMINATIVE');
		JText::script('VBO_COPY');
		JText::script('VBO_COPIED');
		JText::script('VBO_CONF_RM_OVERBOOKING_FLAG');
	}

	/**
	 * Custom method for this widget only to load the booking details.
	 * The method is called by the admin controller through an AJAX request.
	 * The visibility should be public, it should not exit the process, and
	 * any content sent to output will be returned to the AJAX response.
	 * In this case we return an array because this method requires "return":1.
	 */
	public function loadBookingDetails()
	{
		$app = JFactory::getApplication();
		$dbo = JFactory::getDbo();

		$wrapper = $app->input->getString('wrapper', '');

		$booking_key = $app->input->getString('booking_key', '');
		$booking_id  = $app->input->getString('bid', '');

		if (empty($booking_key) && empty($booking_id)) {
			// no bookings found
			VBOHttpDocument::getInstance()->close(400, JText::translate('VBOREPORTSERRNORESERV'));
		}

		$q = $dbo->getQuery(true)
			->select($dbo->qn('o') . '.*')
			->from($dbo->qn('#__vikbooking_orders', 'o'));

		if (!empty($booking_id)) {
			// search by booking ID or OTA booking ID only
			if (preg_match("/^[0-9]+$/", (string)$booking_id)) {
            	// only numbers could be both website and OTA
				$q->where([
					$dbo->qn('o.id') . ' = ' . (int)$booking_id,
					$dbo->qn('o.idorderota') . ' = ' . $dbo->q($booking_id),
				], $glue = 'OR');
			} else {
				// alphanumeric IDs can only belong to an OTA reservation
            	$q->where($dbo->qn('o.idorderota') . ' = ' . $dbo->q($booking_id));
			}
		} else {
			// search by different values
			$q->where(1);
			if (stripos($booking_key, 'id:') === 0) {
				// search by ID or OTA ID
				$seek_parts = explode('id:', $booking_key);
				$seek_value = trim($seek_parts[1]);
				$q->andWhere([
					$dbo->qn('o.id') . ' = ' . $dbo->q($seek_value),
					$dbo->qn('o.idorderota') . ' = ' . $dbo->q($seek_value),
				], $glue = 'OR');
			} elseif (stripos($booking_key, 'otaid:') === 0) {
				// search by OTA Booking ID
				$seek_parts = explode('otaid:', $booking_key);
				$seek_value = trim($seek_parts[1]);
				$q->where($dbo->qn('o.idorderota') . ' = ' . $dbo->q($seek_value));
			} elseif (stripos($booking_key, 'coupon:') === 0) {
				// search by coupon code
				$seek_parts = explode('coupon:', $booking_key);
				$seek_value = trim($seek_parts[1]);
				$q->where($dbo->qn('o.coupon') . ' LIKE ' . $dbo->q("%{$seek_value}%"));
			} elseif (stripos($booking_key, 'name:') === 0) {
				// search by customer nominative
				$seek_parts = explode('name:', $booking_key);
				$seek_value = trim($seek_parts[1]);
				$q->leftJoin($dbo->qn('#__vikbooking_customers_orders', 'co') . ' ON ' . $dbo->qn('co.idorder') . ' = ' . $dbo->qn('o.id'));
				$q->leftJoin($dbo->qn('#__vikbooking_customers', 'c') . ' ON ' . $dbo->qn('c.id') . ' = ' . $dbo->qn('co.idcustomer'));
				$q->where('CONCAT_WS(\' \', ' . $dbo->qn('c.first_name') . ', ' . $dbo->qn('c.last_name') . ') LIKE ' . $dbo->q("%{$seek_value}%"));
			} else {
				// seek for various values
				$q->andWhere([
					$dbo->qn('o.id') . ' = ' . $dbo->q($booking_key),
					$dbo->qn('o.confirmnumber') . ' = ' . $dbo->q($booking_key),
					$dbo->qn('o.idorderota') . ' = ' . $dbo->q($booking_key),
				], $glue = 'OR');
			}
		}

		$q->order($dbo->qn('id') . ' DESC');

		$dbo->setQuery($q, 0, 1);
		$details = $dbo->loadAssoc();

		if (!$details) {
			// no bookings found
			VBOHttpDocument::getInstance()->close(404, JText::translate('VBOREPORTSERRNORESERV'));
		}

		// get customer information
		$cpin = VikBooking::getCPinInstance();
		$customer = $cpin->getCustomerFromBooking($details['id']);
		$customer_bookings_count = 0;
		if ($customer && !empty($customer['country'])) {
			if (is_file(implode(DIRECTORY_SEPARATOR, [VBO_ADMIN_PATH, 'resources', 'countries', $customer['country'] . '.png']))) {
				$customer['country_img'] = '<img src="' . VBO_ADMIN_URI . 'resources/countries/' . $customer['country'] . '.png' . '" title="' . $customer['country'] . '" class="vbo-country-flag vbo-country-flag-left"/>';
			}
			// count customer bookings (any status)
			$customer_bookings_count = $cpin->countCustomerBookings((int) $customer['id']);
		}

		// availability helper
		$av_helper = VikBooking::getAvailabilityInstance();

		// get rooms
		$booking_rooms = VikBooking::loadOrdersRoomsData($details['id']);

		// guests and tariffs information
		$tars 		  = [];
		$guests 	  = [];
		$tot_adults   = 0;
		$tot_children = 0;
		$tot_pets 	  = 0;
		foreach ($booking_rooms as $k => $booking_room) {
			$guests[] = [
				'adults' 	   => $booking_room['adults'],
				'children' 	   => $booking_room['children'],
				'pets' 		   => $booking_room['pets'],
				't_first_name' => $booking_room['t_first_name'],
				't_last_name'  => $booking_room['t_last_name'],
			];

			if (!$k || !$details['split_stay']) {
				$tot_adults   += $booking_room['adults'];
				$tot_children += $booking_room['children'];
				$tot_pets 	  += $booking_room['pets'];
			}

			$q = $dbo->getQuery(true)
				->select('*')
				->from($dbo->qn('#__vikbooking_dispcost'))
				->where($dbo->qn('id') . ' = ' . (int)$booking_room['idtar']);

			$dbo->setQuery($q, 0, 1);
			$tars[($k + 1)] = $dbo->loadAssocList();
		}

		// room stay dates in case of split stay (or modified room nights)
		$room_stay_dates   = [];
		$room_stay_records = [];
		if ($details['split_stay']) {
			if ($details['status'] == 'confirmed') {
				$room_stay_dates = $av_helper->loadSplitStayBusyRecords($details['id']);
			} else {
				$room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $details['id'], []);
			}
		} elseif (!$details['split_stay'] && $details['roomsnum'] > 1 && $details['days'] > 1 && $details['status'] == 'confirmed') {
			// load the occupied stay dates for each room in case they were modified
			$room_stay_records = $av_helper->loadSplitStayBusyRecords($details['id']);
		}

		// currency
		$otacurrency  = !empty($details['channel']) && !empty($details['chcurrency']) ? $details['chcurrency'] : '';
		$currencysymb = VikBooking::getCurrencySymb();

		// channel name and reservation ID
		$otachannel_name = JText::translate('VBORDFROMSITE');
		$otachannel_bid  = '';
		if (!empty($details['channel'])) {
			$channelparts = explode('_', $details['channel']);
			$otachannel = isset($channelparts[1]) && strlen((string)$channelparts[1]) ? $channelparts[1] : ucwords($channelparts[0]);
			$otachannel_name = $otachannel;
			$otachannel_bid  = !empty($details['idorderota']) ? $details['idorderota'] : '';
		}

		// readable dates
		$checkin_info 	  = getdate($details['checkin']);
		$checkin_wday 	  = JText::translate('VB'.strtoupper(substr($checkin_info['weekday'], 0, 3)));
		$checkout_info 	  = getdate($details['checkout']);
		$checkout_wday 	  = JText::translate('VB'.strtoupper(substr($checkout_info['weekday'], 0, 3)));
		$checkin_read_dt  = $checkin_wday . ', ' . implode(' ', [$checkin_info['mday'], VikBooking::sayMonth($checkin_info['mon']), $checkin_info['year'], date('H:i', $details['checkin'])]);
		$checkout_read_dt = $checkout_wday . ', ' . implode(' ', [$checkout_info['mday'], VikBooking::sayMonth($checkout_info['mon']), $checkout_info['year'], date('H:i', $details['checkout'])]);

		// check for special requests
		$special_requests = VBOModelReservation::getInstance($details)->extractSpecialRequests();

		// check for guest messaging
		$messaging_supported = class_exists('VCMChatMessaging');
		$tot_guest_messages  = 0;
		$tot_unread_messages = 0;
		$last_guest_messages = [];
		if ($messaging_supported) {
			$messaging_handler  = VCMChatMessaging::getInstance($details);
			$tot_guest_messages = $messaging_handler->countBookingGuestMessages();
			if ($tot_guest_messages) {
				$tot_unread_messages = $messaging_handler->countBookingGuestMessages($unread = true);
				if (method_exists($messaging_handler, 'loadBookingLatestMessages')) {
					/**
					 * At the moment we save a query and we do not display
					 *  a snapshot of the latest guest messages.
					 */
					// $last_guest_messages = $messaging_handler->loadBookingLatestMessages($details['id'], 0, 5);
				}
			}
		}

		// load the Channel Manager notifications for this booking, if any
		$cm_notifications = [];
		$cm_channels 	  = [];
		if (class_exists('VikChannelManager') && VikChannelManager::isAvailabilityRequest($api_channel = true)) {
			list($cm_notifications, $cm_channels) = $this->loadChannelManagerNotifications($details);
		}

		// check if we got data by clicking on a push/web notification
		$from_push = $this->options()->gotPushData();

		// start output buffering
		ob_start();

		?>
		<div class="vbo-admin-container vbo-admin-container-full vbo-admin-container-compact" data-bookingid="<?php echo $details['id']; ?>">
			<div class="vbo-params-wrap">
				<div class="vbo-params-container">

					<div class="vbo-params-block">
						<div class="vbo-param-container">
							<div class="vbo-param-setting">
							<?php
							$ch_logo_obj  = VikBooking::getVcmChannelsLogo($details['channel'], true);
							$channel_logo = is_object($ch_logo_obj) ? $ch_logo_obj->getSmallLogoURL() : '';
							$custpic_used = false;
							?>
								<div class="vbo-customer-info-box">
									<div class="vbo-customer-info-box-avatar vbo-customer-avatar-medium">
										<span>
										<?php
										if (!empty($channel_logo)) {
											// channel logo has got the highest priority
											?>
											<span class="vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo JHtml::fetch('esc_attr', $otachannel_name); ?>">
												<img src="<?php echo $channel_logo; ?>" onclick="vboWidgetBookDetsOpenBooking('<?php echo $details['id']; ?>');" />
											</span>
											<?php
										} elseif (!empty($customer['pic'])) {
											// customer profile picture
											$custpic_used = true;
											?>
											<span class="vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo JHtml::fetch('esc_attr', $otachannel_name); ?>">
												<img src="<?php echo strpos($customer['pic'], 'http') === 0 ? $customer['pic'] : VBO_SITE_URI . 'resources/uploads/' . $customer['pic']; ?>" onclick="vboWidgetBookDetsOpenBooking('<?php echo $details['id']; ?>');" />
											</span>
											<?php
										} else {
											// we use an icon as fallback
											?>
											<span class="vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo JHtml::fetch('esc_attr', $otachannel_name); ?>" onclick="vboWidgetBookDetsOpenBooking('<?php echo $details['id']; ?>');"><?php VikBookingIcons::e('hotel', 'vbo-dashboard-guest-activity-avatar-icon'); ?></span>
											<?php
										}
										?>
										</span>
									</div>
								</div>
								<span class="label label-info"><?php echo JText::translate('VBDASHUPRESONE') . ' ' . $details['id']; ?></span>
							<?php
							if (!empty($otachannel_bid)) {
								?>
								<span class="label label-info"><?php echo $otachannel_name . ' ' . $otachannel_bid; ?></span>
								<?php
							}
							$status_type = '';
							if (!empty($details['type'])) {
								$status_type = JText::translate('VBO_BTYPE_' . strtoupper($details['type']));
								if (!strcasecmp($details['type'], 'overbooking')) {
									$status_type = '<span class="label label-error vbo-label-nested vbo-label-overbooking" onclick="vboWidgetBookDetsToggleOverbooking(\'' . $wrapper . '\');">' . $status_type . '</span>';
								}
								$status_type .= ' / ';
							}
							$extra_status = $details['refund'] > 0 ? ' / ' . JText::translate('VBO_STATUS_REFUNDED') : '';
							if ($details['status'] == "confirmed") {
								$saystaus = '<span class="label label-success">' . $status_type . JText::translate('VBCONFIRMED') . $extra_status . '</span>';
							} elseif ($details['status'] == "standby") {
								$saystaus = '<span class="label label-warning">' . $status_type . JText::translate('VBSTANDBY') . $extra_status . '</span>';
							} else {
								$saystaus = '<span class="label label-error">' . $status_type . JText::translate('VBCANCELLED') . $extra_status . '</span>';
							}
							echo $saystaus;
							if ($details['closure']) {
								?>
								<span class="label label-error"><?php VikBookingIcons::e('ban'); ?> <?php echo JText::translate('VBDBTEXTROOMCLOSED'); ?></span>
								<?php
							}
							?>
							</div>
						</div>

					<?php
					if (!$customer && !$details['closure'] && !empty($details['custdata'])) {
						?>
						<div class="vbo-param-container">
							<div class="vbo-param-setting"><?php echo $details['custdata']; ?></div>
						</div>
						<?php
					}
					?>

						<div class="vbo-param-container">
							<div class="vbo-param-label">
								<div class="vbo-customer-info-box">
									<div class="vbo-customer-info-box-name">
										<?php echo (isset($customer['country_img']) ? $customer['country_img'] . ' ' : '') . ($customer ? ltrim($customer['first_name'] . ' ' . $customer['last_name']) : JText::translate('VBPVIEWORDERSPEOPLE')); ?>
									</div>
								<?php
								if (!$custpic_used && !empty($customer['pic'])) {
									$customer_name = $customer ? ltrim($customer['first_name'] . ' ' . $customer['last_name']) : '';
									?>
									<div class="vbo-customer-info-box-avatar vbo-customer-avatar-small vbo-widget-bookdets-cpic-zoom">
										<span>
											<img src="<?php echo strpos($customer['pic'], 'http') === 0 ? $customer['pic'] : VBO_SITE_URI . 'resources/uploads/' . $customer['pic']; ?>" data-caption="<?php echo JHtml::fetch('esc_attr', $customer_name); ?>" />
										</span>
									</div>
									<?php
								}
								?>
								</div>
							</div>
							<div class="vbo-param-setting">
							<?php
							$guest_counters = [];
							if ($tot_adults) {
								$guest_counters[] = $tot_adults . ' ' . JText::translate(($tot_adults > 1 ? 'VBMAILADULTS' : 'VBMAILADULT'));
							}
							if ($tot_children) {
								$guest_counters[] = $tot_children . ' ' . JText::translate(($tot_children > 1 ? 'VBMAILCHILDREN' : 'VBMAILCHILD'));
							}
							if ($tot_pets) {
								$guest_counters[] = $tot_pets . ' ' . JText::translate(($tot_pets > 1 ? 'VBO_PETS' : 'VBO_PET'));
							}
							echo implode(', ', $guest_counters);
							?>	
							</div>
						</div>

						<div class="vbo-param-container">
							<div class="vbo-param-label"><?php echo JText::translate('VBPICKUPAT'); ?></div>
							<div class="vbo-param-setting">
								<span class="vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo JHtml::fetch('esc_attr', $checkin_read_dt); ?>"><?php echo date(str_replace("/", $this->datesep, $this->df), $details['checkin']); ?></span>
							</div>
						</div>

						<div class="vbo-param-container">
							<div class="vbo-param-label"><?php echo JText::translate('VBRELEASEAT'); ?></div>
							<div class="vbo-param-setting">
								<span class="vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo JHtml::fetch('esc_attr', $checkout_read_dt); ?>"><?php echo date(str_replace("/", $this->datesep, $this->df), $details['checkout']); ?></span>
								<span>(<?php echo $details['days'] . ' ' . ($details['days'] > 1 ? JText::translate('VBDAYS') : JText::translate('VBDAY')); ?>)</span>
							<?php
							if ($details['split_stay']) {
								?>
								<span class="vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo JHtml::fetch('esc_attr', JText::translate('VBO_SPLIT_STAY')); ?>"><?php VikBookingIcons::e('random'); ?></span>
								<?php
							}
							?>
							</div>
						</div>

						<div class="vbo-param-container">
							<div class="vbo-param-label"><?php echo JText::translate('VBOINVCREATIONDATE'); ?></div>
							<div class="vbo-param-setting">
								<?php echo date(str_replace("/", $this->datesep, $this->df) . ' H:i', $details['ts']); ?>
							</div>
						</div>

					<?php
					if ($special_requests) {
						?>
						<div class="vbo-param-container">
							<div class="vbo-param-setting">
								<blockquote class="vbo-booking-special-requests"><?php echo $special_requests; ?></blockquote>
							</div>
						</div>
						<?php
					}

					/**
					 * Attempt to build the front-site booking link.
					 * 
					 * @since 	1.17.6 (J) - 1.7.6 (WP)
					 */
					$use_sid = empty($details['sid']) && !empty($details['idorderota']) ? $details['idorderota'] : $details['sid'];
					$bestitemid  = VikBooking::findProperItemIdType(['booking'], (!empty($details['lang']) ? $details['lang'] : null));
					$lang_suffix = $bestitemid && !empty($details['lang']) ? '&lang=' . $details['lang'] : '';
					$book_link 	 = VikBooking::externalroute("index.php?option=com_vikbooking&view=booking&sid=" . $use_sid . "&ts=" . $details['ts'] . $lang_suffix, false, (!empty($bestitemid) ? $bestitemid : null));
					// access the model for shortening URLs
					$model = VBOModelShortenurl::getInstance($onlyRouted = true)->setBooking($details);
					$short_url = $model->getShortUrl($book_link);
					?>
						<div class="vbo-param-container">
							<div class="vbo-param-label"><?php echo JText::translate('VBO_CRONJOB_WEBHOOK_TYPE_URL_OPTION'); ?></div>
							<div class="vbo-param-setting">
								<a href="<?php echo $short_url; ?>" target="_blank"><?php echo $short_url; ?></a>
							</div>
						</div>
					<?php

					if ($tot_unread_messages) {
						?>
						<div class="vbo-param-container">
							<div class="vbo-param-label">&nbsp;</div>
							<div class="vbo-param-setting">
								<button type="button" class="btn vbo-config-btn vbo-btn-icon-right" data-hasguestmessages="1">
									<span><?php echo JText::translate('VBO_GUEST_MESSAGING'); ?></span>
									<span class="vbo-bookings-guestmessages-bubble-cont vbo-admin-tipsicon"><i class="<?php echo VikBookingIcons::i('comments'); ?>" data-message-count="<?php echo $tot_unread_messages; ?>"></i></span>
								</button>
							</div>
						</div>
						<?php
					} elseif ($tot_guest_messages) {
						?>
						<div class="vbo-param-container">
							<div class="vbo-param-label">&nbsp;</div>
							<div class="vbo-param-setting">
								<button type="button" class="btn vbo-config-btn vbo-btn-icon-right" data-hasguestmessages="1">
									<span><?php echo JText::translate('VBO_GUEST_MESSAGING'); ?> <?php VikBookingIcons::e('comment-dots'); ?></span>
								</button>
							</div>
						</div>
						<?php
					}
					?>
					</div>

					<div class="vbo-params-block">
					<?php
					foreach ($booking_rooms as $ind => $booking_room) {
						$num = $ind + 1;
						$room_icon = $details['split_stay'] && $ind > 0 ? 'random' : 'bed';
						?>
						<div class="vbo-param-container">
							<div class="vbo-param-label">
								<span><?php VikBookingIcons::e($room_icon); ?> <?php echo $booking_room['room_name']; ?></span>
							<?php
							// room sub-unit
							$room_number = '';
							if (!empty($booking_room['roomindex']) && !$details['closure'] && !empty($booking_room['params'])) {
								$room_params  = json_decode($booking_room['params'], true);
								$arr_features = [];
								if (is_array($room_params) && !empty($room_params['features']) && is_array($room_params['features'])) {
									// parse distinctive features
									foreach ($room_params['features'] as $rind => $rfeatures) {
										if ($rind != $booking_room['roomindex']) {
											continue;
										}
										foreach ($rfeatures as $fname => $fval) {
											if (strlen((string)$fval)) {
												$room_number = '#' . $fval;
												break 2;
											}
										}
									}
								}
							}
							if ($room_number) {
								?>
								<div>
									<small><?php echo $room_number; ?></small>
								</div>
								<?php
							}
							if ($details['roomsnum'] > 1 && !$details['split_stay'] && isset($guests[$ind])) {
								// print guest details for this room
								$room_guest_counters = [];
								if (!empty($guests[$ind]['t_first_name']) || !empty($guests[$ind]['t_last_name'])) {
									$room_guest_counters[] = trim($guests[$ind]['t_first_name'] . ' ' . $guests[$ind]['t_last_name']);
								}
								if ($guests[$ind]['adults']) {
									$room_guest_counters[] = $guests[$ind]['adults'] . ' ' . JText::translate(($guests[$ind]['adults'] > 1 ? 'VBMAILADULTS' : 'VBMAILADULT'));
								}
								if ($guests[$ind]['children']) {
									$room_guest_counters[] = $guests[$ind]['children'] . ' ' . JText::translate(($guests[$ind]['children'] > 1 ? 'VBMAILCHILDREN' : 'VBMAILCHILD'));
								}
								if ($guests[$ind]['pets']) {
									$room_guest_counters[] = $guests[$ind]['pets'] . ' ' . JText::translate(($guests[$ind]['pets'] > 1 ? 'VBO_PETS' : 'VBO_PET'));
								}
								?>
								<div>
									<small><?php echo implode(', ', $room_guest_counters); ?></small>
								</div>
								<?php
							}
							?>
							</div>
							<div class="vbo-param-setting">
								<div class="vbo-widget-bookdets-roomrate">
								<?php
								$active_rplan_id = 0;
								if (!empty($details['pkg']) || $booking_room['cust_cost'] > 0) {
									if (!empty($booking_room['pkg_name'])) {
										// package
										echo $booking_room['pkg_name'];
									} else {
										// custom cost can have an OTA Rate Plan name
										if (!empty($booking_room['otarplan'])) {
											echo ucwords($booking_room['otarplan']);
										} else {
											echo JText::translate('VBOROOMCUSTRATEPLAN');
										}
									}
								} elseif (!empty($tars[$num]) && !empty($tars[$num][0]['idprice'])) {
									$active_rplan_id = $tars[$num][0]['idprice'];
									echo VikBooking::getPriceName($tars[$num][0]['idprice']);
								} elseif (!empty($booking_room['otarplan'])) {
									echo ucwords($booking_room['otarplan']);
								} elseif (!$details['closure']) {
									echo JText::translate('VBOROOMNORATE');
								}
								?>
								</div>
							<?php
							// meals included in the room rate
							if (!empty($booking_room['meals'])) {
								// display included meals defined at room-reservation record
								$included_meals = VBOMealplanManager::getInstance()->roomRateIncludedMeals($booking_room);
							} else {
								// fetch default included meals in the selected rate plan
								$included_meals = $active_rplan_id ? VBOMealplanManager::getInstance()->ratePlanIncludedMeals($active_rplan_id) : [];
							}
							if (!$included_meals && empty($booking_room['meals']) && !empty($details['idorderota']) && !empty($details['channel']) && !empty($details['custdata'])) {
								// attempt to fetch the included meal plans from the raw customer data or OTA reservation and room
								$included_meals = VBOMealplanManager::getInstance()->otaDataIncludedMeals($details, $booking_room);
							}
							if ($included_meals) {
								?>
								<div class="vbo-widget-bookdets-roommeals vbo-wider-badges-wrap">
								<?php
								foreach ($included_meals as $included_meal) {
									?>
									<span class="badge badge-info"><?php echo $included_meal; ?></span>
									<?php
								}
								?>
								</div>
								<?php
							}

							// check for split-stay or modified stay-dates
							if ($details['split_stay'] && $room_stay_dates && isset($room_stay_dates[$ind]) && $room_stay_dates[$ind]['idroom'] == $booking_room['idroom']) {
								// print split stay information for this room
								$room_stay_checkin  = !empty($room_stay_dates[$ind]['checkin_ts']) ? $room_stay_dates[$ind]['checkin_ts'] : $room_stay_dates[$ind]['checkin'];
								$room_stay_checkout = !empty($room_stay_dates[$ind]['checkout_ts']) ? $room_stay_dates[$ind]['checkout_ts'] : $room_stay_dates[$ind]['checkout'];
								$room_stay_nights 	= $av_helper->countNightsOfStay($room_stay_checkin, $room_stay_checkout);
								?>
								<div class="vbo-cal-splitstay-details vbo-bookdet-splitstay-details">
									<div class="vbo-cal-splitstay-dates">
										<span class="vbo-cal-splitstay-room-nights"><?php VikBookingIcons::e('moon'); ?> <?php echo $room_stay_nights . ' ' . ($room_stay_nights > 1 ? JText::translate('VBDAYS') : JText::translate('VBDAY')); ?></span>
										<span class="vbo-cal-splitstay-dates-in"><?php VikBookingIcons::e('plane-arrival'); ?> <?php echo date(str_replace("/", $this->datesep, $this->df), $room_stay_checkin); ?></span>
										<span class="vbo-cal-splitstay-dates-out"><?php VikBookingIcons::e('plane-departure'); ?> <?php echo date(str_replace("/", $this->datesep, $this->df), $room_stay_checkout); ?></span>
									</div>
								</div>
								<?php
							} elseif (!$details['split_stay'] && $room_stay_records && isset($room_stay_records[$ind]) && $room_stay_records[$ind]['idroom'] == $booking_room['idroom']) {
								// print modified stay dates information for this room
								$room_stay_checkin  = $room_stay_records[$ind]['checkin'];
								$room_stay_checkout = $room_stay_records[$ind]['checkout'];
								$room_stay_nights 	= $av_helper->countNightsOfStay($room_stay_checkin, $room_stay_checkout);
								if ($room_stay_checkin != $details['checkin'] || $room_stay_checkout != $details['checkout']) {
									?>
								<div class="vbo-cal-splitstay-details vbo-bookdet-splitstay-details vbo-bookdet-roomdatesmod-details">
									<div class="vbo-cal-splitstay-dates">
										<span class="vbo-cal-splitstay-room-nights"><?php VikBookingIcons::e('moon'); ?> <?php echo $room_stay_nights . ' ' . ($room_stay_nights > 1 ? JText::translate('VBDAYS') : JText::translate('VBDAY')); ?></span>
										<span class="vbo-cal-splitstay-dates-in"><?php VikBookingIcons::e('plane-arrival'); ?> <?php echo date(str_replace("/", $this->datesep, $this->df), $room_stay_checkin); ?></span>
										<span class="vbo-cal-splitstay-dates-out"><?php VikBookingIcons::e('plane-departure'); ?> <?php echo date(str_replace("/", $this->datesep, $this->df), $room_stay_checkout); ?></span>
									</div>
								</div>
								<?php
								}
							}
							?>
							</div>
						</div>
						<?php
					}
					?>
					</div>

				<?php
				if (!$details['closure']) {
					?>
					<div class="vbo-params-block">
						<div class="vbo-param-container">
							<div class="vbo-param-label"><?php echo JText::translate('VBEDITORDERNINE'); ?></div>
							<div class="vbo-param-setting">
								<strong><?php echo ($otacurrency ? "({$otacurrency}) " : '') . $currencysymb . ' ' . VikBooking::numberFormat($details['total']); ?></strong>
							</div>
						</div>
					<?php
					if ($details['totpaid'] > 0) {
						?>
						<div class="vbo-param-container">
							<div class="vbo-param-label"><?php echo JText::translate('VBPEDITBUSYTOTPAID'); ?></div>
							<div class="vbo-param-setting">
								<span><?php echo $currencysymb . ' ' . VikBooking::numberFormat($details['totpaid']); ?></span>
							</div>
						</div>
						<?php
					}
					?>
					</div>
					<?php
				}

					// check for administrator notes
					if (!empty($details['adminnotes'])) {
						?>
					<div class="vbo-params-fieldset">
						<div class="vbo-params-fieldset-label"><?php echo JText::translate('VBADMINNOTESTOGGLE'); ?></div>
						<div class="vbo-params-block">
							<div class="vbo-param-container">
								<div class="vbo-param-setting">
									<blockquote class="vbo-booking-admin-notes"><?php echo nl2br($details['adminnotes']); ?></blockquote>
								</div>
							</div>
						</div>
					</div>
						<?php
					}

					/**
					 * Check if the booking has got some tasks assigned.
					 * 
					 * @since 	1.18.0 (J) - 1.8.0 (WP)
					 */
					$taskManager  = VBOFactory::getTaskManager();
					$bookingTasks = VBOTaskModelTask::getInstance()->filterItems(['id_order' => $details['id']]);
					if ($bookingTasks) {
						?>
					<div class="vbo-params-fieldset vbo-widget-bookdets-tasks-list">
						<div class="vbo-params-fieldset-label"><?php echo JText::translate('VBO_TASK_MANAGER'); ?></div>
						<div class="vbo-bookingdet-tasks-list">
						<?php
						foreach ($bookingTasks as $taskRecord) {
							$task = VBOTaskTaskregistry::getInstance((array) $taskRecord);
							?>
							<div class="vbo-bookingdet-task-details" data-task-id="<?php echo $task->getID(); ?>">
								<div class="vbo-bookingdet-task-detail" data-type="info">
									<span class="vbo-bookingdet-task-title"><?php echo $task->getTitle(); ?></span>
									<span class="vbo-bookingdet-task-sub-title"><?php echo $task->getAreaName($task->getAreaID()); ?></span>
								</div>
							<?php
							$taskUnreadMessages = (bool) $task->get('hasUnreadMessages', false);
							if ($taskUnreadMessages) {
								?>
								<div class="vbo-bookingdet-task-detail" data-type="unread-messages">
									<span class="unread-message-dot"><?php VikBookingIcons::e('comment'); ?></span>
								</div>
								<?php
							}
							if ($taskManager->statusTypeExists($task->getStatus())) {
								$taskStatus = $taskManager->getStatusTypeInstance($task->getStatus());
								?>
								<div class="vbo-bookingdet-task-detail" data-type="status">
									<span class="vbo-tm-task-status-badge vbo-tm-color <?php echo $taskStatus->getColor(); ?>" data-status="<?php echo JHtml::fetch('esc_attr', $taskStatus->getEnum()); ?>"><?php echo $taskStatus->getName(); ?></span>
								</div>
								<?php
							}
							?>
							</div>
							<?php
						}
						?>
						</div>
					</div>
						<?php
					}

					// check if this booking has got a reminder
					$reminders_helper = VBORemindersHelper::getInstance();
					$has_reminders 	  = $reminders_helper->bookingHasReminder($details['id']);
					$few_reminders 	  = [];
					if ($has_reminders) {
						$few_reminders = $reminders_helper->loadReminders([
							'idorder' 	=> $details['id'],
							'onlyorder' => 1,
							'completed' => 1,
							'expired' 	=> 1,
						], 0, 5);
					}
					?>
					<div class="vbo-params-fieldset">
						<div class="vbo-params-fieldset-label"><?php echo JText::translate('VBO_W_REMINDERS_TITLE'); ?></div>
						<div class="vbo-params-block">
						<?php
						foreach ($few_reminders as $reminder) {
							$diff_data = [];
							if (!empty($reminder->duedate)) {
								// calculate distance to expiration date from today
								$diff_data = $reminders_helper->relativeDatesDiff($reminder->duedate);
							}
							?>
							<div class="vbo-param-container">
								<div class="vbo-param-setting">
									<div class="vbo-widget-reminders-record-info">
										<div class="vbo-widget-reminders-record-txt">
											<div class="vbo-widget-reminder-title"><?php echo htmlspecialchars($reminder->title); ?></div>
											<?php
											if (!empty($reminder->descr)) {
												?>
											<div class="vbo-widget-reminder-descr"><?php echo htmlspecialchars($reminder->descr); ?></div>
												<?php
											}
											?>
										</div>
										<div class="vbo-widget-reminders-record-due">
										<?php
										if (!empty($reminder->duedate)) {
											?>
											<div class="vbo-widget-reminders-record-due-datetime">
												<span class="vbo-widget-reminders-record-due-date">
													<span title="<?php echo $reminder->duedate; ?>"><?php echo $diff_data['relative']; ?></span>
												</span>
											<?php
											if ($reminder->usetime) {
												?>
												<span class="vbo-widget-reminders-record-due-time">
													<span><?php echo $diff_data['date_a']->format('H:i'); ?></span>
												</span>
												<?php
											}
											?>
											</div>
											<?php
										}
										?>
										</div>
									</div>
								</div>
							</div>
							<?php
						}
						?>
							<div class="vbo-param-container">
								<div class="vbo-param-label">&nbsp;</div>
								<div class="vbo-param-setting">
								<?php
								if ($has_reminders) {
									?>
									<button type="button" class="btn vbo-config-btn" data-hasreminders="1"><?php VikBookingIcons::e('bell'); ?> <?php echo JText::translate('VBO_SEE_ALL'); ?></button>
									<?php
								} else {
									?>
									<button type="button" class="btn btn-success" data-hasreminders="0"><?php VikBookingIcons::e('plus-circle'); ?> <?php echo JText::translate('VBO_ADD_NEW'); ?></button>
									<?php
								}
								?>
								</div>
							</div>
						</div>
					</div>

					<?php
					if ($customer) {
						/**
						 * Allow to manage the customer documents and display the registration status.
						 * 
						 * @since 	1.16.10 (J) - 1.6.10 (WP)
						 */

						$checked_status = null;
						$checked_cls = 'label-info';
						if (!$details['closure'] && $details['status'] == 'confirmed') {
							switch ($details['checked']) {
								case -1:
									$checked_status = JText::translate('VBOCHECKEDSTATUSNOS');
									$checked_cls = 'label-danger';
									break;
								case 1:
									$checked_status = JText::translate('VBOCHECKEDSTATUSIN');
									$checked_cls = 'label-success';
									break;
								case 2:
									$checked_status = JText::translate('VBOCHECKEDSTATUSOUT');
									$checked_cls = 'label-warning';
									break;
								default:
									if (!empty($customer['pax_data'])) {
										// pre check-in performed via front-end
										$checked_status = JText::translate('VBOCHECKEDSTATUSPRECHECKIN');
									}
									break;
							}
						}
						?>
					<div class="vbo-params-fieldset vbo-widget-bookdets-customer-docs">
						<div class="vbo-params-fieldset-label"><?php echo JText::translate('VBOCUSTOMERDOCUMENTS'); ?></div>
						<div class="vbo-params-block">
							<?php
							if ($customer_bookings_count > 1) {
								// display badge for returning customer
								?>
							<div class="vbo-param-container">
								<div class="vbo-param-label">
									<span class="label label-warning"><?php VikBookingIcons::e('certificate'); ?> <?php echo JText::translate('VBO_CONDTEXT_RULE_RETCUST'); ?></span>
								</div>
								<div class="vbo-param-setting"></div>
							</div>
								<?php
							}
							if ($checked_status) {
								// display the registration status
								?>
							<div class="vbo-param-container">
								<div class="vbo-param-label"><?php echo JText::translate('VBOCHECKEDSTATUS'); ?></div>
								<div class="vbo-param-setting">
									<span class="label <?php echo $checked_cls; ?>"><?php echo $checked_status; ?></span>
								</div>
							</div>
								<?php
							}
							?>
							<div class="vbo-param-container">
								<div class="vbo-param-setting">
								<?php
								/**
								 * Render the customer-dropfiles layout to handle the customer documents.
								 */
								$layout_data = [
									'caller' => 'widget',
									'customer' => $customer,
								];

								// render the permissions layout
								echo JLayoutHelper::render('customer.dropfiles', $layout_data);
								?>
								</div>
							</div>
						</div>
					</div>
						<?php
					}

					// booking history
					$history_obj  = VikBooking::getBookingHistoryInstance($details['id']);
					$history_list = $history_obj->loadHistory();
					if ($history_list) {
						?>
					<div class="vbo-params-fieldset vbo-widget-bookdets-history-list">
						<div class="vbo-params-fieldset-label"><?php echo JText::translate('VBOBOOKHISTORYTAB'); ?></div>
						<div class="vbo-params-block">
						<?php
						$max_display_records = 3;
						foreach ($history_list as $hind => $hist) {
							$html_descr = strpos((string) $hist['descr'], '<') !== false ? $hist['descr'] : nl2br((string) $hist['descr']);
							$text_descr = strip_tags((string) $hist['descr']);
							$text_lines = preg_split("/[\r\n]/", $text_descr);
							$read_more  = false;
							if ($text_lines && count($text_lines) > 1) {
								$first_line = $text_lines[0];
								unset($text_lines[0]);
								if (strlen($first_line) < strlen(implode('', $text_lines))) {
									$text_descr = $first_line;
									$read_more  = true;
								}
							}

							$tip_info = [
								JText::translate('VBOBOOKHISTORYLBLTOT') . ': ' . $currencysymb . ' ' . VikBooking::numberFormat($hist['total']),
								JText::translate('VBOBOOKHISTORYLBLTPAID') . ': ' . $currencysymb . ' ' . VikBooking::numberFormat($hist['totpaid']),
							];
							?>
							<div class="vbo-param-container vbo-widget-bookdets-history-record" style="<?php echo $hind >= $max_display_records ? 'display: none;' : ''; ?>">
								<div class="vbo-param-label">
									<div>
										<span class="vbo-tooltip vbo-tooltip-top vbo-widget-bookdets-history-evname" data-tooltiptext="<?php echo JHtml::fetch('esc_attr', implode(', ', $tip_info)); ?>"><?php echo $history_obj->validType($hist['type'], true); ?></span>
									</div>
									<div>
										<span class="vbo-widget-bookdets-history-evdate"><?php echo JHtml::fetch('date', $hist['dt'], 'Y-m-d H:i:s'); ?></span>
									</div>
								</div>
								<div class="vbo-param-setting vbo-widget-bookdets-history-descr-wrap">
								<?php
								if ($read_more) {
									?>
									<div class="vbo-widget-bookdets-history-descr-txt">
										<span><?php echo $text_descr; ?></span>
										<div>
											<span class="vbo-tooltip vbo-tooltip-top vbo-widget-bookdets-history-descr-more" data-tooltiptext="<?php echo JHtml::fetch('esc_attr', JText::translate('VBO_READ_MORE')); ?>" onclick="vboWidgetBookDetsReadFullHistory(this);">
												<a href="javascript:void(0);">[...]</a>
											</span>
										</div>
									</div>
									<?php
								}
								?>
									<div class="vbo-widget-bookdets-history-descr-html" style="<?php echo $read_more ? 'display: none;' : ''; ?>"><?php echo $html_descr; ?></div>
								</div>
							</div>
							<?php
						}

						if (count($history_list) > $max_display_records) {
							// display button to display all history records
							?>
							<div class="vbo-param-container vbo-widget-bookdets-history-showall">
								<div class="vbo-param-label">&nbsp;</div>
								<div class="vbo-param-setting">
									<button type="button" class="btn vbo-config-btn" onclick="vboWidgetBookDetsHistoryAll('<?php echo $wrapper; ?>');"><?php VikBookingIcons::e('history'); ?> <?php echo JText::translate('VBO_SEE_ALL'); ?></button>
								</div>
							</div>
							<?php
						}
						?>
						</div>
					</div>
						<?php
					}

					// channel manager notifications and channels involved
					if ($cm_notifications) {
						?>
					<div class="vbo-params-fieldset">
						<div class="vbo-params-fieldset-label"><?php echo JText::translate('VBOBOOKHISTORYTCM'); ?></div>
						<div class="vbo-params-block">
						<?php
						$max_display_notifications = 2;
						foreach ($cm_notifications as $notif_ind => $cm_notification) {
							$badge_class = 'success';
							$badge_icon  = 'check-circle';
							if ($cm_notification['type'] == 0) {
								$badge_class = 'error';
								$badge_icon  = 'times-circle';
							} elseif ($cm_notification['type'] == 2) {
								$badge_class = 'warning';
								$badge_icon  = 'exclamation-triangle';
							}
							?>
							<div class="vbo-params-fieldset vbo-widget-bookdets-cmnotifs-record" style="<?php echo $notif_ind >= $max_display_notifications ? 'display: none;' : ''; ?>">
								<div class="vbo-params-fieldset-label">
									<span class="label label-<?php echo $badge_class; ?>">
										<?php VikBookingIcons::e($badge_icon); ?>
										<span><?php echo date(str_replace("/", $this->datesep, $this->df) . ' H:i:s', $cm_notification['ts']); ?></span>
									</span>
								</div>
								<div class="vbo-params-block vbo-params-block-compact">
								<?php
								foreach ($cm_notification['children'] as $channel_key => $channel_notifs) {
									$logo_obj = VikBooking::getVcmChannelsLogo($cm_channels[$channel_key]['name'], true);
									$channel_logo = $logo_obj ? $logo_obj->getSmallLogoURL() : '';
									if (!$channel_logo) {
										continue;
									}
									// build readable channel name
									$raw_ch_name  = (string)$cm_channels[$channel_key]['name'];
									$lower_name   = strtolower($raw_ch_name);
									$lower_name   = preg_replace("/hotel$/", ' hotel', $lower_name);
									$channel_name = ucwords(preg_replace("/api$/", '', $lower_name));
									?>
									<div class="vbo-param-container">
										<div class="vbo-param-label">
											<div class="vbo-customer-info-box">
												<div class="vbo-customer-info-box-avatar vbo-customer-avatar-small">
													<span class="vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo JHtml::fetch('esc_attr', $channel_name); ?>">
														<img src="<?php echo $channel_logo; ?>" />
													</span>
												</div>
											</div>
										</div>
										<div class="vbo-param-setting">
										<?php
										$channel_responses = [];
										foreach ($channel_notifs as $resp_ind => $channel_notif) {
											// build update result string
											$upd_result = (string)$channel_notif['cont'];
											// strip "e4j." from the beginning of the string
											$upd_result = preg_replace("/^e4j./i", '', $upd_result);
											// strip ending hotel code "{hotelid 000000}"
											$upd_result = preg_replace("/\s?\{hotelid\s?[0-9A-Z]+\}$/i", '', $upd_result);

											// default values for extra logging
											$log_result = '';
											$ruid_codes = [];

											// we expect this to be a channel update result
											if ($resp_ind) {
												// parse the response type
												if (preg_match("/^OK\.[A-Z\.]+\.AR_RS/i", $upd_result, $matches)) {
													// successful response example ("OK.Airbnb.AR_RS")
													$upd_result = trim(str_replace($matches[0], "OK {$channel_name}", $upd_result));
												} elseif (preg_match("/^warning\.[A-Z\.]+\.AR_RS/i", $upd_result, $matches)) {
													// warning response example ("warning.Airbnb.AR_RS")
													$upd_result = trim(str_replace($matches[0], $channel_name, $upd_result));
													$log_result = 'warning';
												} elseif (preg_match("/^error\.[A-Z\.]+\.AR_RS/i", $upd_result, $matches)) {
													// error response example ("error.Airbnb.AR_RS")
													$upd_result = trim(str_replace($matches[0], $channel_name, $upd_result));
													$log_result = 'error';
												}

												// check for RUID codes (there could be more than one)
												if (preg_match_all("/RUID: \[.+\]/", $upd_result, $ruid_matches)) {
													$ruid_codes = is_array($ruid_matches[0]) ? $ruid_matches[0] : [];
													foreach ($ruid_matches[0] as $ruid_log) {
														// strip log from update result because it will be displayed separately
														$upd_result = str_replace($ruid_log, '', $upd_result);
													}
												}
											}

											if (in_array($upd_result, $channel_responses)) {
												// do not display duplicate responses from the same channel (i.e. duplicate unexpected notifications)
												continue;
											}

											// register channel response
											$channel_responses[] = $upd_result;

											?>
											<div class="vbo-widget-bookdets-cm-updresult">
											<?php
											// arrow icon
											VikBookingIcons::e(($resp_ind === 0 ? 'long-arrow-right' : 'long-arrow-left'));

											// print a label in case of errors or warning
											if ($log_result) {
												?>
												<span class="label label-<?php echo $log_result == 'warning' ? 'warning' : 'error'; ?>"><?php echo $log_result; ?></span>
												<?php
											}
											?>
												<span class="vbo-widget-bookdets-cm-updresult-log"><?php echo $upd_result; ?></span>
											<?php
											// check for extra logs (RUIDs)
											if ($ruid_codes) {
												?>
												<span class="vbo-widget-bookdets-cm-ruids">
													<button class="btn btn-small vbo-btn-icon-right" type="button" onclick="vboWidgetBookDetsCopyRuids(this);">
														<span class="vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo JHtml::fetch('esc_attr', JText::translate('VBO_COPY')); ?>">RUID <?php VikBookingIcons::e('copy'); ?></span>
													</button>
												</span>
												<textarea class="vbo-textarea-copyable"><?php echo htmlentities(implode("\n", $ruid_codes)); ?></textarea>
												<?php
											}
											?>
											</div>
											<?php
										}

										// check if the channel was notified but did not respond
										if ($channel_responses && (count($channel_responses) % 2) != 0) {
											?>
											<div><?php echo str_repeat('-', 6); ?></div>
											<?php
										}
										?>
										</div>
									</div>
									<?php
								}
								?>
								</div>
							</div>
							<?php
						}

						// check if some notifications were hidden
						if (count($cm_notifications) > $max_display_notifications) {
							// display button to show all channel manager notifications
							?>
							<div class="vbo-param-container vbo-widget-bookdets-cmnotifs-showall">
								<div class="vbo-param-label">&nbsp;</div>
								<div class="vbo-param-setting">
									<button type="button" class="btn vbo-config-btn" onclick="vboWidgetBookDetsCmnotifsAll('<?php echo $wrapper; ?>');"><?php VikBookingIcons::e('network-wired'); ?> <?php echo JText::translate('VBO_SEE_ALL'); ?></button>
								</div>
							</div>
							<?php
						}
						?>
						</div>
					</div>
						<?php
					}
					?>

					<div class="vbo-param-container">
						<div class="vbo-param-setting">
							<button type="button" class="btn vbo-config-btn" onclick="vboWidgetBookDetsOpenBooking('<?php echo $details['id']; ?>');"><?php echo JText::translate('VBOVIEWBOOKINGDET'); ?></button>
							<button type="button" class="btn" onclick="vboWidgetBookDetsOpenBooking('<?php echo $details['id']; ?>', 'edit');"><?php echo JText::translate('VBMODRES'); ?></button>
						</div>
					</div>

				</div>
			</div>
		</div>

		<script type="text/javascript">
			jQuery(function() {

				jQuery('#<?php echo $wrapper; ?>').find('button[data-hasreminders]').on('click', function() {
					// check if the clicked button indicates that there are reminders
					let has_reminders = jQuery(this).attr('data-hasreminders') == '1';

					// render the reminders widget by injecting the proper options
					VBOCore.handleDisplayWidgetNotification({widget_id: 'reminders'}, {
						bid: <?php echo $details['id']; ?>,
						action: has_reminders ? '' : 'add_new',
						completed: has_reminders ? 1 : 0,
						expired: has_reminders ? 1 : 0,
						modal_options: {
							/**
							 * Overwrite modal options for rendering the admin widget.
							 * We need to use a different suffix in case this current widget was
							 * also rendered within a modal, or it would get dismissed in favour
							 * of the newly opened admin widget. Prepending to body is needed
							 * because the reminders widget use the datepicker calendar.
							 */
							suffix: 'widget_modal_inner_reminders',
							body_prepend: true,
						},
					});
				});

				jQuery('#<?php echo $wrapper; ?>').find('button[data-hasguestmessages="1"]').on('click', function() {
					// render the guest messages widget by injecting the proper options
					VBOCore.handleDisplayWidgetNotification({widget_id: 'guest_messages'}, {
						bid: <?php echo $details['id']; ?>,
						modal_options: {
							/**
							 * Overwrite modal options for rendering the admin widget.
							 * We need to use a different suffix in case this current widget was
							 * also rendered within a modal, or it would get dismissed in favour
							 * of the newly opened admin widget.
							 */
							suffix: 'widget_modal_inner_guest_messages',
						},
					});
				});

				jQuery('#<?php echo $wrapper; ?>').find('.vbo-widget-bookdets-cpic-zoom').find('img').on('click', function() {
					// display modal
					VBOCore.displayModal({
						suffix: 'zoom-image',
						title: jQuery(this).attr('data-caption'),
						body: jQuery(this).clone(),
					});
				});

				// check for action button
				var vbo_widget_bookdets_action_btn = jQuery('#<?php echo $wrapper; ?>').find('.vbo-widget-bookdets-actionbtn');
				if (vbo_widget_bookdets_action_btn.length) {
					vbo_widget_bookdets_action_btn.attr('href', vbo_widget_bookdets_action_btn.attr('href').replace('%d', '<?php echo $details['id']; ?>'));
					vbo_widget_bookdets_action_btn.closest('.vbo-widget-push-notification-action').show();
				}

				// register click event on the booking task(s)
				document.querySelector('#<?php echo $wrapper; ?>').querySelectorAll('.vbo-bookingdet-task-details[data-task-id]').forEach((task) => {
					const taskId = task.getAttribute('data-task-id');
					task.addEventListener('click', (e) => {
						// define the modal cancel button
						let cancel_btn = jQuery('<button></button>')
							.attr('type', 'button')
							.addClass('btn')
							.text(<?php echo json_encode(JText::translate('VBANNULLA')); ?>)
							.on('click', () => {
								VBOCore.emitEvent('vbo-tm-edittask-dismiss');
							});

						// define the modal save button
						let save_btn = jQuery('<button></button>')
							.attr('type', 'button')
							.addClass('btn btn-success')
							.text(<?php echo json_encode(JText::translate('VBSAVE')); ?>)
							.on('click', function() {
								// disable button to prevent double submissions
								let submit_btn = jQuery(this);
								submit_btn.prop('disabled', true);

								// start loading animation
								VBOCore.emitEvent('vbo-tm-edittask-loading');

								// get form data
								const taskForm = new FormData(document.querySelector('#vbo-tm-task-manage-form'));

								// build query parameters for the request
								let qpRequest = new URLSearchParams(taskForm);

								// make sure the request always includes the assignees query parameter, even if the list is empty
								if (!qpRequest.has('data[assignees][]')) {
									qpRequest.append('data[assignees][]', []);
								}

								// make sure the request always includes the tags query parameter, even if the list is empty
								if (!qpRequest.has('data[tags][]')) {
									qpRequest.append('data[tags][]', []);
								}

								// make the request
								VBOCore.doAjax(
									"<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=taskmanager.updateTask'); ?>",
									qpRequest.toString(),
									(resp) => {
										// dismiss the modal on success
										VBOCore.emitEvent('vbo-tm-edittask-dismiss');

										// reload the current booking
										vboWidgetBookDetsLoad('<?php echo $wrapper; ?>');
									},
									(error) => {
										// display error message
										alert(error.responseText);

										// re-enable submit button
										submit_btn.prop('disabled', false);

										// stop loading
										VBOCore.emitEvent('vbo-tm-edittask-loading');
									}
								);
							});

						// display modal
						let modalBody = VBOCore.displayModal({
							suffix:         'tm_edittask_modal',
							title:          <?php echo json_encode(JText::translate('VBO_TASK')); ?> + ' #' + taskId,
							extra_class:    'vbo-modal-rounded vbo-modal-taller vbo-modal-large',
							body_prepend:   true,
							lock_scroll:    true,
							escape_dismiss: false,
							footer_left:    cancel_btn,
							footer_right:   save_btn,
							loading_event:  'vbo-tm-edittask-loading',
							dismiss_event:  'vbo-tm-edittask-dismiss',
						});

						// start loading animation
						VBOCore.emitEvent('vbo-tm-edittask-loading');

						// make the request
						VBOCore.doAjax(
							"<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=taskmanager.renderLayout'); ?>",
							{
								type: 'tasks.managetask',
								data: {
									task_id: taskId,
									form_id: 'vbo-tm-task-manage-form',
								},
							},
							(resp) => {
								// stop loading
								VBOCore.emitEvent('vbo-tm-edittask-loading');

								try {
									// decode the response (if needed), and append the content to the modal body
									let obj_res = typeof resp === 'string' ? JSON.parse(resp) : resp;
									modalBody.append(obj_res['html']);
								} catch (err) {
									console.error('Error decoding the response', err, resp);
								}
							},
							(error) => {
								// display error message
								alert(error.responseText);

								// stop loading
								VBOCore.emitEvent('vbo-tm-edittask-loading');
							}
						);
					});
				});

			<?php
			if ($open_task = $this->getOption('task_id', 0)) {
				?>
				document.querySelector('#<?php echo $wrapper; ?> .vbo-bookingdet-task-details[data-task-id="<?php echo $open_task; ?>"]').click();
				<?php
			}
			?>

			});
		<?php
		if ($from_push) {
			// emit the event to read all notifications in the current context when clicking on a push/web notification
			?>
			setTimeout(() => {
				VBOCore.emitEvent('vbo-nc-read-notifications', {
					criteria: {
						group:   '<?php echo !empty($details['idorderota']) && !empty($details['channel']) ? 'otas' : 'website'; ?>',
						idorder: '<?php echo $details['id']; ?>',
					}
				});
			}, 200);
			<?php
		}
		?>
		</script>
		<?php

		// get the HTML buffer
		$html_content = ob_get_contents();
		ob_end_clean();

		// return an associative array of values
		return [
			'html' => $html_content,
		];
	}

	/**
	 * Main method to invoke the widget. Contents will be loaded
	 * through AJAX requests, not via PHP when the page loads.
	 * 
	 * @param 	?VBOMultitaskData 	$data
	 * 
	 * @return 	void
	 */
	public function render(?VBOMultitaskData $data = null)
	{
		// increase widget's instance counter
		static::$instance_counter++;

		// check whether the widget is being rendered via AJAX when adding it through the customizer
		$is_ajax = $this->isAjaxRendering();

		// generate a unique ID for the sticky notes wrapper instance
		$wrapper_instance = !$is_ajax ? static::$instance_counter : rand();
		$wrapper_id = 'vbo-widget-bookdets-' . $wrapper_instance;

		// get permissions
		$vbo_auth_bookings = JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking');
		if (!$vbo_auth_bookings) {
			// display nothing
			return;
		}

		// check multitask data
		$load_bid 			= 0;
		$js_modal_id 		= '';
		$is_modal_rendering = false;
		if ($data) {
			// access Multitask data
			$is_modal_rendering = $data->isModalRendering();
			if ($is_modal_rendering) {
				// get modal JS identifier
				$js_modal_id = $data->getModalJsIdentifier();
			}

			/**
			 * This widget should not get the current ID from Multitask data
			 * to avoid displaying duplicate contents, it should rather get
			 * the ID from the injected options (i.e. new Push notification).
			 */
			$load_bid = $this->options()->fetchBookingId();
		}

		// check if we got data by clicking on a push notification from the ServiceWorker
		$from_push = $this->options()->gotPushData();

		?>
		<div id="<?php echo $wrapper_id; ?>" class="vbo-admin-widget-wrapper" data-instance="<?php echo $wrapper_instance; ?>" data-loadbid="<?php echo $load_bid; ?>">

			<div class="vbo-admin-widget-head">
				<div class="vbo-admin-widget-head-inline">
					<h4><?php echo $this->widgetIcon; ?> <span><?php echo $this->widgetName; ?></span></h4>
				</div>
			</div>

			<div class="vbo-widget-bookdets-wrap">
				<div class="vbo-widget-bookdets-inner">

					<div class="vbo-widget-element-filter-top">
						<div class="btn-group-inline">
							<button type="button" class="btn btn-secondary vbo-context-menu-btn vbo-widget-bookdets-searchtype">
								<span class="vbo-context-menu-ico"><?php VikBookingIcons::e('sort-down'); ?></span>
							</button>
							<input type="text" name="booking_key" value="<?php echo $load_bid ? "id: {$load_bid}" : ''; ?>" placeholder="<?php echo htmlspecialchars(JText::translate('VBOFILTCONFNUMCUST')); ?>" autocomplete="off" />
							<button type="button" class="btn vbo-config-btn" onclick="vboWidgetBookDetsSearch('<?php echo $wrapper_id; ?>');"><?php VikBookingIcons::e('search'); ?> <span class="vbo-widget-bookdets-searchbtn"><?php echo JText::translate('VBODASHSEARCHKEYS'); ?></span></button>
						</div>
					</div>

				<?php
				// check if we need to display the Push notification message
				if ($from_push) {
					// get the title and message
					$push_title   = $this->getOption('title', '');
					$push_message = $this->getOption('message', '');

					// check for notification action and "from"
					$push_action = $this->getOption('action', '');
					$push_from 	 = $this->getOption('from', '');

					// notification severity
					$push_severity = $this->getOption('severity', 'info');

					// replace new line characters with a white space
			        $push_message = preg_replace("/[\r\n]/", ' ', $push_message);

			        // get rid of white space characters (only white space, no new lines or tabs like "\s") used more than once in a row
			        $push_message = preg_replace("/ {2,}/", ' ', $push_message);
					?>
					<div class="vbo-widget-push-notification vbo-widget-push-notification-<?php echo $push_severity; ?>">
					<?php
					if (!empty($push_title)) {
						?>
						<div class="vbo-widget-push-notification-title">
							<strong><?php echo $push_title; ?></strong>
						</div>
						<?php
					}
					if (!empty($push_message)) {
						?>
						<div class="vbo-widget-push-notification-message">
							<span><?php echo $push_message; ?></span>
						</div>
						<?php
					}
					if ($push_action && !strcasecmp($push_from, 'airbnb')) {
						// supported action
						if (!strcasecmp($push_action, 'host_guest_review')) {
							// prepare link to trigger the host-to-guest review action
							?>
						<div class="vbo-widget-push-notification-action" style="display: none;">
							<a class="btn vbo-config-btn vbo-widget-bookdets-actionbtn" href="<?php echo VBOFactory::getPlatform()->getUri()->admin('index.php?option=com_vikbooking&task=editorder&cid[]=%d&notif_action=airbnb_host_guest_review', $xhtml = false); ?>" target="_blank"><?php VikBookingIcons::e('star'); ?> <?php echo JText::translate('VBO_REVIEW_YOUR_GUEST'); ?></a>
						</div>
							<?php
						} elseif (!strcasecmp($push_action, 'new_guest_review')) {
							// prepare link to display the guest review received
							?>
						<div class="vbo-widget-push-notification-action" style="display: none;">
							<a class="btn vbo-config-btn vbo-widget-bookdets-actionbtn" href="<?php echo VBOFactory::getPlatform()->getUri()->admin('index.php?option=com_vikbooking&task=editorder&cid[]=%d&notif_action=see_guest_review', $xhtml = false); ?>" target="_blank"><?php VikBookingIcons::e('star'); ?> <?php echo JText::translate('VBOSEEGUESTREVIEW'); ?></a>
						</div>
							<?php
						}
					}
					?>
					</div>
					<?php
				}
				?>

					<div class="vbo-widget-element-body"></div>

				</div>
			</div>

		</div>
		<?php

		if (static::$instance_counter === 0 || $is_ajax) {
			/**
			 * Print the JS code only once for all instances of this widget.
			 * The real rendering is made through AJAX, not when the page loads.
			 */
			?>
		<a class="vbo-widget-bookdets-basenavuri" href="<?php echo VBOFactory::getPlatform()->getUri()->admin('index.php?option=com_vikbooking&task=editorder&cid[]=%d', $xhtml = false); ?>" style="display: none;"></a>
		<a class="vbo-widget-editbook-basenavuri" href="<?php echo VBOFactory::getPlatform()->getUri()->admin('index.php?option=com_vikbooking&task=editbusy&cid[]=%d', $xhtml = false); ?>" style="display: none;"></a>

		<script type="text/javascript">

			/**
			 * Open the booking details (or edit booking) page for the clicked reservation.
			 */
			function vboWidgetBookDetsOpenBooking(id, edit) {
				var open_url = jQuery((edit ? '.vbo-widget-editbook-basenavuri' : '.vbo-widget-bookdets-basenavuri')).first().attr('href');
				open_url = open_url.replace('%d', id);
				// navigate in a new tab
				window.open(open_url, '_blank');
			}

			/**
			 * Searches for a booking according to input filter.
			 */
			function vboWidgetBookDetsSearch(wrapper, options, bid) {
				var widget_instance = jQuery('#' + wrapper);
				if (!widget_instance.length) {
					return false;
				}

				var booking_key = widget_instance.find('input[name="booking_key"]').val();

				// show loading
				widget_instance.find('.vbo-widget-bookdets-searchbtn').text(Joomla.JText._('VIKLOADING'));

				// the widget method to call
				var call_method = 'loadBookingDetails';

				// make a request to load the booking details
				VBOCore.doAjax(
					"<?php echo $this->getExecWidgetAjaxUri(); ?>",
					{
						widget_id: "<?php echo $this->getIdentifier(); ?>",
						call: call_method,
						return: 1,
						bid: bid,
						booking_key: booking_key,
						_options: options,
						wrapper: wrapper,
						tmpl: "component"
					},
					(response) => {
						// hide loading
						widget_instance.find('.vbo-widget-bookdets-searchbtn').text(Joomla.JText._('VBODASHSEARCHKEYS'));
						try {
							var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
							if (!obj_res.hasOwnProperty(call_method)) {
								widget_instance.find('.vbo-widget-element-body').html('');
								console.error('Unexpected JSON response', obj_res);
								return false;
							}

							// replace HTML with new content
							widget_instance.find('.vbo-widget-element-body').html(obj_res[call_method]['html']);
						} catch(err) {
							console.error('could not parse JSON response', err, response);
						}
					},
					(error) => {
						// hide loading
						widget_instance.find('.vbo-widget-bookdets-searchbtn').text(Joomla.JText._('VBODASHSEARCHKEYS'));
						// display no bookings message
						widget_instance.find('.vbo-widget-element-body').html((error.status != 400 ? '<p class="err">' + error.responseText + '</p>' : ''));
					}
				);
			}

			/**
			 * Attempts to load a booking ID from the injected options.
			 */
			function vboWidgetBookDetsLoad(wrapper, options) {
				var widget_instance = jQuery('#' + wrapper);
				if (!widget_instance.length) {
					return false;
				}

				var load_bid = widget_instance.attr('data-loadbid');
				if (!load_bid || load_bid == '0') {
					return false;
				}

				// load booking
				vboWidgetBookDetsSearch(wrapper, options, load_bid);
			}

			/**
			 * Prepares the input search field with the proper type hint.
			 */
			function vboWidgetBookDetsSearchType(wrapper, search_hint) {
				var widget_instance = jQuery('#' + wrapper);
				if (!widget_instance.length) {
					return false;
				}

				if (!search_hint) {
					search_hint = '';
				} else {
					search_hint += ': ';
				}

				widget_instance.find('input[name="booking_key"]').val(search_hint).focus();
			}

			/**
			 * Displays the full history record description.
			 */
			function vboWidgetBookDetsReadFullHistory(btn) {
				var descr_wrapper = jQuery(btn).closest('.vbo-widget-bookdets-history-descr-wrap');
				descr_wrapper.find('.vbo-widget-bookdets-history-descr-txt').hide();
				descr_wrapper.find('.vbo-widget-bookdets-history-descr-html').show();
			}

			/**
			 * Displays all booking history records.
			 */
			function vboWidgetBookDetsHistoryAll(wrapper) {
				var widget_instance = jQuery('#' + wrapper);
				if (!widget_instance.length) {
					return false;
				}

				// show all records
				widget_instance.find('.vbo-widget-bookdets-history-record').show();

				// hide button
				widget_instance.find('.vbo-widget-bookdets-history-showall').hide('.');
			}

			/**
			 * Display all channel manager notifications.
			 */
			function vboWidgetBookDetsCmnotifsAll(wrapper) {
				var widget_instance = jQuery('#' + wrapper);
				if (!widget_instance.length) {
					return false;
				}

				// show all records
				widget_instance.find('.vbo-widget-bookdets-cmnotifs-record').show();

				// hide button
				widget_instance.find('.vbo-widget-bookdets-cmnotifs-showall').hide('.');
			}

			/**
			 * Copies to clipboard the Booking.com RUID response codes, if any.
			 */
			function vboWidgetBookDetsCopyRuids(btn) {
				var tarea = btn.closest('.vbo-widget-bookdets-cm-updresult').querySelector('.vbo-textarea-copyable');
				VBOCore.copyToClipboard(tarea).then((success) => {
					jQuery(btn).find('.vbo-tooltip').attr('data-tooltiptext', Joomla.JText._('VBO_COPIED') + '!');
				}).catch((err) => {
					alert('Could not copy the logs');
				});
			}

			/**
			 * Toggles the overbooking status-type.
			 */
			function vboWidgetBookDetsToggleOverbooking(wrapper) {
				var widget_instance = jQuery('#' + wrapper);
				if (!widget_instance.length) {
					return false;
				}

				var bid = widget_instance.find('[data-bookingid]').attr('data-bookingid');

				if (confirm(Joomla.JText._('VBO_CONF_RM_OVERBOOKING_FLAG'))) {
					VBOCore.doAjax(
						"<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=bookings.delete_type_flag'); ?>",
						{
							bid: bid,
							flag: 'overbooking',
						},
						(success) => {
							// remove "overbooking" label
							let parent_label = widget_instance.find('.vbo-label-overbooking').parent('.label');
							widget_instance.find('.vbo-label-overbooking').remove();
							parent_label.text(parent_label.text().replace(' / ', ''));

							// turn flag on for the booking modified
							vbo_widget_book_dets_last_mod_bid = bid;
						},
						(error) => {
							alert(error.responseText);
						}
					);
				}
			}

		</script>
			<?php
		}
		?>

		<script type="text/javascript">

			// holds the lastly modified booking ID
			var vbo_widget_book_dets_last_mod_bid = null;

			jQuery(function() {

				// when document is ready, load contents for this widget's instance
				vboWidgetBookDetsLoad('<?php echo $wrapper_id; ?>', <?php echo json_encode($this->getOptions()); ?>);

				// register keyup event for auto-submit
				let input_search = document.querySelector('#<?php echo $wrapper_id; ?> input[name="booking_key"]');
				if (input_search) {
					input_search.addEventListener('keyup', (e) => {
						if (e.key === 'Enter') {
							return vboWidgetBookDetsSearch('<?php echo $wrapper_id; ?>');
						}
						if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
							// match a valid booking ID to increase or decrease
							let search_val = input_search.value;
							let rgx_id = new RegExp(/^(id:)?\s?[0-9]+$/);
							if (search_val && search_val.match(rgx_id)) {
								let rgx_num = new RegExp(/[^0-9]/g);
								let raw_idn = parseInt(search_val.replace(rgx_num, ''));
								if (!isNaN(raw_idn)) {
									// replace value and start search
									input_search.value = search_val.replace(raw_idn, (e.key === 'ArrowUp' ? (raw_idn + 1) : (raw_idn - 1)));
									return vboWidgetBookDetsSearch('<?php echo $wrapper_id; ?>');
								}
							}
						}
					});
				}

				// render context menu
				jQuery('#<?php echo $wrapper_id; ?>').find('.vbo-widget-bookdets-searchtype').vboContextMenu({
					placement: 'bottom-left',
					buttons: [
						{
							icon: '<?php echo VikBookingIcons::i('search'); ?>',
							text: Joomla.JText._('VBDASHUPRESONE'),
							separator: false,
							action: (root, config) => {
								vboWidgetBookDetsSearchType('<?php echo $wrapper_id; ?>', 'id');
							},
						},
						{
							icon: '<?php echo VikBookingIcons::i('search'); ?>',
							text: Joomla.JText._('VBDASHUPRESONE') + ' (' + Joomla.JText._('VBOCHANNEL') + ')',
							separator: false,
							action: (root, config) => {
								vboWidgetBookDetsSearchType('<?php echo $wrapper_id; ?>', 'otaid');
							},
						},
						{
							icon: '<?php echo VikBookingIcons::i('user-tag'); ?>',
							text: Joomla.JText._('VBCOUPON'),
							separator: false,
							action: (root, config) => {
								vboWidgetBookDetsSearchType('<?php echo $wrapper_id; ?>', 'coupon');
							},
						},
						{
							icon: '<?php echo VikBookingIcons::i('user'); ?>',
							text: Joomla.JText._('VBCUSTOMERNOMINATIVE'),
							separator: false,
							action: (root, config) => {
								vboWidgetBookDetsSearchType('<?php echo $wrapper_id; ?>', 'name');
							},
						},
					],
				});

				// subscribe to the multitask-panel-close event to emit the event for the lastly modified booking ID
				document.addEventListener(VBOCore.multitask_close_event, function() {
					if (vbo_widget_book_dets_last_mod_bid) {
						// emit the event with data for anyone who is listening to it
						VBOCore.emitEvent('vbo_booking_modified', {
							bid: vbo_widget_book_dets_last_mod_bid
						});
					}
				});

			<?php
			if ($is_modal_rendering) {
				// focus search input field & register to the event emitted when reminders have changed
				?>
				setTimeout(() => {
					jQuery('#<?php echo $wrapper_id; ?>').find('input[name="booking_key"]').focus();
				}, 400);

				var vbo_widget_bookdets_watch_reminders_fn = (e) => {
					if (!e || !e.detail || !e.detail.hasOwnProperty('bid') || !e.detail['bid']) {
						return;
					}
					let booking_element = jQuery('#<?php echo $wrapper_id; ?>').find('[data-bookingid]').first();
					if (booking_element.length && booking_element.attr('data-bookingid') == e.detail['bid']) {
						// reload current booking details
						vboWidgetBookDetsSearch('<?php echo $wrapper_id; ?>');
					}
				};

				document.addEventListener('vbo_reminders_changed', vbo_widget_bookdets_watch_reminders_fn);

				document.addEventListener(VBOCore.widget_modal_dismissed + '<?php echo $js_modal_id; ?>', (e) => {
					document.removeEventListener('vbo_reminders_changed', vbo_widget_bookdets_watch_reminders_fn);
				});

				// subscribe to the modal-dismissed event to emit the event for the lastly modified booking ID
				document.addEventListener(VBOCore.widget_modal_dismissed + '<?php echo $js_modal_id; ?>', function() {
					if (vbo_widget_book_dets_last_mod_bid) {
						// emit the event with data for anyone who is listening to it
						VBOCore.emitEvent('vbo_booking_modified', {
							bid: vbo_widget_book_dets_last_mod_bid
						});
					}
				});
				<?php
			}
			?>

			});

		</script>

		<?php
	}

	/**
	 * Loads a list of Channel Manager notifications and channels for the given booking.
	 * 
	 * @param 	array 	$booking 	the current booking record.
	 * 
	 * @return 	array 				list of notifications and channels involved.
	 */
	protected function loadChannelManagerNotifications(array $booking)
	{
		$dbo = JFactory::getDbo();

		$channels = [];

		$q = $dbo->getQuery(true)
			->select('*')
			->from($dbo->qn('#__vikchannelmanager_notifications'))
			->where($dbo->qn('from') . ' = ' . $dbo->q('VCM'))
			->where($dbo->qn('idordervb') . ' = ' . (int)$booking['id'])
			->order($dbo->qn('ts') . ' DESC');

		$dbo->setQuery($q);
		$parents = $dbo->loadAssocList();

		foreach ($parents as $k => $parent) {
			$q = $dbo->getQuery(true)
				->select($dbo->qn(['type', 'cont', 'channel']))
				->from($dbo->qn('#__vikchannelmanager_notification_child'))
				->where($dbo->qn('id_parent') . ' = ' . (int)$parent['id'])
				->order($dbo->qn('id') . ' ASC');

			$dbo->setQuery($q);
			$children = $dbo->loadAssocList();

			// set children notifications by grouping them under each channel
			$parents[$k]['children'] = [];

			// fetch the details for each channel involved
			foreach ($children as $ind => $child) {
				if (empty($child['channel'])) {
					continue;
				}

				$channel = VikChannelManager::getChannel($child['channel']);
				if (!$channel) {
					// we don't want to list a notification with no channel details
					unset($children[$ind]);
					continue;
				}

				// set channel details
				$channels[$child['channel']] = $channel;

				if (!isset($parents[$k]['children'][$child['channel']])) {
					// open channel container
					$parents[$k]['children'][$child['channel']] = [];
				}

				// push channel notification details
				$parents[$k]['children'][$child['channel']][] = $child;
			}

			if (!$children) {
				// we don't want a notification with no children (channels notified)
				unset($parents[$k]);
				continue;
			}
		}

		if (!$channels) {
			// unset all notifications in case of no channel details
			$parents = [];
		}

		return [array_values($parents), $channels];
	}
}