# vikbooking/1.8.15/site/views/search/view.html.php

VikBooking Hotel Booking Engine &amp; PMS, version 1.8.15. 1,528 lines.

- Page: https://pluginprobe.com/plugins/vikbooking/1.8.15/code/site/views/search/view.html.php
- Raw: https://pluginprobe.com/plugins/vikbooking/1.8.15/raw/site/views/search/view.html.php
- Modified: 2026-09-14T11:37:40+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/vikbooking/1.8.15/code/site/views/search/view.html.php#L10-L20`.

```php
<?php
/**
 * @package     VikBooking
 * @subpackage  com_vikbooking
 * @author      Alessio Gaggii - e4j - Extensionsforjoomla.com
 * @copyright   Copyright (C) 2018 e4j - Extensionsforjoomla.com. 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!');

jimport('joomla.application.component.view');

class VikbookingViewSearch extends JViewVikBooking
{
	public function display($tpl = null)
	{
		if (!VikBooking::allowBooking()) {
			echo VikBooking::getDisabledBookingMsg();
			return;
		}

		$app = JFactory::getApplication();
		$dbo = JFactory::getDbo();
		$session = JFactory::getSession();
		$vbo_tn = VikBooking::getTranslator();

		$pcheckindate = VikRequest::getString('checkindate', '', 'request');
		$pcheckinm = VikRequest::getString('checkinm', '', 'request');
		$pcheckinh = VikRequest::getString('checkinh', '', 'request');
		$pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
		$pcheckoutm = VikRequest::getString('checkoutm', '', 'request');
		$pcheckouth = VikRequest::getString('checkouth', '', 'request');
		$pcategories = VikRequest::getString('categories', '', 'request');
		$proomsnum = VikRequest::getInt('roomsnum', '', 'request');
		$proomsnum = $proomsnum < 1 ? 1 : $proomsnum;
		$showchildren = VikBooking::showChildrenFront();
		$padults = (array) VikRequest::getVar('adults', []);
		$pchildren = (array) VikRequest::getVar('children', []);
		$ppkg_id = VikRequest::getInt('pkg_id', '', 'request');
		$pcategory_ids = (array) VikRequest::getVar('category_ids', []);

		$nowdf = VikBooking::getDateFormat();
		if ($nowdf == "%d/%m/%Y") {
			$df = 'd/m/Y';
		} elseif ($nowdf == "%m/%d/%Y") {
			$df = 'm/d/Y';
		} else {
			$df = 'Y/m/d';
		}

		$timeopst = VikBooking::getTimeOpenStore();
		if (!(strlen($pcheckinh) > 0) && !(strlen($pcheckouth) > 0) && is_array($timeopst)) {
			$opent = VikBooking::getHoursMinutes($timeopst[0]);
			$closet = VikBooking::getHoursMinutes($timeopst[1]);
			$pcheckinh = $opent[0];
			$pcheckinm = $opent[1];
			$pcheckouth = $closet[0];
			$pcheckoutm = $closet[1];
		}

		// channel manager
		$ch_start_date = VikRequest::getString('start_date', '', 'request');
		$ch_end_date = VikRequest::getString('end_date', '', 'request');
		$ch_num_adults = VikRequest::getInt('num_adults', '', 'request');
		$ch_num_children = VikRequest::getInt('num_children', '', 'request');
		if (!empty($ch_start_date) && !empty($ch_end_date)) {
			if (!empty($ch_num_adults) && $ch_num_adults > 0) {
				$padults = [$ch_num_adults];
			}
			if (!empty($ch_num_children) && $ch_num_children > 0) {
				$pchildren = [$ch_num_children];
			}
			if ($ch_start_date_ts = strtotime($ch_start_date)) {
				if ($ch_end_date_ts = strtotime($ch_end_date)) {
					$pcheckindate = date($df, $ch_start_date_ts);
					$pcheckoutdate = date($df, $ch_end_date_ts);
					if (is_array($timeopst)) {
						$opent = VikBooking::getHoursMinutes($timeopst[0]);
						$closet = VikBooking::getHoursMinutes($timeopst[1]);
						$pcheckinh = $opent[0];
						$pcheckinm = $opent[1];
						$pcheckouth = $closet[0];
						$pcheckoutm = $closet[1];
					} else {
						$pcheckinh = 0;
						$pcheckinm = 0;
						$pcheckouth = 0;
						$pcheckoutm = 0;
					}
				}
			}
		}

		/**
		 * To facilitate the search suggestions to drive more bookings in case of low
		 * availability on the requested dates, we grab all rooms by ignoring their
		 * minimum needed occupancy, and by considering only their maximum capacity.
		 * This can make sense because "solo-rates" (for single-occupancy) are, most
		 * of the times, forced by most OTAs. If the "search suggestions" are enabled
		 * then the minimum occupancy of the rooms will be ignored to guarantee the
		 * completion of a reservation by the guest, which would be booking a greater
		 * room. In terms of pricing and availability nothing changes of course.
		 * 
		 * @since 	1.14.3 (J) - 1.4.3 (WP)
		 */
		$booking_suggestion = VikRequest::getInt('suggestion', 0, 'request');

		/**
		 * It is possible to display some listings as unavailable in case they are fully booked.
		 * If enabled, the unavailable listings will be displayed as long as some are available.
		 * 
		 * @since 	1.17.2 (J) - 1.7.2 (WP)
		 */
		$unavailable_listings = [];

		$arradultsrooms = [];
		$arradultsclause = [];
		$arrpeople = [];
		if ($padults) {
			foreach ($padults as $kad => $adu) {
				$roomnumb = $kad + 1;
				if (strlen($adu)) {
					$numadults = intval($adu);
					if ($numadults >= 0) {
						$arradultsrooms[$roomnumb] = $numadults;
						$arrpeople[$roomnumb]['adults'] = $numadults;
						// build capacity requirement clauses
						$room_requirements = [];
						if (!$booking_suggestion) {
							array_push($room_requirements, "`r`.`fromadult`<=" . $numadults);
						}
						array_push($room_requirements, "`r`.`toadult`>=" . $numadults);
						if ($showchildren && !empty($pchildren[$kad]) && intval($pchildren[$kad]) > 0) {
							$numchildren = intval($pchildren[$kad]);
							$arrpeople[$roomnumb]['children'] = $numchildren;
							if (!$booking_suggestion) {
								array_push($room_requirements, "`r`.`fromchild`<=" . $numchildren);
							}
							array_push($room_requirements, "`r`.`tochild`>=" . $numchildren);
						} else {
							$arrpeople[$roomnumb]['children'] = 0;
							// if no children then the room must accept no children
							if ($showchildren && intval($pchildren[$kad]) == 0) {
								array_push($room_requirements, "`r`.`fromchild` = 0");
							}
						}
						array_push($room_requirements, "`r`.`totpeople` >= " . ($arrpeople[$roomnumb]['adults'] + $arrpeople[$roomnumb]['children']));
						if (!$booking_suggestion) {
							array_push($room_requirements, "`r`.`mintotpeople` <= " . ($arrpeople[$roomnumb]['adults'] + $arrpeople[$roomnumb]['children']));
						}
						$strclause = '(' . implode(' AND ', $room_requirements) . ')';
						$arradultsclause[] = $strclause;
					}
				}
			}
		}

		// Modify booking
		$mod_booking = [];
		$skip_busy_ids = [];
		$cur_mod = $session->get('vboModBooking', '');
		if (is_array($cur_mod) && $cur_mod) {
			$mod_booking = $cur_mod;
			$skip_busy_ids = VikBooking::loadBookingBusyIds($mod_booking['id']);
		}

		// Session values
		$session->set('vbroomsnum', $proomsnum);
		$session->set('vbarrpeople', $arrpeople);

		if (empty($pcheckindate) || empty($pcheckoutdate) || $proomsnum <= 0 || count($arradultsrooms) != $proomsnum) {
			$this->setVboError(JText::translate('VBSELPRDATE'));
			return;
		}

		if (!VikBooking::dateIsValid($pcheckindate) || !VikBooking::dateIsValid($pcheckoutdate)) {
			$this->setVboError(JText::translate('VBWRONGDF') . ": " . VikBooking::sayDateFormat());
			return;
		}

		$first = VikBooking::getDateTimestamp($pcheckindate, $pcheckinh, $pcheckinm);
		$second = VikBooking::getDateTimestamp($pcheckoutdate, $pcheckouth, $pcheckoutm);
		$actnow = time();
		$today_bookings = VikBooking::todayBookings();
		if ($today_bookings) {
			$actnow = mktime(0, 0, 0, date('n'), date('j'), date('Y'));
		}

		if ($second <= $first || $first < $actnow) {
			$session->set('vbcheckin', '');
			$session->set('vbcheckout', '');
			if ($first <= $actnow) {
				if (date('d/m/Y', $first) == date('d/m/Y', $actnow)) {
					$emess = JText::translate('VBSRCHERRCHKINPASSED');
				} else {
					$emess = JText::translate('VBSRCHERRCHKINPAST');
				}
			} else {
				$emess = JText::translate('VBPICKBRET');
			}
			$this->setVboError($emess);
			return;
		}

		// set stay dates and number of nights
		$session->set('vbcheckin', $first);
		$session->set('vbcheckout', $second);
		$secdiff = $second - $first;
		$daysdiff = $secdiff / 86400;
		if (is_int($daysdiff)) {
			if ($daysdiff < 1) {
				$daysdiff = 1;
			}
		} else {
			if ($daysdiff < 1) {
				$daysdiff = 1;
			} else {
				$sum = floor($daysdiff) * 86400;
				$newdiff = $secdiff - $sum;
				$maxhmore = VikBooking::getHoursMoreRb() * 3600;
				if ($maxhmore >= $newdiff) {
					$daysdiff = floor($daysdiff);
				} else {
					$daysdiff = ceil($daysdiff);
				}
			}
		}

		// push data to tracker
		VikBooking::getTracker()->pushDates($first, $second, $daysdiff)->pushParty($arrpeople);

		// restrictions
		$allrestrictions = VikBooking::loadRestrictions(false);
		$restrictions = VikBooking::globalRestrictions($allrestrictions);
		$restrcheckin = getdate($first);
		$restrcheckout = getdate($second);
		$restrictionsvalid = true;
		$restrictions_affcount = 0;
		$restrictionerrmsg = '';
		if ($restrictions) {
			if (array_key_exists($restrcheckin['mon'], $restrictions)) {
				// restriction found for this month, checking:
				$restrictions_affcount++;
				if (strlen((string)$restrictions[$restrcheckin['mon']]['wday'])) {
					$rvalidwdays = [$restrictions[$restrcheckin['mon']]['wday']];
					if (strlen((string)$restrictions[$restrcheckin['mon']]['wdaytwo'])) {
						$rvalidwdays[] = $restrictions[$restrcheckin['mon']]['wdaytwo'];
					}
					if (!in_array($restrcheckin['wday'], $rvalidwdays)) {
						$restrictionsvalid = false;
						$restrictionerrmsg = JText::sprintf('VBRESTRERRWDAYARRIVAL', VikBooking::sayMonth($restrcheckin['mon']), VikBooking::sayWeekDay($restrictions[$restrcheckin['mon']]['wday']).(strlen($restrictions[$restrcheckin['mon']]['wdaytwo']) > 0 ? '/'.VikBooking::sayWeekDay($restrictions[$restrcheckin['mon']]['wdaytwo']) : ''));
					} elseif ($restrictions[$restrcheckin['mon']]['multiplyminlos'] == 1) {
						if (($daysdiff % $restrictions[$restrcheckin['mon']]['minlos']) != 0) {
							$restrictionsvalid = false;
							$restrictionerrmsg = JText::sprintf('VBRESTRERRMULTIPLYMINLOS', VikBooking::sayMonth($restrcheckin['mon']), $restrictions[$restrcheckin['mon']]['minlos']);
						}
					}
					$comborestr = VikBooking::parseJsDrangeWdayCombo($restrictions[$restrcheckin['mon']]);
					if ($comborestr) {
						if (array_key_exists($restrcheckin['wday'], $comborestr)) {
							if (!in_array($restrcheckout['wday'], $comborestr[$restrcheckin['wday']])) {
								$restrictionsvalid = false;
								$restrictionerrmsg = JText::sprintf('VBRESTRERRWDAYCOMBO', VikBooking::sayMonth($restrcheckin['mon']), VikBooking::sayWeekDay($comborestr[$restrcheckin['wday']][0]).(count($comborestr[$restrcheckin['wday']]) == 2 ? '/'.VikBooking::sayWeekDay($comborestr[$restrcheckin['wday']][1]) : ''), VikBooking::sayWeekDay($restrcheckin['wday']));
							}
						}
					}
				} elseif (!empty($restrictions[$restrcheckin['mon']]['ctad']) || !empty($restrictions[$restrcheckin['mon']]['ctdd'])) {
					if (!empty($restrictions[$restrcheckin['mon']]['ctad'])) {
						$ctarestrictions = explode(',', $restrictions[$restrcheckin['mon']]['ctad']);
						if (in_array('-'.$restrcheckin['wday'].'-', $ctarestrictions)) {
							$restrictionsvalid = false;
							$restrictionerrmsg = JText::sprintf('VBRESTRERRWDAYCTAMONTH', VikBooking::sayWeekDay($restrcheckin['wday']), VikBooking::sayMonth($restrcheckin['mon']));
						}
					}
					if (!empty($restrictions[$restrcheckin['mon']]['ctdd'])) {
						$ctdrestrictions = explode(',', $restrictions[$restrcheckin['mon']]['ctdd']);
						if (in_array('-'.$restrcheckout['wday'].'-', $ctdrestrictions)) {
							$restrictionsvalid = false;
							$restrictionerrmsg = JText::sprintf('VBRESTRERRWDAYCTDMONTH', VikBooking::sayWeekDay($restrcheckout['wday']), VikBooking::sayMonth($restrcheckin['mon']));
						}
					}
				}
				if (!empty($restrictions[$restrcheckin['mon']]['maxlos']) && $restrictions[$restrcheckin['mon']]['maxlos'] > 0 && $restrictions[$restrcheckin['mon']]['maxlos'] > $restrictions[$restrcheckin['mon']]['minlos']) {
					if ($daysdiff > $restrictions[$restrcheckin['mon']]['maxlos']) {
						$restrictionsvalid = false;
						$restrictionerrmsg = JText::sprintf('VBRESTRERRMAXLOSEXCEEDED', VikBooking::sayMonth($restrcheckin['mon']), $restrictions[$restrcheckin['mon']]['maxlos']);
					}
				}
				if ($daysdiff < $restrictions[$restrcheckin['mon']]['minlos']) {
					$restrictionsvalid = false;
					$restrictionerrmsg = JText::sprintf('VBRESTRERRMINLOSEXCEEDED', VikBooking::sayMonth($restrcheckin['mon']), $restrictions[$restrcheckin['mon']]['minlos']);
				}
			} elseif (array_key_exists('range', $restrictions)) {
				/**
				 * We use this map to know which restriction IDs are okay or not okay with the Min LOS.
				 * The most recent restrictions will have a higher priority over the oldest ones.
				 * 
				 * @since 	1.13.5 (J) - 1.3.6 (WP)
				 */
				$minlos_priority = [
					'ok'  => [],
					'nok' => [],
				];
				//
				foreach ($restrictions['range'] as $restr) {
					/**
					 * We should not always add 82799 seconds to the end date of the restriction
					 * because if they only last for one day (like a Saturday), then $restr['dto']
					 * will be already set to the time 23:59:59.
					 * 
					 * @since 	1.13 (J) - 1.3.0 (WP)
					 * @since 	1.18.3 (J) - 1.8.3 (WP) timestamps ending with 9 for a range greater than 1 day indicate that the restriction end time
					 * 			is already set to 23:59:59 (VCM Connector may be responsible), so no seconds should be added or the restriction end date
					 *  		will be the day after, and if after the check-in time, it may be included.
					 */
					$end_operator = date('Y-m-d', $restr['dfrom']) != date('Y-m-d', $restr['dto']) && substr((string) $restr['dto'], -1, 1) != 9 ? 82799 : 0;
					//
					if ($restr['dfrom'] <= $restrcheckin[0] && ($restr['dto'] + $end_operator) >= $restrcheckin[0]) {
						// restriction found for this date range based on arrival date, check if compliant
						$restrictions_affcount++;
						if (strlen((string)$restr['wday'])) {
							$rvalidwdays = [$restr['wday']];
							if (strlen((string)$restr['wdaytwo'])) {
								$rvalidwdays[] = $restr['wdaytwo'];
							}
							if (!in_array($restrcheckin['wday'], $rvalidwdays)) {
								$restrictionsvalid = false;
								$restrictionerrmsg = JText::sprintf('VBRESTRERRWDAYARRIVALRANGE', VikBooking::sayWeekDay($restr['wday']).(strlen((string)$restr['wdaytwo']) ? '/'.VikBooking::sayWeekDay($restr['wdaytwo']) : ''));
							} elseif ($restr['multiplyminlos'] == 1) {
								if (($daysdiff % $restr['minlos']) != 0) {
									$restrictionsvalid = false;
									$restrictionerrmsg = JText::sprintf('VBRESTRERRMULTIPLYMINLOSRANGE', $restr['minlos']);
								}
							}
							$comborestr = VikBooking::parseJsDrangeWdayCombo($restr);
							if ($comborestr) {
								if (array_key_exists($restrcheckin['wday'], $comborestr)) {
									if (!in_array($restrcheckout['wday'], $comborestr[$restrcheckin['wday']])) {
										$restrictionsvalid = false;
										$restrictionerrmsg = JText::sprintf('VBRESTRERRWDAYCOMBORANGE', VikBooking::sayWeekDay($comborestr[$restrcheckin['wday']][0]).(count($comborestr[$restrcheckin['wday']]) == 2 ? '/'.VikBooking::sayWeekDay($comborestr[$restrcheckin['wday']][1]) : ''), VikBooking::sayWeekDay($restrcheckin['wday']));
									}
								}
							}
						} elseif (!empty($restr['ctad']) || !empty($restr['ctdd'])) {
							if (!empty($restr['ctad'])) {
								$ctarestrictions = explode(',', $restr['ctad']);
								if (in_array('-'.$restrcheckin['wday'].'-', $ctarestrictions)) {
									$restrictionsvalid = false;
									$restrictionerrmsg = JText::sprintf('VBRESTRERRWDAYCTARANGE', VikBooking::sayWeekDay($restrcheckin['wday']));
								}
							}
							if (!empty($restr['ctdd'])) {
								$ctdrestrictions = explode(',', $restr['ctdd']);
								if (in_array('-'.$restrcheckout['wday'].'-', $ctdrestrictions) && $restrcheckout[0] <= ($restr['dto'] + $end_operator)) {
									$restrictionsvalid = false;
									$restrictionerrmsg = JText::sprintf('VBRESTRERRWDAYCTDRANGE', VikBooking::sayWeekDay($restrcheckout['wday']));
								}
							}
						}
						if (!empty($restr['maxlos']) && $restr['maxlos'] > 0 && $restr['maxlos'] >= $restr['minlos']) {
							if ($daysdiff > $restr['maxlos']) {
								$restrictionsvalid = false;
								$restrictionerrmsg = JText::sprintf('VBRESTRERRMAXLOSEXCEEDEDRANGE', $restr['maxlos']);
							}
						}
						if ($daysdiff < $restr['minlos']) {
							$restrictionsvalid = false;
							$restrictionerrmsg = JText::sprintf('VBRESTRERRMINLOSEXCEEDEDRANGE', $restr['minlos']);
							array_push($minlos_priority['nok'], (int)$restr['id']);
						} else {
							array_push($minlos_priority['ok'], (int)$restr['id']);
						}
					} elseif ($restr['dfrom'] <= $restrcheckout[0] && ($restr['dto'] + $end_operator) >= $restrcheckout[0] && !empty($restr['ctdd'])) {
						/**
						 * We validate the CTD restrictions depending on the check-out date.
						 * 
						 * @since 	1.16.3 (J) - 1.6.3 (WP)
						 */
						$ctdrestrictions = explode(',', $restr['ctdd']);
						if (in_array('-'.$restrcheckout['wday'].'-', $ctdrestrictions)) {
							$restrictions_affcount++;
							$restrictionsvalid = false;
							$restrictionerrmsg = JText::sprintf('VBRESTRERRWDAYCTDRANGE', VikBooking::sayWeekDay($restrcheckout['wday']));
						}
					}
				}
				/**
				 * We give priority to more recent restrictions to override MinLOS.
				 * 
				 * @since 	1.13.5 (J) - 1.3.6 (WP)
				 */
				if (!$restrictionsvalid && $minlos_priority['ok'] && $minlos_priority['nok'] && max($minlos_priority['ok']) > max($minlos_priority['nok'])) {
					// we unset the error message and we reset the validity because a more recent restriction is allowing this MinLOS
					$restrictionsvalid = true;
					$restrictionerrmsg = '';
				}
				//
			}
		}
		/**
		 * We are no longer counting the "global restriction records" applied to all rooms, but
		 * we are rather counting all restriction records, global and for individual rooms. This
		 * way we apply the default Min LOS only when there are no restrictions at all. We are
		 * also ignoring if some restrictions were applied. This is the previous IF statement:
		 * 
		 * if (!$restrictions || $restrictions_affcount <= 0) {
		 * 
		 * @since 	1.15.0 (J) - 1.5.0 (WP)
		 */
		if (!$allrestrictions) {
			// check global MinLOS (only in case there are no restrictions affecting these dates or no restrictions at all)
			$globminlos = VikBooking::getDefaultNightsCalendar();
			if ($globminlos > 1 && $daysdiff < $globminlos) {
				$restrictionsvalid = false;
				$restrictionerrmsg = JText::sprintf('VBRESTRERRMINLOSEXCEEDEDRANGE', $globminlos);
			}
		}

		// check if we are coming from a package to not consider the global restrictions for the standard booking procedure
		if (!empty($ppkg_id)) {
			$pkg = VikBooking::getPackage($ppkg_id);
			if ($pkg) {
				//the package exists so we let the next view perform the validation of the package criteria (MinLOS, MaxLOS etc..).
				$restrictionsvalid = true;
				$restrictionerrmsg = '';
			}
		}

		// closing dates
		$err_closingdates = VikBooking::validateClosingDates($first, $second, $df);
		if (!empty($err_closingdates)) {
			$restrictionsvalid = false;
			$restrictionerrmsg = JText::sprintf('VBERRDATESCLOSED', $err_closingdates);
		}

		// maximum date in the future for bookings
		$err_maxdatefuture = VikBooking::validateMaxDateBookings($first);
		if (!empty($err_maxdatefuture)) {
			$restrictionsvalid = false;
			$restrictionerrmsg = JText::sprintf('VBOERRMAXDATEBOOKINGS', $err_maxdatefuture);
		}

		// minimum days in advance for bookings
		$err_mindaysadv = VikBooking::validateMinDaysAdvance($first);
		if (!empty($err_mindaysadv)) {
			$restrictionsvalid = false;
			$restrictionerrmsg = JText::sprintf('VBOERRMINDAYSADV', $err_mindaysadv);
		}

		if ($restrictionsvalid !== true) {
			$this->setVboError($restrictionerrmsg);
			return;
		}

		$hoursdiff = VikBooking::countHoursToArrival($first);
		$q = "SELECT `p`.*,`r`.`id` AS `r_reference_id`,`r`.`name`,`r`.`img`,`r`.`idcat`,`r`.`idcarat`,`r`.`units`,`r`.`moreimgs`,`r`.`fromadult`,`r`.`toadult`,`r`.`fromchild`,`r`.`tochild`,`r`.`smalldesc`,`r`.`totpeople`,`r`.`params`,`r`.`imgcaptions`,`rp`.`name` AS `rpname`,`rp`.`minlos`,`rp`.`minhadv` FROM `#__vikbooking_dispcost` AS `p`, `#__vikbooking_rooms` AS `r`, `#__vikbooking_prices` AS `rp` WHERE `p`.`days`=".(int)$daysdiff." AND `p`.`idroom`=`r`.`id` AND `p`.`idprice`=`rp`.`id` AND `r`.`avail`='1' AND (".implode(" OR ", $arradultsclause).") ORDER BY `p`.`cost` ASC, `p`.`idprice` DESC, `p`.`idroom` ASC;";
		$dbo->setQuery($q);
		$tars = $dbo->loadAssocList();

		if (!$tars) {
			$sayerr = JText::translate('VBNOROOMAVFOR') . " " . $daysdiff . " " . ($daysdiff > 1 ? JText::translate('VBDAYS') : JText::translate('VBDAY'));
			if (count($padults) === 1) {
				$sayerr .= ", ".$arrpeople[1]['adults']." ".($arrpeople[1]['adults'] > 1 ? JText::translate('VBSEARCHRESADULTS') : JText::translate('VBSEARCHRESADULT'));
				if ($arrpeople[1]['children'] > 0) {
					$sayerr .= ", ".$arrpeople[1]['children']." ".($arrpeople[1]['children'] > 1 ? JText::translate('VBSEARCHRESCHILDREN') : JText::translate('VBSEARCHRESCHILD'));
				}
			}
			$err_code_info = [
				'code' => 2,
				'fromts' => $first,
				'tots' => $second,
				'party' => $arrpeople
			];

			// push data to tracker and close
			VikBooking::getTracker()->pushMessage($sayerr, 'error')->closeTrack();

			$this->setVboError($sayerr, $err_code_info);
			return;
		}

		$vbo_tn->translateContents($tars, '#__vikbooking_rooms', ['id' => 'r_reference_id']);
		$arrtar = [];
		foreach ($tars as $tar) {
			$arrtar[$tar['idroom']][] = $tar;
		}

		// rate plans closed on these dates
		$roomrpclosed = VikBooking::getRoomRplansClosedInDates(array_keys($arrtar), $first, $daysdiff);
		if ($roomrpclosed) {
			foreach ($arrtar as $kk => $tt) {
				if (array_key_exists($kk, $roomrpclosed)) {
					foreach ($tt as $tk => $tv) {
						if (array_key_exists($tv['idprice'], $roomrpclosed[$kk])) {
							unset($arrtar[$kk][$tk]);
						}
					}
					if (!$arrtar[$kk]) {
						unset($arrtar[$kk]);
					} else {
						$arrtar[$kk] = array_values($arrtar[$kk]);
					}
				}
			}
		}

		// rate plans with a minlos, or with a min hours in advance
		foreach ($arrtar as $kk => $tt) {
			foreach ($tt as $tk => $tv) {
				if (!empty($tv['minlos']) && $tv['minlos'] > $daysdiff) {
					unset($arrtar[$kk][$tk]);
				} elseif ($hoursdiff < $tv['minhadv']) {
					unset($arrtar[$kk][$tk]);
				}
			}
			if (!$arrtar[$kk]) {
				unset($arrtar[$kk]);
			} else {
				$arrtar[$kk] = array_values($arrtar[$kk]);
			}
		}

		/**
		 * Search results filtering options.
		 * 
		 * @since 	1.18.3 (J) - 1.8.3 (WP)
		 */
		$resultFilters = (array) $app->input->get('filters', [], 'array');
		$filterAmenityIds = array_values(array_filter(array_map('intval', (array) ($resultFilters['amenity'] ?? null))));
		if (is_array($resultFilters['category'] ?? null)) {
			// sanitize category filtering options
			$resultFilters['category'] = array_values(array_filter(array_map('intval', $resultFilters['category'])));
			if ($resultFilters['category']) {
				// replace native category filter
				$pcategory_ids = $resultFilters['category'];
			}
		}

		// native search filtering and available records
		$filtercat = (!empty($pcategories) && $pcategories != "all");
		$filtermulticat = (is_array($pcategory_ids) && $pcategory_ids);
		$groupdays = VikBooking::getGroupDays($first, $second, $daysdiff);
		$morehst = VikBooking::getHoursRoomAvail() * 3600;
		$allbusy = VikBooking::loadBusyRecords(array_keys($arrtar), $first, $second);
		$all_locked = VikBooking::loadLockedRecords(array_keys($arrtar), $actnow);
		foreach ($arrtar as $kk => $tt) {
			$cats = explode(';', (string) $tt[0]['idcat']);
			if ($filtercat) {
				if (!in_array($pcategories, $cats)) {
					unset($arrtar[$kk]);
					continue;
				}
			}
			if ($filtermulticat) {
				/**
				 * Added support for multiple category IDs as filter
				 * 
				 * @since 	1.13
				 */
				$multicat_found = false;
				foreach ($pcategory_ids as $fcatid) {
					if (!empty($fcatid) && in_array($fcatid, $cats)) {
						$multicat_found = true;
						break;
					}
				}
				if (!$multicat_found) {
					unset($arrtar[$kk]);
					continue;
				}
			}

			if ($filterAmenityIds) {
				// filter listings by amenities (all the selected ones must be supported by the listing)
				$carats = array_values(array_filter(array_map('intval', explode(';', (string) $tt[0]['idcarat']))));
				if (array_diff($filterAmenityIds, $carats)) {
					// some amenities are not available with this listing
					unset($arrtar[$kk]);
					continue;
				}
			}

			/**
			 * Trigger event to allow third party plugins to validate custom search results filtering.
			 * 
			 * @since 	1.18.3 (J) - 1.8.3 (WP)
			 */
			$customFiltering = VBOFactory::getPlatform()->getDispatcher()->filter('onApplySearchResultsFiltering', [$tt[0], $resultFilters]);
			if (in_array(false, $customFiltering, true)) {
				// listing was requested to be unset
				unset($arrtar[$kk]);
				continue;
			}

			// availability controls
			$arrtar[$kk][0]['unitsavail'] = $tt[0]['units'];
			if ($allbusy && isset($allbusy[$kk]) && $allbusy[$kk]) {
				$units_booked = [];
				$check_locked = (bool)($all_locked && isset($all_locked[$kk]) && $all_locked[$kk]);
				foreach ($groupdays as $gday) {
					$bfound = 0;
					foreach ($allbusy[$kk] as $bu) {
						if (in_array($bu['id'], $skip_busy_ids)) {
							// Booking modification
							continue;
						}
						if ($gday >= $bu['checkin'] && $gday <= ($morehst + $bu['checkout'])) {
							$bfound++;
						}
					}
					if ($bfound >= $tt[0]['units']) {
						// listing is fully booked
						$unavailable_listings[] = $kk;
						unset($arrtar[$kk]);
						break;
					} else {
						$units_booked[] = $bfound;
						if ($check_locked === true) {
							foreach ($all_locked[$kk] as $bu) {
								if ($gday >= $bu['checkin'] && $gday <= $bu['realback']) {
									$bfound++;
								}
							}
							if ($bfound >= $tt[0]['units']) {
								unset($arrtar[$kk]);
								break;
							} else {
								// push peak to properly calculate the units left by considering the locked records
								$units_booked[] = $bfound;
							}
						}
					}
				}
				if (isset($arrtar[$kk]) && $units_booked) {
					$tot_u_booked = max($units_booked);
					$tot_u_left = ($tt[0]['units'] - $tot_u_booked);
					$arrtar[$kk][0]['unitsavail'] = $tot_u_left >= 0 ? $tot_u_left : 0;
				}
			} elseif (!VikBooking::roomNotLocked($kk, $tt[0]['units'], $first, $second)) {
				// room is fully locked
				unset($arrtar[$kk]);
			} elseif ($all_locked && isset($all_locked[$kk]) && $all_locked[$kk]) {
				// update number of units left by counting the units temporary locked
				$units_booked = [];
				foreach ($groupdays as $gday) {
					$bfound = 0;
					foreach ($all_locked[$kk] as $bu) {
						if ($gday >= $bu['checkin'] && $gday <= $bu['realback']) {
							$bfound++;
						}
					}
					// push peak to properly calculate the units left
					$units_booked[] = $bfound;
				}
				if (isset($arrtar[$kk]) && $units_booked) {
					$tot_u_booked = max($units_booked);
					$tot_u_left = ($tt[0]['units'] - $tot_u_booked);
					$arrtar[$kk][0]['unitsavail'] = $tot_u_left >= 0 ? $tot_u_left : 0;
				}
			}

			// single room restrictions
			if (isset($arrtar[$kk]) && $allrestrictions) {
				$roomrestr = VikBooking::roomRestrictions($kk, $allrestrictions);
				if ($roomrestr) {
					$restrictionerrmsg = VikBooking::validateRoomRestriction($roomrestr, $restrcheckin, $restrcheckout, $daysdiff);
					if (strlen($restrictionerrmsg) > 0) {
						// check if we are coming from a package to not consider the restrictions for the standard booking procedure
						$canunset = true;
						if (!empty($ppkg_id)) {
							$pkg = VikBooking::getPackage($ppkg_id);
							if ($pkg) {
								// the package exists so we let the next view perform the validation of the package criterai (MinLOS, MaxLOS etc..).
								$canunset = false;
							}
						}
						if ($canunset === true) {
							unset($arrtar[$kk]);
						} else {
							$restrictionerrmsg = '';
						}
					}
				}
			}

			/**
			 * Room-level maximum advance booking notice validation.
			 * 
			 * @since 	1.16.3 (J) - 1.6.3 (WP)
			 */
			$err_maxdatefuture_room = VikBooking::validateMaxDateBookings($first, $kk);
			if (!empty($err_maxdatefuture_room)) {
				// unset non-compliant room and register last error
				unset($arrtar[$kk]);
				$restrictionerrmsg = JText::sprintf('VBOERRMAXDATEBOOKINGS', $err_maxdatefuture_room);
			}

			/**
			 * Room-level minimum advance booking notice validation.
			 * 
			 * @since 	1.18.3 (J) - 1.8.3 (WP)
			 */
			$err_mindaysadv_room = VikBooking::validateMinDaysAdvance($first, $kk);
			if (!empty($err_mindaysadv_room)) {
				// unset non-compliant room and register last error
				unset($arrtar[$kk]);
				if (is_float($err_mindaysadv_room)) {
					// number of minimum hours in advance not met
					$restrictionerrmsg = JText::sprintf('VBOERRMINBOOKNOTICE', JText::sprintf('VBO_N_HOURS', intval($err_mindaysadv_room * 24)));
				} else {
					// integer expected
					$restrictionerrmsg = JText::sprintf('VBOERRMINDAYSADV', $err_mindaysadv_room);
				}
			}
		}

		if (!is_array($arrtar) || !$arrtar) {
			$err_code_info = [];
			if (strlen($restrictionerrmsg)) {
				VikError::raiseWarning('', $restrictionerrmsg);
			} else {
				$err_code_info = [
					'code' => 1,
					'fromts' => $first,
					'tots' => $second,
					'party' => $arrpeople
				];
			}
			$msg = JText::translate('VBNOROOMSINDATE');

			// push data to tracker and close
			VikBooking::getTracker()->pushMessage($msg, 'error')->closeTrack();

			$this->setVboError($msg, $err_code_info);
			return;
		}

		/**
		 * Despite of the higher resources needed, we parse all rate plans for all rooms.
		 * 
		 * @since 	1.13 (J) - 1.3.0 (WP)
		 */
		$arrtar = VikBooking::applySeasonalPrices($arrtar, $first, $second);
		$multi_rates = 1;
		foreach ($arrtar as $idr => $tars) {
			$multi_rates = count($tars) > $multi_rates ? count($tars) : $multi_rates;
		}
		if ($multi_rates > 1) {
			for ($r = 1; $r < $multi_rates; $r++) {
				$deeper_rates = [];
				foreach ($arrtar as $idr => $tars) {
					foreach ($tars as $tk => $tar) {
						if ($tk == $r) {
							$deeper_rates[$idr][0] = $tar;
							break;
						}
					}
				}
				if (!$deeper_rates) {
					continue;
				}
				$deeper_rates = VikBooking::applySeasonalPrices($deeper_rates, $first, $second);
				foreach ($deeper_rates as $idr => $dtars) {
					foreach ($dtars as $dtk => $dtar) {
						$arrtar[$idr][$r] = $dtar;
					}
				}
			}
		}
		$arrtar = VikBooking::sortResults($arrtar);

		// separate results per number of rooms with $results
		$tmparrtar = $arrtar;
		$results = [];
		$multiroomcount = [];
		foreach ($arrpeople as $numroom => $aduchild) {
			$arrtar = $tmparrtar;
			$diffusage = [];
			$aduchild['children'] = !array_key_exists('children', $aduchild) ? 0 : $aduchild['children'];
			$nowtotpeople = $aduchild['adults'] + $aduchild['children'];
			foreach ($arrtar as $kk => $tt) {
				$validchildren = true;
				if ($showchildren) {
					if (!($tt[0]['fromchild'] <= $aduchild['children'] && $tt[0]['tochild'] >= $aduchild['children']) && $aduchild['children'] > 0) {
						$validchildren = false;
					}
				}
				$validtotpeople = true;
				if ($nowtotpeople > $tt[0]['totpeople']) {
					$errmess = JText::sprintf('VBERRPEOPLEPERROOM', $nowtotpeople, $aduchild['adults'], $aduchild['children']);
					$validtotpeople = false;
				}
				if ($validchildren && $validtotpeople) {
					if ($tt[0]['toadult'] == $aduchild['adults']) {
						//clean the diffusage from best usage in case it exists from before
						foreach ($arrtar[$kk] as $kpr => $vpr) {
							if (array_key_exists('diffusage', $arrtar[$kk][$kpr])) {
								unset($arrtar[$kk][$kpr]['diffusage']);
							}
							if (array_key_exists('diffusagecost', $arrtar[$kk][$kpr])) {
								//restore original price
								$operator = substr($arrtar[$kk][$kpr]['diffusagecost'], 0, 1);
								$valpcent = substr($arrtar[$kk][$kpr]['diffusagecost'], -1);
								if ($operator == "+") {
									if ($valpcent == "%") {
										$diffvalue = substr($arrtar[$kk][$kpr]['diffusagecost'], 1, (strlen($arrtar[$kk][$kpr]['diffusagecost']) - 1));
										if (array_key_exists('diffusagecostpernight', $arrtar[$kk][$kpr]) && $arrtar[$kk][$kpr]['diffusagecostpernight'] > 0) {
											$arrtar[$kk][$kpr]['cost'] = $arrtar[$kk][$kpr]['diffusagecostpernight'];
										} else {
											$arrtar[$kk][$kpr]['cost'] = round(($vpr['cost'] * (100 - $diffvalue) / 100), 2);
										}
									} else {
										$diffvalue = substr($arrtar[$kk][$kpr]['diffusagecost'], 1);
										$arrtar[$kk][$kpr]['cost'] = $vpr['cost'] - $diffvalue;
									}
								} elseif ($operator == "-") {
									if ($valpcent == "%") {
										$diffvalue = substr($arrtar[$kk][$kpr]['diffusagecost'], 1, (strlen($arrtar[$kk][$kpr]['diffusagecost']) - 1));
										if (array_key_exists('diffusagecostpernight', $arrtar[$kk][$kpr]) && $arrtar[$kk][$kpr]['diffusagecostpernight'] > 0) {
											$arrtar[$kk][$kpr]['cost'] = $arrtar[$kk][$kpr]['diffusagecostpernight'];
										} else {
											$arrtar[$kk][$kpr]['cost'] = round(($vpr['cost'] * (100 + $diffvalue) / 100), 2);
										}
									} else {
										$diffvalue = substr($arrtar[$kk][$kpr]['diffusagecost'], 1);
										$arrtar[$kk][$kpr]['cost'] = $vpr['cost'] + $diffvalue;
									}
								}
								//
								unset($arrtar[$kk][$kpr]['diffusagecost']);
								unset($arrtar[$kk][$kpr]['diffusagecostpernight']);
							}
						}

						// Maximum Occupancy Charges/Discounts
						$diffusageprice = VikBooking::loadAdultsDiff($kk, $aduchild['adults']);
						// Occupancy Override
						if (array_key_exists('occupancy_ovr', $tt[0]) && array_key_exists($aduchild['adults'], $tt[0]['occupancy_ovr']) && strlen($tt[0]['occupancy_ovr'][$aduchild['adults']]['value'])) {
							$diffusageprice = $tt[0]['occupancy_ovr'][$aduchild['adults']];
						}

						if (is_array($diffusageprice)) {
							// set a charge or discount to the price(s) for the different usage of the room
							foreach ($arrtar[$kk] as $kpr => $vpr) {
								$arrtar[$kk][$kpr]['diffusage'] = $aduchild['adults'];
								if ($diffusageprice['chdisc'] == 1) {
									// charge
									if ($diffusageprice['valpcent'] == 1) {
										// fixed value
										$arrtar[$kk][$kpr]['diffusagecostpernight'] = $diffusageprice['pernight'] == 1 ? 1 : 0;
										$aduseval = $diffusageprice['pernight'] == 1 ? $diffusageprice['value'] * $arrtar[$kk][$kpr]['days'] : $diffusageprice['value'];
										$arrtar[$kk][$kpr]['diffusagecost'] = "+".$aduseval;
										$arrtar[$kk][$kpr]['cost'] = $vpr['cost'] + $aduseval;
									} else {
										// percentage value
										$arrtar[$kk][$kpr]['diffusagecostpernight'] = $diffusageprice['pernight'] == 1 ? $vpr['cost'] : 0;
										$aduseval = $diffusageprice['pernight'] == 1 ? round(($vpr['cost'] * $diffusageprice['value'] / 100) * $arrtar[$kk][$kpr]['days'] + $vpr['cost'], 2) : round(($vpr['cost'] * (100 + $diffusageprice['value']) / 100), 2);
										$arrtar[$kk][$kpr]['diffusagecost'] = "+".$diffusageprice['value']."%";
										$arrtar[$kk][$kpr]['cost'] = $aduseval;
									}
								} else {
									// discount
									if ($diffusageprice['valpcent'] == 1) {
										// fixed value
										$arrtar[$kk][$kpr]['diffusagecostpernight'] = $diffusageprice['pernight'] == 1 ? 1 : 0;
										$aduseval = $diffusageprice['pernight'] == 1 ? $diffusageprice['value'] * $arrtar[$kk][$kpr]['days'] : $diffusageprice['value'];
										$arrtar[$kk][$kpr]['diffusagecost'] = "-".$aduseval;
										$arrtar[$kk][$kpr]['cost'] = $vpr['cost'] - $aduseval;
									} else {
										// percentage value
										$arrtar[$kk][$kpr]['diffusagecostpernight'] = $diffusageprice['pernight'] == 1 ? $vpr['cost'] : 0;
										$aduseval = $diffusageprice['pernight'] == 1 ? round($vpr['cost'] - ((($vpr['cost'] / $arrtar[$kk][$kpr]['days']) * $diffusageprice['value'] / 100) * $arrtar[$kk][$kpr]['days']), 2) : round(($vpr['cost'] * (100 - $diffusageprice['value']) / 100), 2);
										$arrtar[$kk][$kpr]['diffusagecost'] = "-".$diffusageprice['value']."%";
										$arrtar[$kk][$kpr]['cost'] = $aduseval;
									}
								}
							}
						}
						// Maximum Occupancy Charges/Discounts
						// best usage
						$results[$numroom][] = $arrtar[$kk];
						if (!isset($multiroomcount[$arrtar[$kk][0]['idroom']])) {
							$multiroomcount[$arrtar[$kk][0]['idroom']] = ['count' => 0];
						}
						$multiroomcount[$arrtar[$kk][0]['idroom']]['count'] += 1;
						$multiroomcount[$arrtar[$kk][0]['idroom']]['unitsavail'] = (int)$arrtar[$kk][0]['unitsavail'];
						$multiroomcount[$arrtar[$kk][0]['idroom']]['diffusage_r'.$numroom] = 0;
					} elseif (($tt[0]['fromadult'] <= $aduchild['adults'] || $booking_suggestion) && $tt[0]['toadult'] > $aduchild['adults']) {
						// different usage
						$diffusageprice = VikBooking::loadAdultsDiff($kk, $aduchild['adults']);
						// Occupancy Override
						if (array_key_exists('occupancy_ovr', $tt[0]) && array_key_exists($aduchild['adults'], $tt[0]['occupancy_ovr']) && strlen($tt[0]['occupancy_ovr'][$aduchild['adults']]['value'])) {
							$diffusageprice = $tt[0]['occupancy_ovr'][$aduchild['adults']];
						}

						if (is_array($diffusageprice)) {
							// set a charge or discount to the price(s) for the different usage of the room
							foreach ($arrtar[$kk] as $kpr => $vpr) {
								$arrtar[$kk][$kpr]['diffusage'] = $aduchild['adults'];
								if ($diffusageprice['chdisc'] == 1) {
									// charge
									if ($diffusageprice['valpcent'] == 1) {
										// fixed value
										$arrtar[$kk][$kpr]['diffusagecostpernight'] = $diffusageprice['pernight'] == 1 ? 1 : 0;
										$aduseval = $diffusageprice['pernight'] == 1 ? $diffusageprice['value'] * $arrtar[$kk][$kpr]['days'] : $diffusageprice['value'];
										$arrtar[$kk][$kpr]['diffusagecost'] = "+".$aduseval;
										$arrtar[$kk][$kpr]['cost'] = $vpr['cost'] + $aduseval;
									} else {
										// percentage value
										$arrtar[$kk][$kpr]['diffusagecostpernight'] = $diffusageprice['pernight'] == 1 ? $vpr['cost'] : 0;
										$aduseval = $diffusageprice['pernight'] == 1 ? round(($vpr['cost'] * $diffusageprice['value'] / 100) * $arrtar[$kk][$kpr]['days'] + $vpr['cost'], 2) : round(($vpr['cost'] * (100 + $diffusageprice['value']) / 100), 2);
										$arrtar[$kk][$kpr]['diffusagecost'] = "+".$diffusageprice['value']."%";
										$arrtar[$kk][$kpr]['cost'] = $aduseval;
									}
								} else {
									// discount
									if ($diffusageprice['valpcent'] == 1) {
										// fixed value
										$arrtar[$kk][$kpr]['diffusagecostpernight'] = $diffusageprice['pernight'] == 1 ? 1 : 0;
										$aduseval = $diffusageprice['pernight'] == 1 ? $diffusageprice['value'] * $arrtar[$kk][$kpr]['days'] : $diffusageprice['value'];
										$arrtar[$kk][$kpr]['diffusagecost'] = "-".$aduseval;
										$arrtar[$kk][$kpr]['cost'] = $vpr['cost'] - $aduseval;
									} else {
										// percentage value
										$arrtar[$kk][$kpr]['diffusagecostpernight'] = $diffusageprice['pernight'] == 1 ? $vpr['cost'] : 0;
										$aduseval = $diffusageprice['pernight'] == 1 ? round($vpr['cost'] - ((($vpr['cost'] / $arrtar[$kk][$kpr]['days']) * $diffusageprice['value'] / 100) * $arrtar[$kk][$kpr]['days']), 2) : round(($vpr['cost'] * (100 - $diffusageprice['value']) / 100), 2);
										$arrtar[$kk][$kpr]['diffusagecost'] = "-".$diffusageprice['value']."%";
										$arrtar[$kk][$kpr]['cost'] = $aduseval;
									}
								}
							}
						} else {
							$arrtar[$kk][0]['diffusage'] = $aduchild['adults'];
						}
						$diffusage[$numroom][] = $arrtar[$kk];
						if (!isset($multiroomcount[$arrtar[$kk][0]['idroom']])) {
							$multiroomcount[$arrtar[$kk][0]['idroom']] = ['count' => 0];
						}
						$multiroomcount[$arrtar[$kk][0]['idroom']]['count'] += 1;
						$multiroomcount[$arrtar[$kk][0]['idroom']]['unitsavail'] = (int)$arrtar[$kk][0]['unitsavail'];
						$multiroomcount[$arrtar[$kk][0]['idroom']]['diffusage_r'.$numroom] = 1;
					}
				}
			}
			// merge $diffusage rooms with and after best usage rooms in $results
			if ($diffusage) {
				foreach ($diffusage as $nr => $du) {
					foreach ($du as $duroom) {
						$results[$nr][]=$duroom;
					}
				}
			}
		}

		// check if rooms repeated passed the availability
		$limitpassed = false;
		$search_type = VikBooking::getSmartSearchType();
		$js_search = $search_type == 'dynamic' ? true : false;
		// dynamic smart search via PHP
		$js_overcounter = [];
		if ($js_search && $proomsnum > 1 && $multiroomcount) {
			$tot_avail = 0;
			foreach ($multiroomcount as $idroom => $info) {
				$tot_avail += $info['unitsavail'];
			}
			if ($tot_avail >= $proomsnum) {
				$gen_avail = $results;
				foreach ($multiroomcount as $idroom => $info) {
					$multiroomcount[$idroom]['used'] = 0;
					if ($info['count'] > $info['unitsavail']) {
						$excessnum = $info['count'] - $info['unitsavail'];
						if ($excessnum > 0) {
							for ($z = $proomsnum; $z >= 1; $z--) {
								if ($excessnum > 0) {
									/**
									 * @see  comments below
									 */
									$excessproc = false;
									//
									foreach ($results[$z] as $kres => $res) {
										if ($res[0]['idroom'] == $idroom) {
											/**
											 * We do not need to unset the rooms as we risk to produce no results.
											 * We skip this control if the "search type" setting is set to "dynamic".
											 * 
											 * @since 	1.12
											 */
											if (!$js_search) {
												unset($gen_avail[$z][$kres]);
											}
											$excessproc = true;
										}
									}
									if ($excessproc) {
										/**
										 * @see  comments below
										 */
										$excessnum--;
									}
								}
							}
						}
					}
				}
				/**
				 * We do not need to unset the rooms as we risk to produce no results.
				 * We skip this control if the "search type" setting is set to "dynamic".
				 * 
				 * @since 	1.12 Patch October 2nd 2019
				 */
				if (!$js_search) {
					for ($z = $proomsnum; $z >= 1; $z--) {
						if (!isset($gen_avail[$z]) || !$gen_avail[$z]) {
							foreach ($gen_avail as $oknroom => $res) {
								if (count($gen_avail[$oknroom]) > 1) {
									$searchfrom = min(array_keys($res));
									foreach ($res as $kr => $rr) {
										if (intval($kr) > intval($searchfrom)) {
											//check if the second, third.. cheapest room(s) is compatible
											if ($rr[0]['fromadult'] <= $arrpeople[$z]['adults'] && $rr[0]['toadult'] >= $arrpeople[$z]['adults'] && $rr[0]['fromchild'] <= $arrpeople[$z]['children'] && $rr[0]['tochild'] >= $arrpeople[$z]['children']) {
												$gen_avail[$z][] = $gen_avail[$oknroom][$kr];
												unset($gen_avail[$oknroom][$kr]);
												break 2;
											}
										}
									}
								}
							}
						}
					}
				}
				/**
				 * VBO 1.10 May 2018
				 *
				 * Scenario: One room with 3 units in total, 2 units booked and 1 free on certain dates, for 6 adults.
				 * Another room with 5 units available on these dates, for 2 adults. Dynamic search enabled.
				 *
				 * Reservation: 3 room types, 6 adults 1st room, 6 adults 2nd room, 2 adults 3rd room.
				 *
				 * Results: the 6-adult room is displayed for the 1st and 2nd rooms requested, the 2-adult room for the 3rd.
				 * With the Dynamic search enabled, by selecting the 6-adult room once, it gets removed from the other combo.
				 * This leaves one room-party empty, with no results, but no errors are displayed for the search suggestions,
				 * the search results page just gets stuck in there because for a party there are no rooms that can be chosen.
				 * 
				 * Solution: if one room-party has no more rooms, unset it so that $js_overcounter won't be set, and the
				 * "automatic smart search via PHP" statement will be processed below. This will raise the error "not enough rooms",
				 * and the Search Suggestions will correctly calculate the availability.
				 *
				 */
				foreach ($gen_avail as $oknroom => $res) {
					if (!$gen_avail[$oknroom]) {
						unset($gen_avail[$oknroom]);
					}
				}
				//
				if (count($gen_avail) == $proomsnum) {
					$js_overcounter = $multiroomcount;
					unset($gen_avail);
				}
			}
		}

		// automatic smart search via PHP
		if ($proomsnum > 1 && $multiroomcount) {
			foreach ($multiroomcount as $idroom => $info) {
				if ($info['count'] > $info['unitsavail']) {
					$excessnum = $info['count'] - $info['unitsavail'];
					for ($z = $proomsnum; $z >= 1; $z--) {
						/**
						 * VBO 1.10 June 2018
						 * the different usage clause below should be ignored, or we risk to keep a room in excess
						 * for some room parties when there should be actually no availability for booking multiple times.
						 * This room in excess may be removed only for the parties with different usage, and this is incorrect.
						 */
						if (true || (array_key_exists('diffusage_r'.$z, $info) && $info['diffusage_r'.$z] == 1)) {
							// remove repeated room where diffusage and excessnum still exceeds
							if ($excessnum > 0 && !$js_overcounter) {
								foreach ($results[$z] as $kres => $res) {
									if ($res[0]['idroom'] == $idroom) {
										unset($results[$z][$kres]);
										$limitpassed = true;
									}
								}
								if ($limitpassed) {
									$excessnum--;
								}
							}
						}
					}
					// if excessnum still exceeds, means that the room is not available for the repeated best usages
					if ($excessnum > 0) {
						for ($z = $proomsnum; $z >= 1; $z--) {
							if ($excessnum > 0 && !$js_overcounter) {
								foreach ($results[$z] as $kres => $res) {
									if ($res[0]['idroom'] == $idroom) {
										unset($results[$z][$kres]);
										$limitpassed = true;
									}
								}
								if ($limitpassed) {
									$excessnum--;
								}
							}
						}
					}
					//
				}
			}
		}
		//
		//if some room was repeated and removed from the multi rooms searched, check if enough results for each room
		if ($limitpassed == true) {
			$critic = [];
			for ($z = $proomsnum; $z >= 1; $z--) {
				if (!isset($results[$z]) || !$results[$z]) {
					$critic[] = $z;
					unset($results[$z]);
				}
			}
			if ($critic) {
				//some rooms have 0 results, check if something good for this num of adults can be placed here
				$moved = [];
				foreach ($critic as $kcr => $nroom) {
					foreach ($results as $oknroom => $res) {
						if (count($results[$oknroom]) > 1) {
							$searchfrom = min(array_keys($res));
							foreach ($res as $kr => $rr) {
								if (intval($kr) > intval($searchfrom)) {
									//check if the second, third.. cheapest room(s) is compatible
									if ($rr[0]['fromadult'] <= $arrpeople[$nroom]['adults'] && $rr[0]['toadult'] >= $arrpeople[$nroom]['adults'] && $rr[0]['fromchild'] <= $arrpeople[$nroom]['children'] && $rr[0]['tochild'] >= $arrpeople[$nroom]['children']) {
										$results[$nroom][] = $results[$oknroom][$kr];
										$moved[] = $oknroom.'_'.$nroom;
										unset($results[$oknroom][$kr]);
										unset($critic[$kcr]);
										break 2;
									}
								}
							}
						}
					}
				}
				ksort($results);
				if ($moved) {
					//check if moved rooms had charges/discounts for adults occupancy to update it
					foreach ($moved as $move) {
						$movedata = explode('_', $move);
						if ($arrpeople[$movedata[0]]['adults'] != $arrpeople[$movedata[1]]['adults'] && array_key_exists('diffusagecost', $results[$movedata[1]][0][0])) {
							//reset prices of the room
							foreach ($results[$movedata[1]][0] as $kpr => $vpr) {
								if (array_key_exists('diffusage', $results[$movedata[1]][0][$kpr])) {
									unset($results[$movedata[1]][0][$kpr]['diffusage']);
								}
								if (array_key_exists('diffusagecost', $results[$movedata[1]][0][$kpr])) {
									//restore original price
									$operator = substr($results[$movedata[1]][0][$kpr]['diffusagecost'], 0, 1);
									$valpcent = substr($results[$movedata[1]][0][$kpr]['diffusagecost'], -1);
									if ($operator == "+") {
										if ($valpcent == "%") {
											$diffvalue = substr($results[$movedata[1]][0][$kpr]['diffusagecost'], 1, (strlen($results[$movedata[1]][0][$kpr]['diffusagecost']) - 1));
											if (array_key_exists('diffusagecostpernight', $results[$movedata[1]][0][$kpr]) && $results[$movedata[1]][0][$kpr]['diffusagecostpernight'] > 0) {
												$results[$movedata[1]][0][$kpr]['cost'] = $results[$movedata[1]][0][$kpr]['diffusagecostpernight'];
											} else {
												$results[$movedata[1]][0][$kpr]['cost'] = round(($vpr['cost'] * (100 - $diffvalue) / 100), 2);
											}
										} else {
											$diffvalue = substr($results[$movedata[1]][0][$kpr]['diffusagecost'], 1);
											$results[$movedata[1]][0][$kpr]['cost'] = $vpr['cost'] - $diffvalue;
										}
									} elseif ($operator == "-") {
										if ($valpcent == "%") {
											$diffvalue = substr($results[$movedata[1]][0][$kpr]['diffusagecost'], 1, (strlen($results[$movedata[1]][0][$kpr]['diffusagecost']) - 1));
											if (array_key_exists('diffusagecostpernight', $results[$movedata[1]][0][$kpr]) && $results[$movedata[1]][0][$kpr]['diffusagecostpernight'] > 0) {
												$results[$movedata[1]][0][$kpr]['cost'] = $results[$movedata[1]][0][$kpr]['diffusagecostpernight'];
											} else {
												$results[$movedata[1]][0][$kpr]['cost'] = round(($vpr['cost'] * (100 + $diffvalue) / 100), 2);
											}
										} else {
											$diffvalue = substr($results[$movedata[1]][0][$kpr]['diffusagecost'], 1);
											$results[$movedata[1]][0][$kpr]['cost'] = $vpr['cost'] + $diffvalue;
										}
									}
									//
									unset($results[$movedata[1]][0][$kpr]['diffusagecost']);
									unset($results[$movedata[1]][0][$kpr]['diffusagecostpernight']);
								}
							}
							//end reset prices of the room
							$diffusageprice = VikBooking::loadAdultsDiff($results[$movedata[1]][0][0]['idroom'], $arrpeople[$movedata[1]]['adults']);
							//Occupancy Override - Special Price may be setting a charge/discount for this occupancy while default price had no occupancy pricing
							if (!is_array($diffusageprice)) {
								foreach ($results[$movedata[1]][0] as $kpr => $vpr) {
									if (array_key_exists('occupancy_ovr', $vpr) && array_key_exists($arrpeople[$movedata[1]]['adults'], $vpr['occupancy_ovr']) && strlen($vpr['occupancy_ovr'][$arrpeople[$movedata[1]]['adults']]['value'])) {
										$diffusageprice = $vpr['occupancy_ovr'][$arrpeople[$movedata[1]]['adults']];
										break;
									}
								}
								reset($results[$movedata[1]][0]);
							}
							//
							if (is_array($diffusageprice)) {
								//set a charge or discount to the price(s) for the different usage of the room
								foreach ($results[$movedata[1]][0] as $kpr => $vpr) {
									//Occupancy Override
									if (array_key_exists('occupancy_ovr', $results[$movedata[1]][0][$kpr]) && array_key_exists($arrpeople[$movedata[1]]['adults'], $results[$movedata[1]][0][$kpr]['occupancy_ovr']) && strlen($results[$movedata[1]][0][$kpr]['occupancy_ovr'][$arrpeople[$movedata[1]]['adults']]['value'])) {
										$diffusageprice = $results[$movedata[1]][0][$kpr]['occupancy_ovr'][$arrpeople[$movedata[1]]['adults']];
									}
									//
									$results[$movedata[1]][0][$kpr]['diffusage'] = $arrpeople[$movedata[1]]['adults'];
									if ($diffusageprice['chdisc'] == 1) {
										//charge
										if ($diffusageprice['valpcent'] == 1) {
											//fixed value
											$results[$movedata[1]][0][$kpr]['diffusagecostpernight'] = $diffusageprice['pernight'] == 1 ? 1 : 0;
											$aduseval = $diffusageprice['pernight'] == 1 ? $diffusageprice['value'] * $results[$movedata[1]][0][$kpr]['days'] : $diffusageprice['value'];
											$results[$movedata[1]][0][$kpr]['diffusagecost'] = "+".$aduseval;
											$results[$movedata[1]][0][$kpr]['cost'] = $vpr['cost'] + $aduseval;
										} else {
											//percentage value
											$results[$movedata[1]][0][$kpr]['diffusagecostpernight'] = $diffusageprice['pernight'] == 1 ? $vpr['cost'] : 0;
											$aduseval = $diffusageprice['pernight'] == 1 ? round(($vpr['cost'] * $diffusageprice['value'] / 100) * $results[$movedata[1]][0][$kpr]['days'] + $vpr['cost'], 2) : round(($vpr['cost'] * (100 + $diffusageprice['value']) / 100), 2);
											$results[$movedata[1]][0][$kpr]['diffusagecost'] = "+".$diffusageprice['value']."%";
											$results[$movedata[1]][0][$kpr]['cost'] = $aduseval;
										}
									} else {
										//discount
										if ($diffusageprice['valpcent'] == 1) {
											//fixed value
											$results[$movedata[1]][0][$kpr]['diffusagecostpernight'] = $diffusageprice['pernight'] == 1 ? 1 : 0;
											$aduseval = $diffusageprice['pernight'] == 1 ? $diffusageprice['value'] * $results[$movedata[1]][0][$kpr]['days'] : $diffusageprice['value'];
											$results[$movedata[1]][0][$kpr]['diffusagecost'] = "-".$aduseval;
											$results[$movedata[1]][0][$kpr]['cost'] = $vpr['cost'] - $aduseval;
										} else {
											//percentage value
											$results[$movedata[1]][0][$kpr]['diffusagecostpernight'] = $diffusageprice['pernight'] == 1 ? $vpr['cost'] : 0;
											$aduseval = $diffusageprice['pernight'] == 1 ? round($vpr['cost'] - ((($vpr['cost'] / $results[$movedata[1]][0][$kpr]['days']) * $diffusageprice['value'] / 100) * $results[$movedata[1]][0][$kpr]['days']), 2) : round(($vpr['cost'] * (100 - $diffusageprice['value']) / 100), 2);
											$results[$movedata[1]][0][$kpr]['diffusagecost'] = "-".$diffusageprice['value']."%";
											$results[$movedata[1]][0][$kpr]['cost'] = $aduseval;
										}
									}
								}
							}
						}
					}
					//end check if moved rooms had charges/discounts for adults occupancy to update it
				}
				//
			}
		}
		//
		$results = VikBooking::sortMultipleResults($results);

		// save prices in session for the modules
		$sessvals = [];
		$modprices = [];
		$sessvals['roomsnum'] = $proomsnum;
		$sessvals['checkin'] = $first;
		$sessvals['checkout'] = $second;
		for ($i = 1; $i <= $proomsnum; $i++) {
			if (!isset($results[$i])) {
				continue;
			}
			foreach ($results[$i] as $indres => $res) {
				$modprices[$i][] = $res[0]['cost'];
			}
		}
		for ($i = 1; $i <= $proomsnum; $i++) {
			if (!isset($modprices[$i])) {
				continue;
			}
			$mincost = min($modprices[$i]);
			$maxcost = max($modprices[$i]);
			$sessvals[$i]['min'] = $mincost;
			$sessvals[$i]['max'] = $maxcost;
			$sessvals[$i]['adults'] = $arrpeople[$i]['adults'];
			$sessvals[$i]['children'] = $arrpeople[$i]['children'];
		}
		$session->set('vbsearchdata', $sessvals);

		/**
		 * Build a pool of room rates in the search results before filtering by price.
		 * 
		 * @since 	1.18.3 (J) - 1.8.3 (WP)
		 */
		$rates_pool = [];
		foreach ($results as $roomPartyIndex => $roomPartyRooms) {
			foreach ($roomPartyRooms as $roomPartyRoom) {
				foreach ($roomPartyRoom as $roomRateResult) {
					// take only the first (lowest) rate plan
					$rates_pool[] = (float) ($roomRateResult['cost'] ?? 0);
					break;
				}
			}
		}
		$rates_pool = array_values(array_filter($rates_pool));

		/**
		 * Apply price filters (legacy compatibility also available with "pricefilter module").
		 * 
		 * @since 	1.18.3 (J) - 1.8.3 (WP) added support to search results filtering options.
		 */
		$ppricefrom = VikRequest::getInt('r1pricefrom', '', 'request');
		$ppriceto = VikRequest::getInt('r1priceto', '', 'request');
		$price_range_filtering = false;
		if (($resultFilters['price']['min'] ?? 0) && ($resultFilters['price']['max'] ?? 0)) {
			// use search results filtering options
			$ppricefrom = (float) $resultFilters['price']['min'];
			$ppriceto   = (float) $resultFilters['price']['max'];
			$price_range_filtering = true;
		}
		if (!empty($ppricefrom) && !empty($ppriceto)) {
			foreach ($results as $oknroom => $res) {
				$totroomres = count($res);
				foreach ($res as $kr => $rr) {
					if ($oknroom > 1 && !$price_range_filtering) {
						$ppricefrom = VikRequest::getInt('r'.$oknroom.'pricefrom', '', 'request');
						$ppriceto = VikRequest::getInt('r'.$oknroom.'priceto', '', 'request');
					}
					if (!empty($ppricefrom) && !empty($ppriceto)) {
						if ($rr[0]['cost'] < $ppricefrom || $rr[0]['cost'] > $ppriceto) {
							if ($totroomres > 1) {
								unset($results[$oknroom][$kr]);
								$totroomres--;
							}
						}
					}
				}
			}
		}

		if (count($results) == $proomsnum) {
			// check whether the user is coming from roomdetails
			$proomdetail = VikRequest::getInt('roomdetail', 0, 'request');
			$rate_plan_id = VikRequest::getInt('rate_plan_id', 0, 'request');
			$user_currency = VikRequest::getString('user_currency', '', 'request');
			$children_age = VikRequest::getVar('children_age', []);
			$pitemid = VikRequest::getInt('Itemid', 0, 'request');
			if (!empty($proomdetail) && array_key_exists($proomdetail, $arrtar) && $proomsnum == 1) {
				// push data to tracker and close
				VikBooking::getTracker()->pushRooms($proomdetail)->closeTrack();
				// store in the session that we are coming from the room details page
				$session->set('vboSearchRoomId', $proomdetail);

				/**
				 * Build redirect URI to the "showprc" View to continue the booking process, and
				 * keep some special vars that may serve to reflect the original booking link.
				 * 
				 * @since 	1.15.0 (J) - 1.5.0 (WP)
				 */
				$url_query_args = [
					'option' 	=> 'com_vikbooking',
					'task' 		=> 'showprc',
					'roomsnum' 	=> '1',
					'roomopt' 	=> [
						$proomdetail,
					],
					'adults' 	=> [
						$arrpeople[1]['adults'],
					],
					'children' 	=> [
						$arrpeople[1]['children'],
					],
					'days' 		=> $daysdiff,
					'checkin' 	=> $first,
					'checkout' 	=> $second,
				];
				if (!empty($ppkg_id)) {
					$url_query_args['pkg_id'] = $ppkg_id;
				}
				if (!empty($rate_plan_id)) {
					$url_query_args['rate_plan_id'] = $rate_plan_id;
				}
				if (is_array($children_age) && $children_age) {
					$url_query_args['children_age'] = $children_age;
				}
				if (!empty($user_currency)) {
					$url_query_args['user_currency'] = $user_currency;
				}
				if (!empty($pcategory_ids)) {
					$url_query_args['category_ids'] = $pcategory_ids;
				}
				if (!empty($pitemid)) {
					$url_query_args['Itemid'] = $pitemid;
				}
				
				$app->redirect(JRoute::rewrite('index.php?' . http_build_query($url_query_args), false));
				exit;
			} else {
				if (!empty($proomdetail) && $proomsnum == 1) {
					$q = "SELECT `id`,`name` FROM `#__vikbooking_rooms` WHERE `id`=".intval($proomdetail).";";
					$dbo->setQuery($q);
					$cdet = $dbo->loadAssocList();
					if ($cdet) {
						$vbo_tn->translateContents($cdet, '#__vikbooking_rooms');
						$msg = JText::sprintf('VBDETAILCNOTAVAIL', $cdet[0]['name'], $daysdiff);
						VikError::raiseWarning('', $msg);
						// push data to tracker and close
						VikBooking::getTracker()->pushRooms($proomdetail)->pushMessage($msg, 'warning')->closeTrack();
					}
				} elseif (!empty($proomdetail) && $proomsnum > 1) {
					//check whether the user is coming from roomdetails and if the room is available for any room party
					$room_missing = false;
					foreach ($results as $indroom => $rooms) {
						$room_found = false;
						foreach ($rooms as $room) {
							if ($room[0]['idroom'] == $proomdetail && (!array_key_exists('unitsavail', $room[0]) || $room[0]['unitsavail'] >= $proomsnum)) {
								$room_found = true;
								break;
							}
						}
						if (!$room_found) {
							$room_missing = true;
							break;
						}
					}
					if ($room_missing === false) {
						$aduchild_str = '';
						foreach ($arrpeople as $people) {
							$aduchild_str .= '&adults[]='.$people['adults'].'&children[]='.$people['children'];
						}
						$roomopt_str = '';
						for ($ri = 0; $ri < $proomsnum; $ri++) { 
							$roomopt_str .= '&roomopt[]='.$proomdetail;
						}
						// store in the session that we are coming from the room details page
						$session->set('vboSearchRoomId', $proomdetail);
						//
						$app->redirect(JRoute::rewrite("index.php?option=com_vikbooking&task=showprc&roomsnum=".$proomsnum.$roomopt_str.$aduchild_str."&days=".$daysdiff."&checkin=".$first."&checkout=".$second.(!empty($ppkg_id) ? "&pkg_id=" . $ppkg_id : "").(!empty($pitemid) ? "&Itemid=" . $pitemid : ""), false));
						// push data to tracker and close
						VikBooking::getTracker()->pushRooms(array_fill(0, $proomsnum, $proomdetail))->closeTrack();
						exit;
					} else {
						$q = "SELECT `id`,`name` FROM `#__vikbooking_rooms` WHERE `id`=".intval($proomdetail).";";
						$dbo->setQuery($q);
						$cdet = $dbo->loadAssocList();
						if ($cdet) {
							$vbo_tn->translateContents($cdet, '#__vikbooking_rooms');
							$msg = JText::sprintf('VBDETAILMULTIRNOTAVAIL', $proomsnum, $cdet[0]['name'], $daysdiff);
							VikError::raiseWarning('', $msg);
							// push data to tracker and close
							VikBooking::getTracker()->pushMessage($msg, 'warning')->closeTrack();
						}
					}
				}

				// filter unavailable listing IDs
				$unavailable_listings = array_filter(array_map('intval', array_unique($unavailable_listings)));

				// set View properties
				$this->res = $results;
				$this->days = $daysdiff;
				$this->checkin = $first;
				$this->checkout = $second;
				$this->roomsnum = $proomsnum;
				$this->arrpeople = $arrpeople;
				$showchildren = $showchildren ? 1 : 0;
				$this->showchildren = $showchildren;
				$this->js_overcounter = $js_overcounter;
				$this->mod_booking = $mod_booking;
				$this->unavailable_listings = $unavailable_listings;
				$this->rates_pool = $rates_pool;
				$this->vbo_tn = $vbo_tn;
				// theme
				$theme = VikBooking::getTheme();
				if ($theme != 'default') {
					$thdir = VBO_SITE_PATH.DS.'themes'.DS.$theme.DS.'search';
					if (is_dir($thdir)) {
						$this->_setPath('template', $thdir.DS);
					}
				}

				// unset the session if we were coming from the room details page
				$session->set('vboSearchRoomId', '');

				// close tracker
				VikBooking::getTracker()
					->setExtraInfo('results', $results)
					->closeTrack();

				parent::display($tpl);
			}
		} else {
			// zero results for some room
			$errmess = [];
			if (isset($critic) && $critic) {
				foreach ($critic as $nroom) {
					$errmess[] = $arrpeople[$nroom]['adults']." ".($arrpeople[$nroom]['adults'] == 1 ? JText::translate('VBSEARCHRESADULT') : JText::translate('VBSEARCHRESADULTS')).($arrpeople[$nroom]['children'] > 0 ? ", ".$arrpeople[$nroom]['children']." ".($arrpeople[$nroom]['children'] == 1 ? JText::translate('VBSEARCHRESCHILD') : JText::translate('VBSEARCHRESCHILDREN')) : "");
				}
				$errmess = array_unique($errmess);
			} else {
				$errmess[] = JText::translate('VBOSEARCHERRCODETHREEBASE');
			}
			$err_code_info = [
				'code' => 3,
				'fromts' => $first,
				'tots' => $second,
				'party' => $arrpeople
			];
			$msg = JText::sprintf('VBSEARCHERRNOTENOUGHROOMS', implode(" - ", $errmess));

			// push data to tracker and close
			VikBooking::getTracker()->pushMessage($msg, 'error')->closeTrack();

			$this->setVboError($msg, $err_code_info);
		}
	}

	protected function setVboError($err, $err_code_info = [])
	{
		$app = JFactory::getApplication();

		$ppkg_id = VikRequest::getInt('pkg_id', '', 'request');
		$pitemid = VikRequest::getInt('Itemid', '', 'request');

		if (!empty($ppkg_id)) {
			if (!empty($err)) {
				VikError::raiseWarning('', $err);
			}

			$app->redirect(JRoute::rewrite("index.php?option=com_vikbooking&view=packagedetails&pkgid=".$ppkg_id.(!empty($pitemid) ? "&Itemid=".$pitemid : ""), false));
			$app->close();
		} else {
			showSelectVb($err, $err_code_info);
		}
	}
}

```
