| @@ -1,0 +1,459 @@ | ||
| 1 | +<?php | |
| 2 | +/** | |
| 3 | + * @package VikBooking | |
| 4 | + * @subpackage com_vikbooking | |
| 5 | + * @author Alessio Gaggii - e4j - Extensionsforjoomla.com | |
| 6 | + * @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved. | |
| 7 | + * @license GNU General Public License version 2 or later; see LICENSE | |
| 8 | + * @link https://vikwp.com | |
| 9 | + */ | |
| 10 | + | |
| 11 | +defined('ABSPATH') or die('No script kiddies please!'); | |
| 12 | + | |
| 13 | +jimport('joomla.application.component.view'); | |
| 14 | + | |
| 15 | +class VikbookingViewBooking extends JViewVikBooking | |
| 16 | +{ | |
| 17 | + public function display($tpl = null) | |
| 18 | + { | |
| 19 | + $dbo = JFactory::getDbo(); | |
| 20 | + $app = JFactory::getApplication(); | |
| 21 | + $document = JFactory::getDocument(); | |
| 22 | + $vbo_tn = VikBooking::getTranslator(); | |
| 23 | + | |
| 24 | + // validation of data and availability before the rendering | |
| 25 | + $sid = VikRequest::getString('sid', '', 'request'); | |
| 26 | + $ts = VikRequest::getString('ts', '', 'request'); | |
| 27 | + if (empty($sid) || empty($ts)) { | |
| 28 | + showSelectVb(JText::translate('VBINSUFDATA')); | |
| 29 | + return; | |
| 30 | + } | |
| 31 | + | |
| 32 | + $q = "SELECT `o`.*,(SELECT SUM(`or`.`adults`) FROM `#__vikbooking_ordersrooms` AS `or` WHERE `or`.`idorder`=`o`.`id`) AS `tot_adults` FROM `#__vikbooking_orders` AS `o` WHERE (`o`.`sid`=" . $dbo->quote($sid) . " OR `o`.`idorderota`=" . $dbo->quote($sid) . ") AND `o`.`ts`=" . $dbo->quote($ts); | |
| 33 | + $dbo->setQuery($q, 0, 1); | |
| 34 | + $order = $dbo->loadAssoc(); | |
| 35 | + if (!$order) { | |
| 36 | + showSelectVb(JText::translate('VBORDERNOTFOUND')); | |
| 37 | + return; | |
| 38 | + } | |
| 39 | + | |
| 40 | + // availability helper | |
| 41 | + $av_helper = VikBooking::getAvailabilityInstance(); | |
| 42 | + | |
| 43 | + // room stay dates in case of split stay | |
| 44 | + $room_stay_dates = []; | |
| 45 | + if ($order['split_stay']) { | |
| 46 | + if ($order['status'] == 'confirmed') { | |
| 47 | + $room_stay_dates = $av_helper->loadSplitStayBusyRecords($order['id']); | |
| 48 | + } else { | |
| 49 | + $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $order['id'], []); | |
| 50 | + } | |
| 51 | + } | |
| 52 | + | |
| 53 | + if ($order['status'] == 'confirmed') { | |
| 54 | + // prepare impression data for channels | |
| 55 | + $impressiondata = $order; | |
| 56 | + $q = "SELECT `or`.`idtar`,`d`.`idprice`,`p`.`idiva`,`t`.`aliq`,`t`.`taxcap` FROM `#__vikbooking_ordersrooms` AS `or` " . | |
| 57 | + "LEFT JOIN `#__vikbooking_dispcost` `d` ON `d`.`id`=`or`.`idtar` " . | |
| 58 | + "LEFT JOIN `#__vikbooking_prices` `p` ON `p`.`id`=`d`.`idprice` " . | |
| 59 | + "LEFT JOIN `#__vikbooking_iva` `t` ON `t`.`id`=`p`.`idiva` " . | |
| 60 | + "WHERE `or`.`idorder`='".$order['id']."' ORDER BY `t`.`aliq` ASC"; | |
| 61 | + $dbo->setQuery($q, 0, 1); | |
| 62 | + $taxdata = $dbo->loadAssoc(); | |
| 63 | + if ($taxdata) { | |
| 64 | + $taxes = 0; | |
| 65 | + if (!empty($taxdata['aliq'])) { | |
| 66 | + $realtotal = round(($order['total'] / ((100 + $taxdata['aliq']) / 100)), 2); | |
| 67 | + $taxes = round(($order['total'] - $realtotal), 2); | |
| 68 | + /** | |
| 69 | + * Tax Cap implementation. | |
| 70 | + * | |
| 71 | + * @since 1.12 | |
| 72 | + */ | |
| 73 | + if ($taxdata['taxcap'] > 0 && $taxes > $taxdata['taxcap']) { | |
| 74 | + $realtotal = $order['total'] - $taxdata['taxcap']; | |
| 75 | + $taxes = $taxdata['taxcap']; | |
| 76 | + } | |
| 77 | + $impressiondata['total'] = $realtotal; | |
| 78 | + } | |
| 79 | + $impressiondata['taxes'] = $taxes; | |
| 80 | + $impressiondata['fees'] = 0; | |
| 81 | + } | |
| 82 | + VikBooking::invokeChannelManager(true, $impressiondata); | |
| 83 | + // end prepare impression data for channels | |
| 84 | + } elseif ($order['status'] == 'standby') { | |
| 85 | + $roomavail = false; | |
| 86 | + | |
| 87 | + $q = $dbo->getQuery(true) | |
| 88 | + ->select($dbo->qn('or') . '.*') | |
| 89 | + ->select($dbo->qn('r.units')) | |
| 90 | + ->from($dbo->qn('#__vikbooking_ordersrooms', 'or')) | |
| 91 | + ->leftJoin($dbo->qn('#__vikbooking_rooms', 'r') . ' ON ' . $dbo->qn('or.idroom') . ' = ' . $dbo->qn('r.id')) | |
| 92 | + ->where($dbo->qn('or.idorder') . ' = ' . (int)$order['id']) | |
| 93 | + ->order($dbo->qn('or.id') . ' ASC'); | |
| 94 | + | |
| 95 | + $dbo->setQuery($q); | |
| 96 | + $orderrooms = $dbo->loadAssocList(); | |
| 97 | + | |
| 98 | + /** | |
| 99 | + * Even if redundant, we read the eventually occupied records by this booking ID to ensure | |
| 100 | + * server-side cache will not produce a booking cancellation after receiving a valid payment. | |
| 101 | + * An eventual list of busy record IDs is passed to the method for checking if the room is | |
| 102 | + * available, because the status of the reservation may actually be confirmed already. | |
| 103 | + * | |
| 104 | + * @since 1.16.9 (J) - 1.6.9 (WP) | |
| 105 | + */ | |
| 106 | + $dbo->setQuery( | |
| 107 | + $dbo->getQuery(true) | |
| 108 | + ->select($dbo->qn('idbusy')) | |
| 109 | + ->from($dbo->qn('#__vikbooking_ordersbusy')) | |
| 110 | + ->where($dbo->qn('idorder') . ' = ' . (int)$order['id']) | |
| 111 | + ); | |
| 112 | + $skip_busy_ids = array_column($dbo->loadAssocList(), 'idbusy'); | |
| 113 | + | |
| 114 | + foreach ($orderrooms as $kor => $or) { | |
| 115 | + // determine proper values for this room | |
| 116 | + $room_stay_checkin = $order['checkin']; | |
| 117 | + $room_stay_checkout = $order['checkout']; | |
| 118 | + if ($order['split_stay'] && $room_stay_dates && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom']) { | |
| 119 | + $room_stay_checkin = !empty($room_stay_dates[$kor]['checkin_ts']) ? $room_stay_dates[$kor]['checkin_ts'] : $room_stay_dates[$kor]['checkin']; | |
| 120 | + $room_stay_checkout = !empty($room_stay_dates[$kor]['checkout_ts']) ? $room_stay_dates[$kor]['checkout_ts'] : $room_stay_dates[$kor]['checkout']; | |
| 121 | + } | |
| 122 | + | |
| 123 | + // make sure room is still available | |
| 124 | + $roomavail = VikBooking::roomBookable($or['idroom'], $or['units'], $room_stay_checkin, $room_stay_checkout, $skip_busy_ids); | |
| 125 | + if (!$roomavail) { | |
| 126 | + break; | |
| 127 | + } | |
| 128 | + } | |
| 129 | + | |
| 130 | + // tell if the booking has been automatically cancelled | |
| 131 | + $autoremove = false; | |
| 132 | + | |
| 133 | + // make sure we are not beyond the check-in date and time | |
| 134 | + $today_midnight = mktime(0, 0, 0, date('n'), date('j'), date('Y')); | |
| 135 | + if ($today_midnight > $order['checkin']) { | |
| 136 | + $roomavail = false; | |
| 137 | + } | |
| 138 | + | |
| 139 | + /** | |
| 140 | + * Check if we are dealing with a pending booking solution of a quote. | |
| 141 | + * | |
| 142 | + * @since 1.18.8 (J) - 1.8.8 (WP) | |
| 143 | + */ | |
| 144 | + $quoteValidUntilDt = null; | |
| 145 | + if (!empty($order['idquote'])) { | |
| 146 | + // load quote record | |
| 147 | + $quoteRecord = VBOMvcModel::getInstance('quote')->getItem((int) $order['idquote']); | |
| 148 | + if (!empty($quoteRecord->valid_until)) { | |
| 149 | + $quoteValidUntilDt = JFactory::getDate($quoteRecord->valid_until); | |
| 150 | + } | |
| 151 | + } | |
| 152 | + | |
| 153 | + if ($quoteValidUntilDt) { | |
| 154 | + // make sure the quote is still valid | |
| 155 | + if (JFactory::getDate('now') > $quoteValidUntilDt) { | |
| 156 | + $roomavail = false; | |
| 157 | + $autoremove = true; | |
| 158 | + } | |
| 159 | + } else { | |
| 160 | + // check global settings for booking auto-cancellation | |
| 161 | + $minautoremove = VikBooking::getMinutesAutoRemove(); | |
| 162 | + $mins_elapsed = floor((time() - $order['ts']) / 60); | |
| 163 | + if ($minautoremove > 0 && $mins_elapsed > $minautoremove) { | |
| 164 | + $roomavail = false; | |
| 165 | + $autoremove = true; | |
| 166 | + } | |
| 167 | + } | |
| 168 | + | |
| 169 | + /** | |
| 170 | + * Make sure not to cancel an OTA pending reservation. | |
| 171 | + * | |
| 172 | + * @since 1.15.4 (J) - 1.5.10 (WP) | |
| 173 | + */ | |
| 174 | + $is_ota_pending = (!empty($order['type']) && !empty($order['channel'])); | |
| 175 | + | |
| 176 | + if ($roomavail || $is_ota_pending) { | |
| 177 | + // invoke channel impression | |
| 178 | + VikBooking::invokeChannelManager(false); | |
| 179 | + // | |
| 180 | + } else { | |
| 181 | + // set the booking to cancelled | |
| 182 | + $q = "UPDATE `#__vikbooking_orders` SET `status`='cancelled' WHERE `id`=".(int)$order['id'].";"; | |
| 183 | + $dbo->setQuery($q); | |
| 184 | + $dbo->execute(); | |
| 185 | + // update status in the array | |
| 186 | + $order['status'] = 'cancelled'; | |
| 187 | + // | |
| 188 | + $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . (int)$order['id'] . ";"; | |
| 189 | + $dbo->setQuery($q); | |
| 190 | + $dbo->execute(); | |
| 191 | + $q = "SELECT * FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$order['id'].";"; | |
| 192 | + $dbo->setQuery($q); | |
| 193 | + $ordbusy = $dbo->loadAssocList(); | |
| 194 | + if ($ordbusy) { | |
| 195 | + foreach ($ordbusy as $ob) { | |
| 196 | + $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`=".(int)$ob['idbusy'].";"; | |
| 197 | + $dbo->setQuery($q); | |
| 198 | + $dbo->execute(); | |
| 199 | + } | |
| 200 | + } | |
| 201 | + $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$order['id'].";"; | |
| 202 | + $dbo->setQuery($q); | |
| 203 | + $dbo->execute(); | |
| 204 | + | |
| 205 | + /** | |
| 206 | + * We now save onto a var the full error message to understand what made the booking become cancelled. | |
| 207 | + * This way, by checking the booking history it will be easy to understand the cause of the issue. | |
| 208 | + * | |
| 209 | + * @since October 15th 2020 | |
| 210 | + */ | |
| 211 | + $history_err_descr = ''; | |
| 212 | + if ($today_midnight > $order['checkin']) { | |
| 213 | + $history_err_descr = JText::translate('VBOBOOKNOLONGERPAYABLE'); | |
| 214 | + } elseif ($autoremove === true) { | |
| 215 | + $history_err_descr = JText::translate('VBOERRAUTOREMOVED'); | |
| 216 | + } else { | |
| 217 | + $history_err_descr = JText::translate('VBERRREPSEARCH'); | |
| 218 | + } | |
| 219 | + if (!empty($history_err_descr)) { | |
| 220 | + VikError::raiseWarning('', $history_err_descr); | |
| 221 | + } | |
| 222 | + | |
| 223 | + //Booking History | |
| 224 | + VikBooking::getBookingHistoryInstance()->setBid($order['id'])->store('CA', $history_err_descr); | |
| 225 | + // | |
| 226 | + } | |
| 227 | + } | |
| 228 | + | |
| 229 | + // render the booking details | |
| 230 | + | |
| 231 | + // set noindex instruction for robots | |
| 232 | + $document->setMetaData('robots', 'noindex, nofollow'); | |
| 233 | + | |
| 234 | + // load jQuery | |
| 235 | + if (VikBooking::loadJquery()) { | |
| 236 | + JHtml::fetch('jquery.framework', true, true); | |
| 237 | + } | |
| 238 | + | |
| 239 | + $tars = []; | |
| 240 | + $cookie = $app->input->cookie; | |
| 241 | + $pcheckin = $order['checkin']; | |
| 242 | + $pcheckout = $order['checkout']; | |
| 243 | + $secdiff = $pcheckout - $pcheckin; | |
| 244 | + $daysdiff = $secdiff / 86400; | |
| 245 | + if (is_int($daysdiff)) { | |
| 246 | + if ($daysdiff < 1) { | |
| 247 | + $daysdiff = 1; | |
| 248 | + } | |
| 249 | + } else { | |
| 250 | + if ($daysdiff < 1) { | |
| 251 | + $daysdiff = 1; | |
| 252 | + } else { | |
| 253 | + $sum = floor($daysdiff) * 86400; | |
| 254 | + $newdiff = $secdiff - $sum; | |
| 255 | + $maxhmore = VikBooking::getHoursMoreRb() * 3600; | |
| 256 | + if ($maxhmore >= $newdiff) { | |
| 257 | + $daysdiff = floor($daysdiff); | |
| 258 | + } else { | |
| 259 | + $daysdiff = ceil($daysdiff); | |
| 260 | + } | |
| 261 | + } | |
| 262 | + } | |
| 263 | + $is_package = !empty($order['pkg']) ? true : false; | |
| 264 | + | |
| 265 | + // get the rooms booked list | |
| 266 | + $dbo->setQuery( | |
| 267 | + $dbo->getQuery(true) | |
| 268 | + ->select($dbo->qn('or') . '.*') | |
| 269 | + ->select($dbo->qn('r.id', 'r_reference_id')) | |
| 270 | + ->select([ | |
| 271 | + $dbo->qn('r.name'), | |
| 272 | + $dbo->qn('r.img'), | |
| 273 | + $dbo->qn('r.idcarat'), | |
| 274 | + $dbo->qn('r.fromadult'), | |
| 275 | + $dbo->qn('r.toadult'), | |
| 276 | + ]) | |
| 277 | + ->from($dbo->qn('#__vikbooking_ordersrooms', 'or')) | |
| 278 | + ->leftJoin($dbo->qn('#__vikbooking_rooms', 'r') . ' ON ' . $dbo->qn('or.idroom') . ' = ' . $dbo->qn('r.id')) | |
| 279 | + ->where($dbo->qn('or.idorder') . ' = ' . (int)$order['id']) | |
| 280 | + ->order($dbo->qn('or.id') . ' ASC') | |
| 281 | + ); | |
| 282 | + $orderrooms = $dbo->loadAssocList(); | |
| 283 | + | |
| 284 | + if ($orderrooms) { | |
| 285 | + $vbo_tn->translateContents($orderrooms, '#__vikbooking_rooms', array('id' => 'r_reference_id')); | |
| 286 | + foreach ($orderrooms as $kor => $or) { | |
| 287 | + $num = $kor + 1; | |
| 288 | + if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) { | |
| 289 | + //package or custom cost set from the back-end | |
| 290 | + continue; | |
| 291 | + } | |
| 292 | + $q = "SELECT `t`.*,`p`.`name`,`p`.`free_cancellation`,`p`.`canc_deadline`,`p`.`canc_policy` FROM `#__vikbooking_dispcost` AS `t` LEFT JOIN `#__vikbooking_prices` AS `p` ON `t`.`idprice`=`p`.`id` WHERE `t`.`id`=" . (int)$or['idtar'] . ";"; | |
| 293 | + $dbo->setQuery($q); | |
| 294 | + $tar = $dbo->loadAssocList(); | |
| 295 | + if (!$tar) { | |
| 296 | + continue; | |
| 297 | + } | |
| 298 | + $vbo_tn->translateContents($tar, '#__vikbooking_prices', array('id' => 'idprice')); | |
| 299 | + | |
| 300 | + // determine proper values for this room | |
| 301 | + $room_stay_checkin = $order['checkin']; | |
| 302 | + $room_stay_checkout = $order['checkout']; | |
| 303 | + $room_stay_nights = $order['days']; | |
| 304 | + if ($order['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom']) { | |
| 305 | + $room_stay_checkin = !empty($room_stay_dates[$kor]['checkin_ts']) ? $room_stay_dates[$kor]['checkin_ts'] : $room_stay_dates[$kor]['checkin']; | |
| 306 | + $room_stay_checkout = !empty($room_stay_dates[$kor]['checkout_ts']) ? $room_stay_dates[$kor]['checkout_ts'] : $room_stay_dates[$kor]['checkout']; | |
| 307 | + $room_stay_nights = $av_helper->countNightsOfStay($room_stay_checkin, $room_stay_checkout); | |
| 308 | + } | |
| 309 | + | |
| 310 | + // apply seasonal rates | |
| 311 | + $tar = VikBooking::applySeasonsRoom($tar, $room_stay_checkin, $room_stay_checkout); | |
| 312 | + | |
| 313 | + // apply OBP rules | |
| 314 | + $tar = VBORoomHelper::getInstance()->applyOBPRules($tar, $or, $or['adults']); | |
| 315 | + | |
| 316 | + // push tariffs | |
| 317 | + $tars[$num] = $tar[0]; | |
| 318 | + } | |
| 319 | + } | |
| 320 | + | |
| 321 | + $days_to_arrival = 0; | |
| 322 | + $now_info = getdate(); | |
| 323 | + $checkin_info = getdate($order['checkin']); | |
| 324 | + if ($now_info[0] < $checkin_info[0]) { | |
| 325 | + while ($now_info[0] < $checkin_info[0]) { | |
| 326 | + if (!($now_info['mday'] != $checkin_info['mday'] || $now_info['mon'] != $checkin_info['mon'] || $now_info['year'] != $checkin_info['year'])) { | |
| 327 | + break; | |
| 328 | + } | |
| 329 | + $days_to_arrival++; | |
| 330 | + $now_info = getdate(mktime(0, 0, 0, $now_info['mon'], ($now_info['mday'] + 1), $now_info['year'])); | |
| 331 | + } | |
| 332 | + } | |
| 333 | + | |
| 334 | + $is_refundable = 0; | |
| 335 | + $daysadv_refund_arr = []; | |
| 336 | + $daysadv_refund = 0; | |
| 337 | + $canc_policy = ''; | |
| 338 | + foreach ($tars as $num => $tar) { | |
| 339 | + if ($tar['free_cancellation'] < 1) { | |
| 340 | + // if at least one rate plan is non-refundable, the whole reservation cannot be cancelled | |
| 341 | + $is_refundable = 0; | |
| 342 | + $daysadv_refund_arr = []; | |
| 343 | + break; | |
| 344 | + } | |
| 345 | + $is_refundable = 1; | |
| 346 | + $daysadv_refund_arr[] = $tar['canc_deadline']; | |
| 347 | + } | |
| 348 | + | |
| 349 | + // get the rate plan with the lowest cancellation deadline | |
| 350 | + $daysadv_refund = count($daysadv_refund_arr) > 0 ? min($daysadv_refund_arr) : $daysadv_refund; | |
| 351 | + if ($daysadv_refund > 0) { | |
| 352 | + foreach ($tars as $num => $tar) { | |
| 353 | + if ($tar['free_cancellation'] > 0 && $tar['canc_deadline'] == $daysadv_refund) { | |
| 354 | + //get the cancellation policy from the first rate plan with free cancellation and same cancellation deadline | |
| 355 | + $canc_policy = $tar['canc_policy']; | |
| 356 | + break; | |
| 357 | + } | |
| 358 | + } | |
| 359 | + } | |
| 360 | + | |
| 361 | + $payment = ""; | |
| 362 | + if (!empty($order['idpayment'])) { | |
| 363 | + $exppay = explode('=', $order['idpayment']); | |
| 364 | + $payment = VikBooking::getPayment($exppay[0], $vbo_tn); | |
| 365 | + } | |
| 366 | + $pnodep = VikRequest::getString('nodep', '', 'request'); | |
| 367 | + $cnodep = $cookie->get('vboFA', '', 'string'); | |
| 368 | + $nodep = intval($pnodep) > 0 || intval($cnodep) > 0 ? 1 : 0; | |
| 369 | + | |
| 370 | + /** | |
| 371 | + * Scan the booking and related rooms for damage deposit payment data. | |
| 372 | + * Obtain also the information about possible previous payments. | |
| 373 | + * | |
| 374 | + * @since 1.17.6 (J) - 1.7.6 (WP) | |
| 375 | + */ | |
| 376 | + $damage_deposit_payment = VBORoomHelper::getInstance()->getDamageDepositSplitPayment($order, $orderrooms); | |
| 377 | + $prev_dd_payments = []; | |
| 378 | + if ($damage_deposit_payment['damagedep_gross'] ?? 0) { | |
| 379 | + $prev_dd_payments = VikBooking::getBookingHistoryInstance($order['id']) | |
| 380 | + ->getEventsWithData('PN', function($data) { | |
| 381 | + return (is_object($data) && !empty($data->damage_deposit)); | |
| 382 | + }); | |
| 383 | + if (!$prev_dd_payments) { | |
| 384 | + // check also the first payment event in case of OTA bookings | |
| 385 | + $prev_dd_payments = VikBooking::getBookingHistoryInstance($order['id']) | |
| 386 | + ->getEventsWithData('P0', function($data) { | |
| 387 | + return (is_object($data) && !empty($data->damage_deposit)); | |
| 388 | + }); | |
| 389 | + } | |
| 390 | + } | |
| 391 | + | |
| 392 | + /** | |
| 393 | + * Upselling extra services only for non-cancelled bookings | |
| 394 | + * with a check-out date in the future. | |
| 395 | + * | |
| 396 | + * @since 1.13.0 (J) - 1.3.0 (WP) | |
| 397 | + */ | |
| 398 | + $upselling = []; | |
| 399 | + if ($order['status'] != 'cancelled' && VikBooking::upsellingEnabled() && $order['checkout'] > time()) { | |
| 400 | + $upsell_data = []; | |
| 401 | + foreach ($orderrooms as $kor => $or) { | |
| 402 | + $room_data = new stdClass; | |
| 403 | + $room_data->id = $or['idroom']; | |
| 404 | + $room_data->name = $or['name']; | |
| 405 | + $room_data->img = $or['img']; | |
| 406 | + $room_data->adults = $or['adults']; | |
| 407 | + $room_data->children = $or['children']; | |
| 408 | + $room_data->nights = $order['days']; | |
| 409 | + $room_data->options = []; | |
| 410 | + if (!empty($or['optionals'])) { | |
| 411 | + $optids = explode(';', $or['optionals']); | |
| 412 | + foreach ($optids as $optid) { | |
| 413 | + if (!empty($optid)) { | |
| 414 | + // the : may express the quantity | |
| 415 | + $optidparts = explode(':', $optid); | |
| 416 | + array_push($room_data->options, (int)$optidparts[0]); | |
| 417 | + } | |
| 418 | + } | |
| 419 | + } | |
| 420 | + array_push($upsell_data, $room_data); | |
| 421 | + } | |
| 422 | + $upselling = VikBooking::loadUpsellingData($upsell_data, $order, $vbo_tn); | |
| 423 | + } | |
| 424 | + | |
| 425 | + /** | |
| 426 | + * Trigger first tracking conversion on booking. | |
| 427 | + * | |
| 428 | + * @since 1.16.3 (J) - 1.6.3 (WP) | |
| 429 | + */ | |
| 430 | + VikBooking::getTracker()->triggerBookingConversion($order); | |
| 431 | + | |
| 432 | + $this->ord = $order; | |
| 433 | + $this->orderrooms = $orderrooms; | |
| 434 | + $this->tars = $tars; | |
| 435 | + $this->days_to_arrival = $days_to_arrival; | |
| 436 | + $this->is_refundable = $is_refundable; | |
| 437 | + $this->daysadv_refund = $daysadv_refund; | |
| 438 | + $this->canc_policy = $canc_policy; | |
| 439 | + $this->payment = $payment; | |
| 440 | + $this->nodep = $nodep; | |
| 441 | + $this->upselling = $upselling; | |
| 442 | + $this->damage_deposit_payment = $damage_deposit_payment; | |
| 443 | + $this->prev_dd_payments = $prev_dd_payments; | |
| 444 | + $this->quoteValidUntilDt = $quoteValidUntilDt ?? null; | |
| 445 | + $this->vbo_tn = $vbo_tn; | |
| 446 | + | |
| 447 | + // theme | |
| 448 | + $theme = VikBooking::getTheme(); | |
| 449 | + if ($theme != 'default') { | |
| 450 | + $thdir = VBO_SITE_PATH . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $theme . DIRECTORY_SEPARATOR . 'booking'; | |
| 451 | + if (is_dir($thdir)) { | |
| 452 | + $this->_setPath('template', $thdir . DIRECTORY_SEPARATOR); | |
| 453 | + } | |
| 454 | + } | |
| 455 | + // | |
| 456 | + | |
| 457 | + parent::display($tpl); | |
| 458 | + } | |
| 459 | +} | |