quote($sid) . " OR `o`.`idorderota`=" . $dbo->quote($sid) . ") AND `o`.`ts`=" . $dbo->quote($ts); $dbo->setQuery($q, 0, 1); $order = $dbo->loadAssoc(); if (!$order) { showSelectVb(JText::translate('VBORDERNOTFOUND')); return; } // availability helper $av_helper = VikBooking::getAvailabilityInstance(); // room stay dates in case of split stay $room_stay_dates = []; if ($order['split_stay']) { if ($order['status'] == 'confirmed') { $room_stay_dates = $av_helper->loadSplitStayBusyRecords($order['id']); } else { $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $order['id'], []); } } if ($order['status'] == 'confirmed') { // prepare impression data for channels $impressiondata = $order; $q = "SELECT `or`.`idtar`,`d`.`idprice`,`p`.`idiva`,`t`.`aliq`,`t`.`taxcap` FROM `#__vikbooking_ordersrooms` AS `or` " . "LEFT JOIN `#__vikbooking_dispcost` `d` ON `d`.`id`=`or`.`idtar` " . "LEFT JOIN `#__vikbooking_prices` `p` ON `p`.`id`=`d`.`idprice` " . "LEFT JOIN `#__vikbooking_iva` `t` ON `t`.`id`=`p`.`idiva` " . "WHERE `or`.`idorder`='".$order['id']."' ORDER BY `t`.`aliq` ASC"; $dbo->setQuery($q, 0, 1); $taxdata = $dbo->loadAssoc(); if ($taxdata) { $taxes = 0; if (!empty($taxdata['aliq'])) { $realtotal = round(($order['total'] / ((100 + $taxdata['aliq']) / 100)), 2); $taxes = round(($order['total'] - $realtotal), 2); /** * Tax Cap implementation. * * @since 1.12 */ if ($taxdata['taxcap'] > 0 && $taxes > $taxdata['taxcap']) { $realtotal = $order['total'] - $taxdata['taxcap']; $taxes = $taxdata['taxcap']; } $impressiondata['total'] = $realtotal; } $impressiondata['taxes'] = $taxes; $impressiondata['fees'] = 0; } VikBooking::invokeChannelManager(true, $impressiondata); // end prepare impression data for channels } elseif ($order['status'] == 'standby') { $roomavail = false; $q = $dbo->getQuery(true) ->select($dbo->qn('or') . '.*') ->select($dbo->qn('r.units')) ->from($dbo->qn('#__vikbooking_ordersrooms', 'or')) ->leftJoin($dbo->qn('#__vikbooking_rooms', 'r') . ' ON ' . $dbo->qn('or.idroom') . ' = ' . $dbo->qn('r.id')) ->where($dbo->qn('or.idorder') . ' = ' . (int)$order['id']) ->order($dbo->qn('or.id') . ' ASC'); $dbo->setQuery($q); $orderrooms = $dbo->loadAssocList(); /** * Even if redundant, we read the eventually occupied records by this booking ID to ensure * server-side cache will not produce a booking cancellation after receiving a valid payment. * An eventual list of busy record IDs is passed to the method for checking if the room is * available, because the status of the reservation may actually be confirmed already. * * @since 1.16.9 (J) - 1.6.9 (WP) */ $dbo->setQuery( $dbo->getQuery(true) ->select($dbo->qn('idbusy')) ->from($dbo->qn('#__vikbooking_ordersbusy')) ->where($dbo->qn('idorder') . ' = ' . (int)$order['id']) ); $skip_busy_ids = array_column($dbo->loadAssocList(), 'idbusy'); foreach ($orderrooms as $kor => $or) { // determine proper values for this room $room_stay_checkin = $order['checkin']; $room_stay_checkout = $order['checkout']; if ($order['split_stay'] && $room_stay_dates && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom']) { $room_stay_checkin = !empty($room_stay_dates[$kor]['checkin_ts']) ? $room_stay_dates[$kor]['checkin_ts'] : $room_stay_dates[$kor]['checkin']; $room_stay_checkout = !empty($room_stay_dates[$kor]['checkout_ts']) ? $room_stay_dates[$kor]['checkout_ts'] : $room_stay_dates[$kor]['checkout']; } // make sure room is still available $roomavail = VikBooking::roomBookable($or['idroom'], $or['units'], $room_stay_checkin, $room_stay_checkout, $skip_busy_ids); if (!$roomavail) { break; } } // tell if the booking has been automatically cancelled $autoremove = false; // make sure we are not beyond the check-in date and time $today_midnight = mktime(0, 0, 0, date('n'), date('j'), date('Y')); if ($today_midnight > $order['checkin']) { $roomavail = false; } /** * Check if we are dealing with a pending booking solution of a quote. * * @since 1.18.8 (J) - 1.8.8 (WP) */ $quoteValidUntilDt = null; if (!empty($order['idquote'])) { // load quote record $quoteRecord = VBOMvcModel::getInstance('quote')->getItem((int) $order['idquote']); if (!empty($quoteRecord->valid_until)) { $quoteValidUntilDt = JFactory::getDate($quoteRecord->valid_until); } } if ($quoteValidUntilDt) { // make sure the quote is still valid if (JFactory::getDate('now') > $quoteValidUntilDt) { $roomavail = false; $autoremove = true; } } else { // check global settings for booking auto-cancellation $minautoremove = VikBooking::getMinutesAutoRemove(); $mins_elapsed = floor((time() - $order['ts']) / 60); if ($minautoremove > 0 && $mins_elapsed > $minautoremove) { $roomavail = false; $autoremove = true; } } /** * Make sure not to cancel an OTA pending reservation. * * @since 1.15.4 (J) - 1.5.10 (WP) */ $is_ota_pending = (!empty($order['type']) && !empty($order['channel'])); if ($roomavail || $is_ota_pending) { // invoke channel impression VikBooking::invokeChannelManager(false); // } else { // set the booking to cancelled $q = "UPDATE `#__vikbooking_orders` SET `status`='cancelled' WHERE `id`=".(int)$order['id'].";"; $dbo->setQuery($q); $dbo->execute(); // update status in the array $order['status'] = 'cancelled'; // $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . (int)$order['id'] . ";"; $dbo->setQuery($q); $dbo->execute(); $q = "SELECT * FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$order['id'].";"; $dbo->setQuery($q); $ordbusy = $dbo->loadAssocList(); if ($ordbusy) { foreach ($ordbusy as $ob) { $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`=".(int)$ob['idbusy'].";"; $dbo->setQuery($q); $dbo->execute(); } } $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$order['id'].";"; $dbo->setQuery($q); $dbo->execute(); /** * We now save onto a var the full error message to understand what made the booking become cancelled. * This way, by checking the booking history it will be easy to understand the cause of the issue. * * @since October 15th 2020 */ $history_err_descr = ''; if ($today_midnight > $order['checkin']) { $history_err_descr = JText::translate('VBOBOOKNOLONGERPAYABLE'); } elseif ($autoremove === true) { $history_err_descr = JText::translate('VBOERRAUTOREMOVED'); } else { $history_err_descr = JText::translate('VBERRREPSEARCH'); } if (!empty($history_err_descr)) { VikError::raiseWarning('', $history_err_descr); } //Booking History VikBooking::getBookingHistoryInstance()->setBid($order['id'])->store('CA', $history_err_descr); // } } // render the booking details // set noindex instruction for robots $document->setMetaData('robots', 'noindex, nofollow'); // load jQuery if (VikBooking::loadJquery()) { JHtml::fetch('jquery.framework', true, true); } $tars = []; $cookie = $app->input->cookie; $pcheckin = $order['checkin']; $pcheckout = $order['checkout']; $secdiff = $pcheckout - $pcheckin; $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); } } } $is_package = !empty($order['pkg']) ? true : false; // get the rooms booked list $dbo->setQuery( $dbo->getQuery(true) ->select($dbo->qn('or') . '.*') ->select($dbo->qn('r.id', 'r_reference_id')) ->select([ $dbo->qn('r.name'), $dbo->qn('r.img'), $dbo->qn('r.idcarat'), $dbo->qn('r.fromadult'), $dbo->qn('r.toadult'), ]) ->from($dbo->qn('#__vikbooking_ordersrooms', 'or')) ->leftJoin($dbo->qn('#__vikbooking_rooms', 'r') . ' ON ' . $dbo->qn('or.idroom') . ' = ' . $dbo->qn('r.id')) ->where($dbo->qn('or.idorder') . ' = ' . (int)$order['id']) ->order($dbo->qn('or.id') . ' ASC') ); $orderrooms = $dbo->loadAssocList(); if ($orderrooms) { $vbo_tn->translateContents($orderrooms, '#__vikbooking_rooms', array('id' => 'r_reference_id')); foreach ($orderrooms as $kor => $or) { $num = $kor + 1; if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) { //package or custom cost set from the back-end continue; } $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'] . ";"; $dbo->setQuery($q); $tar = $dbo->loadAssocList(); if (!$tar) { continue; } $vbo_tn->translateContents($tar, '#__vikbooking_prices', array('id' => 'idprice')); // determine proper values for this room $room_stay_checkin = $order['checkin']; $room_stay_checkout = $order['checkout']; $room_stay_nights = $order['days']; if ($order['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom']) { $room_stay_checkin = !empty($room_stay_dates[$kor]['checkin_ts']) ? $room_stay_dates[$kor]['checkin_ts'] : $room_stay_dates[$kor]['checkin']; $room_stay_checkout = !empty($room_stay_dates[$kor]['checkout_ts']) ? $room_stay_dates[$kor]['checkout_ts'] : $room_stay_dates[$kor]['checkout']; $room_stay_nights = $av_helper->countNightsOfStay($room_stay_checkin, $room_stay_checkout); } // apply seasonal rates $tar = VikBooking::applySeasonsRoom($tar, $room_stay_checkin, $room_stay_checkout); // apply OBP rules $tar = VBORoomHelper::getInstance()->applyOBPRules($tar, $or, $or['adults']); // push tariffs $tars[$num] = $tar[0]; } } $days_to_arrival = 0; $now_info = getdate(); $checkin_info = getdate($order['checkin']); if ($now_info[0] < $checkin_info[0]) { while ($now_info[0] < $checkin_info[0]) { if (!($now_info['mday'] != $checkin_info['mday'] || $now_info['mon'] != $checkin_info['mon'] || $now_info['year'] != $checkin_info['year'])) { break; } $days_to_arrival++; $now_info = getdate(mktime(0, 0, 0, $now_info['mon'], ($now_info['mday'] + 1), $now_info['year'])); } } $is_refundable = 0; $daysadv_refund_arr = []; $daysadv_refund = 0; $canc_policy = ''; foreach ($tars as $num => $tar) { if ($tar['free_cancellation'] < 1) { // if at least one rate plan is non-refundable, the whole reservation cannot be cancelled $is_refundable = 0; $daysadv_refund_arr = []; break; } $is_refundable = 1; $daysadv_refund_arr[] = $tar['canc_deadline']; } // get the rate plan with the lowest cancellation deadline $daysadv_refund = count($daysadv_refund_arr) > 0 ? min($daysadv_refund_arr) : $daysadv_refund; if ($daysadv_refund > 0) { foreach ($tars as $num => $tar) { if ($tar['free_cancellation'] > 0 && $tar['canc_deadline'] == $daysadv_refund) { //get the cancellation policy from the first rate plan with free cancellation and same cancellation deadline $canc_policy = $tar['canc_policy']; break; } } } $payment = ""; if (!empty($order['idpayment'])) { $exppay = explode('=', $order['idpayment']); $payment = VikBooking::getPayment($exppay[0], $vbo_tn); } $pnodep = VikRequest::getString('nodep', '', 'request'); $cnodep = $cookie->get('vboFA', '', 'string'); $nodep = intval($pnodep) > 0 || intval($cnodep) > 0 ? 1 : 0; /** * Scan the booking and related rooms for damage deposit payment data. * Obtain also the information about possible previous payments. * * @since 1.17.6 (J) - 1.7.6 (WP) */ $damage_deposit_payment = VBORoomHelper::getInstance()->getDamageDepositSplitPayment($order, $orderrooms); $prev_dd_payments = []; if ($damage_deposit_payment['damagedep_gross'] ?? 0) { $prev_dd_payments = VikBooking::getBookingHistoryInstance($order['id']) ->getEventsWithData('PN', function($data) { return (is_object($data) && !empty($data->damage_deposit)); }); if (!$prev_dd_payments) { // check also the first payment event in case of OTA bookings $prev_dd_payments = VikBooking::getBookingHistoryInstance($order['id']) ->getEventsWithData('P0', function($data) { return (is_object($data) && !empty($data->damage_deposit)); }); } } /** * Upselling extra services only for non-cancelled bookings * with a check-out date in the future. * * @since 1.13.0 (J) - 1.3.0 (WP) */ $upselling = []; if ($order['status'] != 'cancelled' && VikBooking::upsellingEnabled() && $order['checkout'] > time()) { $upsell_data = []; foreach ($orderrooms as $kor => $or) { $room_data = new stdClass; $room_data->id = $or['idroom']; $room_data->name = $or['name']; $room_data->img = $or['img']; $room_data->adults = $or['adults']; $room_data->children = $or['children']; $room_data->nights = $order['days']; $room_data->options = []; if (!empty($or['optionals'])) { $optids = explode(';', $or['optionals']); foreach ($optids as $optid) { if (!empty($optid)) { // the : may express the quantity $optidparts = explode(':', $optid); array_push($room_data->options, (int)$optidparts[0]); } } } array_push($upsell_data, $room_data); } $upselling = VikBooking::loadUpsellingData($upsell_data, $order, $vbo_tn); } /** * Trigger first tracking conversion on booking. * * @since 1.16.3 (J) - 1.6.3 (WP) */ VikBooking::getTracker()->triggerBookingConversion($order); $this->ord = $order; $this->orderrooms = $orderrooms; $this->tars = $tars; $this->days_to_arrival = $days_to_arrival; $this->is_refundable = $is_refundable; $this->daysadv_refund = $daysadv_refund; $this->canc_policy = $canc_policy; $this->payment = $payment; $this->nodep = $nodep; $this->upselling = $upselling; $this->damage_deposit_payment = $damage_deposit_payment; $this->prev_dd_payments = $prev_dd_payments; $this->quoteValidUntilDt = $quoteValidUntilDt ?? null; $this->vbo_tn = $vbo_tn; // theme $theme = VikBooking::getTheme(); if ($theme != 'default') { $thdir = VBO_SITE_PATH . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $theme . DIRECTORY_SEPARATOR . 'booking'; if (is_dir($thdir)) { $this->_setPath('template', $thdir . DIRECTORY_SEPARATOR); } } // parent::display($tpl); } }