PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
← All changes | admin/views/ratesoverv/view.html.php +456 -0 1.6.2 → 1.8.16 View file →
@@ -1,0 +1,456 @@
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 +// import Joomla view library
14 +jimport('joomla.application.component.view');
15 +
16 +class VikBookingViewRatesoverv extends JViewVikBooking
17 +{
18 + public function display($tpl = null)
19 + {
20 + // Set the toolbar
21 + $this->addToolBar();
22 +
23 + if (!JFactory::getUser()->authorise('core.vbo.pricing', 'com_vikbooking')) {
24 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
25 + }
26 +
27 + /**
28 + * Check the next festivities periodically
29 + *
30 + * @since 1.12.0 (J) - 1.2.0 (WP)
31 + */
32 + $fests = VikBooking::getFestivitiesInstance();
33 + if ($fests->shouldCheckFestivities()) {
34 + $fests->storeNextFestivities();
35 + }
36 + $festivities = $fests->loadFestDates();
37 +
38 + $dbo = JFactory::getDbo();
39 + $app = JFactory::getApplication();
40 + $session = JFactory::getSession();
41 +
42 + // define the maximum number of days
43 + $MAX_DAYS = VBOFactory::getConfig()->getInt('roverv_max_days', 60);
44 +
45 + $cid = VikRequest::getVar('cid', array(0));
46 + $sesscids = $session->get('vbRatesOviewCids', array());
47 + if (empty($cid[0]) && is_array($sesscids) && count($sesscids)) {
48 + // load rooms from session only if no room IDs requested
49 + $cid = $sesscids;
50 + }
51 +
52 + // first room ID
53 + $roomid = (int)$cid[0];
54 +
55 + if (empty($roomid)) {
56 + $q = "SELECT `id` FROM `#__vikbooking_rooms` WHERE `avail`=1 ORDER BY `#__vikbooking_rooms`.`name` ASC LIMIT 1";
57 + $dbo->setQuery($q);
58 + $roomid = $dbo->loadResult();
59 + if (!$roomid) {
60 + $q = "SELECT `id` FROM `#__vikbooking_rooms` ORDER BY `#__vikbooking_rooms`.`name` ASC LIMIT 1";
61 + $dbo->setQuery($q);
62 + $roomid = $dbo->loadResult();
63 + }
64 + }
65 + if (empty($roomid)) {
66 + $app->redirect("index.php?option=com_vikbooking&task=rooms");
67 + exit;
68 + }
69 + // make sure to set at least the first index of cid[]
70 + $cid[0] = $roomid;
71 + //
72 + $q = "SELECT `id`,`name`,`img` FROM `#__vikbooking_rooms` ORDER BY `#__vikbooking_rooms`.`name` ASC;";
73 + $dbo->setQuery($q);
74 + $all_rooms = $dbo->loadAssocList();
75 +
76 + /**
77 + * Load categories to be used for group filter.
78 + *
79 + * @since 1.13 (J) - 1.3.0 (WP)
80 + */
81 + $q = "SELECT `id`,`name` FROM `#__vikbooking_categories` ORDER BY `#__vikbooking_categories`.`name` ASC;";
82 + $dbo->setQuery($q);
83 + $categories = $dbo->loadAssocList();
84 +
85 + // load rooms rows for all requested rooms
86 + $roomrows = [];
87 + $reqids = [];
88 + $reqcats = [];
89 + foreach ($cid as $rid) {
90 + if (empty($rid)) {
91 + continue;
92 + }
93 + $rid = (int)$rid;
94 + if ($rid < 0) {
95 + // category
96 + array_push($reqcats, ($rid + (abs($rid) * 2)));
97 + } else {
98 + // room
99 + array_push($reqids, $rid);
100 + }
101 + }
102 + if (!$reqcats && !$reqids) {
103 + $app->redirect("index.php?option=com_vikbooking&task=rooms");
104 + exit;
105 + }
106 +
107 + if (!$reqcats && empty($sesscids) && count($reqids) === 1 && count($all_rooms) > 1) {
108 + // push the first 5 rooms by default
109 + $max_def_rooms = VBOFactory::getConfig()->getInt('roverv_def_max_rooms', 5);
110 + $def_rooms_count = 0;
111 + $reqids = [];
112 + foreach ($all_rooms as $aroom) {
113 + $reqids[] = $aroom['id'];
114 + $def_rooms_count++;
115 + if ($def_rooms_count >= $max_def_rooms) {
116 + break;
117 + }
118 + }
119 + }
120 +
121 + $clauses = [];
122 + if (count($reqids)) {
123 + array_push($clauses, "`id` IN (" . implode(', ', $reqids) . ")");
124 + }
125 + foreach ($reqcats as $cat_id) {
126 + array_push($clauses, "(`idcat`='" . $cat_id . ";' OR `idcat` LIKE '" . $cat_id . ";%' OR `idcat` LIKE '%;" . $cat_id . ";%' OR `idcat` LIKE '%;" . $cat_id . ";')");
127 + }
128 + $q = "SELECT * FROM `#__vikbooking_rooms` WHERE ".implode(' OR ', $clauses)." ORDER BY `name` ASC;";
129 + $dbo->setQuery($q);
130 + $rows = $dbo->loadAssocList();
131 + if ($rows) {
132 + foreach ($rows as $row) {
133 + $roomrows[$row['id']] = $row;
134 + }
135 + }
136 + if (!$roomrows) {
137 + $app->redirect("index.php?option=com_vikbooking&task=rooms");
138 + exit;
139 + }
140 + if (!$reqids) {
141 + // in case of just a category filter we need to restore the requested room IDs for later actions
142 + $reqids = array_keys($roomrows);
143 + }
144 + // get all requested and valid room IDs
145 + $req_room_ids = array_keys($roomrows);
146 + $session->set('vbRatesOviewCids', $req_room_ids);
147 + // Restrictions
148 + $all_restrictions = [];
149 + foreach ($req_room_ids as $rid) {
150 + $all_restrictions[(int)$rid] = VikBooking::loadRestrictions(true, array($rid));
151 + }
152 + // length of stay pricing overview (only if one single room requested)
153 + $first_roomrestr = isset($all_restrictions[(int)$roomid]) ? $all_restrictions[(int)$roomid] : array();
154 + $pnights_cal = VikRequest::getVar('nights_cal', array());
155 + $pnights_cal = VikBooking::filterNightsSeasonsCal($pnights_cal);
156 + $room_nights_cal = isset($roomrows[(int)$roomid]) ? explode(',', VikBooking::getRoomParam('seasoncal_nights', $roomrows[(int)$roomid]['params'])) : [];
157 + $room_nights_cal = VikBooking::filterNightsSeasonsCal($room_nights_cal);
158 + $seasons_cal = [];
159 + $seasons_cal_nights = [];
160 + if ($pnights_cal) {
161 + $seasons_cal_nights = $pnights_cal;
162 + } elseif ($room_nights_cal) {
163 + $seasons_cal_nights = $room_nights_cal;
164 + } else {
165 + $q = "SELECT `days` FROM `#__vikbooking_dispcost` WHERE `idroom`=".intval($roomid)." ORDER BY `#__vikbooking_dispcost`.`days` ASC LIMIT 7;";
166 + $dbo->setQuery($q);
167 + $nights_vals = $dbo->loadAssocList();
168 + if ($nights_vals) {
169 + $nights_got = [];
170 + foreach ($nights_vals as $night) {
171 + $nights_got[] = $night['days'];
172 + }
173 + $seasons_cal_nights = VikBooking::filterNightsSeasonsCal($nights_got);
174 + }
175 + }
176 + if (count($req_room_ids) > 1) {
177 + // it's useless to spend server resources to calculate the seasons calendar nights (LOS Pricing Overview) since it won't be displayed when more than 1 room
178 + $seasons_cal_nights = [];
179 + }
180 + if ($seasons_cal_nights) {
181 + $q = "SELECT `p`.*,`tp`.`name`,`tp`.`attr`,`tp`.`idiva`,`tp`.`breakfast_included`,`tp`.`free_cancellation`,`tp`.`canc_deadline` FROM `#__vikbooking_dispcost` AS `p` LEFT JOIN `#__vikbooking_prices` `tp` ON `p`.`idprice`=`tp`.`id` WHERE `p`.`days` IN (".implode(',', $seasons_cal_nights).") AND `p`.`idroom`=".(int)$roomid." ORDER BY `p`.`days` ASC, `p`.`cost` ASC;";
182 + $dbo->setQuery($q);
183 + $tars = $dbo->loadAssocList();
184 + if ($tars) {
185 + $arrtar = [];
186 + foreach ($tars as $tar) {
187 + $arrtar[$tar['days']][] = $tar;
188 + }
189 + $seasons_cal['nights'] = $seasons_cal_nights;
190 + $seasons_cal['offseason'] = $arrtar;
191 + $q = "SELECT * FROM `#__vikbooking_seasons` WHERE `idrooms` LIKE '%-".$roomid."-%';";
192 + $dbo->setQuery($q);
193 + $seasons = $dbo->loadAssocList();
194 + if ($seasons) {
195 + //Restrictions
196 + $all_seasons = [];
197 + $curtime = time();
198 + foreach ($seasons as $sk => $s) {
199 + if (empty($s['from']) && empty($s['to'])) {
200 + continue;
201 + }
202 + $now_year = !empty($s['year']) ? $s['year'] : date('Y');
203 + list($sfrom, $sto) = VikBooking::getSeasonRangeTs($s['from'], $s['to'], $now_year);
204 + if ($sto < $curtime && empty($s['year'])) {
205 + $now_year += 1;
206 + list($sfrom, $sto) = VikBooking::getSeasonRangeTs($s['from'], $s['to'], $now_year);
207 + }
208 + if ($sto >= $curtime) {
209 + $s['from_ts'] = $sfrom;
210 + $s['to_ts'] = $sto;
211 + $all_seasons[] = $s;
212 + }
213 + }
214 + if (count($all_seasons) > 0) {
215 + $vbo_df = VikBooking::getDateFormat();
216 + $vbo_df = $vbo_df == "%d/%m/%Y" ? 'd/m/Y' : ($vbo_df == "%m/%d/%Y" ? 'm/d/Y' : 'Y/m/d');
217 + $hcheckin = 0;
218 + $mcheckin = 0;
219 + $hcheckout = 0;
220 + $mcheckout = 0;
221 + $timeopst = VikBooking::getTimeOpenStore();
222 + if (is_array($timeopst)) {
223 + $opent = VikBooking::getHoursMinutes($timeopst[0]);
224 + $closet = VikBooking::getHoursMinutes($timeopst[1]);
225 + $hcheckin = $opent[0];
226 + $mcheckin = $opent[1];
227 + $hcheckout = $closet[0];
228 + $mcheckout = $closet[1];
229 + }
230 + $all_seasons = VikBooking::sortSeasonsRangeTs($all_seasons);
231 + $seasons_cal['seasons'] = $all_seasons;
232 + $seasons_cal['season_prices'] = [];
233 + $seasons_cal['restrictions'] = [];
234 + //calc price changes for each season and for each num-night
235 + foreach ($all_seasons as $sk => $s) {
236 + $checkin_base_ts = $s['from_ts'];
237 + $is_dst = date('I', $checkin_base_ts);
238 + foreach ($arrtar as $numnights => $tar) {
239 + $checkout_base_ts = $s['to_ts'];
240 + for($i = 1; $i <= $numnights; $i++) {
241 + $checkout_base_ts += 86400;
242 + $is_now_dst = date('I', $checkout_base_ts);
243 + if ($is_dst != $is_now_dst) {
244 + if ((int)$is_dst == 1) {
245 + $checkout_base_ts += 3600;
246 + } else {
247 + $checkout_base_ts -= 3600;
248 + }
249 + $is_dst = $is_now_dst;
250 + }
251 + }
252 + //calc check-in and check-out ts for the two dates
253 + $first = VikBooking::getDateTimestamp(date($vbo_df, $checkin_base_ts), $hcheckin, $mcheckin);
254 + $second = VikBooking::getDateTimestamp(date($vbo_df, $checkout_base_ts), $hcheckout, $mcheckout);
255 + $tar = VikBooking::applySeasonsRoom($tar, $first, $second, $s);
256 + $seasons_cal['season_prices'][$sk][$numnights] = $tar;
257 + //Restrictions
258 + if (count($first_roomrestr)) {
259 + $season_restr = VikBooking::parseSeasonRestrictions($first, $second, $numnights, $first_roomrestr);
260 + if (count($season_restr) > 0) {
261 + $seasons_cal['restrictions'][$sk][$numnights] = $season_restr;
262 + }
263 + }
264 + }
265 + }
266 + }
267 + }
268 + }
269 + }
270 + //calendar rates
271 + $todayd = getdate();
272 + $tsstart = mktime(0, 0, 0, $todayd['mon'], $todayd['mday'], $todayd['year']);
273 + $startdate = VikRequest::getString('startdate', '', 'request');
274 + if (!empty($startdate)) {
275 + $startts = VikBooking::getDateTimestamp($startdate, 0, 0);
276 + if (!empty($startts)) {
277 + $session->set('vbRatesOviewTs', $startts);
278 + $tsstart = $startts;
279 + }
280 + } else {
281 + $prevts = $session->get('vbRatesOviewTs', '');
282 + if (!empty($prevts)) {
283 + $tsstart = $prevts;
284 + }
285 + }
286 +
287 + // read the rates for the lowest number of nights for each room requested
288 + $roomrates = [];
289 + foreach ($req_room_ids as $rid) {
290 + /**
291 + * Some types of price may not have a cost for 1 or 2 nights,
292 + * so joining by MIN(`days`) may exclude certain types of price.
293 + * We need to manually get via PHP all types of price.
294 + * Old query below is no longer in use, even though it was
295 + * compatible with the SQL strict mode (only_full_group_by).
296 + * $q = "SELECT `r`.`id`,`r`.`idroom`,`r`.`days`,`r`.`idprice`,`r`.`cost`,`p`.`name` FROM `#__vikbooking_dispcost` AS `r` INNER JOIN (SELECT MIN(`days`) AS `min_days` FROM `#__vikbooking_dispcost` WHERE `idroom`=".(int)$rid." GROUP BY `idroom`) AS `r2` ON `r`.`days`=`r2`.`min_days` LEFT JOIN `#__vikbooking_prices` `p` ON `p`.`id`=`r`.`idprice` WHERE `r`.`idroom`=".(int)$rid." GROUP BY `r`.`id`,`r`.`idroom`,`r`.`days`,`r`.`idprice`,`r`.`cost`,`p`.`name` ORDER BY `r`.`days` ASC, `r`.`cost` ASC;";
297 + *
298 + * @since 1.10 (J) - 1.0.14 (WP)
299 + */
300 + $q = "SELECT `r`.`id`,`r`.`idroom`,`r`.`days`,`r`.`idprice`,`r`.`cost`,`p`.`name`,`p`.`minlos`,`p`.`derived_id`
301 + FROM `#__vikbooking_dispcost` AS `r`
302 + LEFT JOIN `#__vikbooking_prices` `p` ON `p`.`id`=`r`.`idprice`
303 + WHERE `r`.`idroom`=".(int)$rid."
304 + ORDER BY `r`.`days` ASC, `r`.`cost` ASC";
305 + $dbo->setQuery($q, 0, 50);
306 + $nowroomrates = $dbo->loadAssocList();
307 +
308 + if ($nowroomrates) {
309 + $parsed_room_prices = [];
310 + foreach ($nowroomrates as $rrk => $rrv) {
311 + if (isset($parsed_room_prices[$rrv['idprice']])) {
312 + unset($nowroomrates[$rrk]);
313 + continue;
314 + }
315 + $nowroomrates[$rrk]['cost'] = round(($rrv['cost'] / $rrv['days']), 2);
316 + $nowroomrates[$rrk]['days'] = 1;
317 + $parsed_room_prices[$rrv['idprice']] = 1;
318 + }
319 + }
320 +
321 + $nowroomrates = array_values($nowroomrates);
322 +
323 + // push rates for this room
324 + $roomrates[(int)$rid] = $nowroomrates;
325 + }
326 +
327 + // sort room rates by the best rate plan name
328 + foreach ($roomrates as $rid => $roomrplans) {
329 + $roomrates[$rid] = VikBooking::sortRatePlans($roomrplans);
330 + }
331 +
332 + // read all the bookings between these dates for all rooms
333 + $booked_dates = [];
334 + $info_start = getdate($tsstart);
335 + $endts = mktime(23, 59, 59, $info_start['mon'], ($info_start['mday'] + $MAX_DAYS), $info_start['year']);
336 +
337 + $q = "SELECT `b`.*,`ob`.`idorder`,`o`.`custdata`,`o`.`status`,`o`.`days`,`o`.`roomsnum`,`o`.`idorderota`,`o`.`channel`,`o`.`colortag`,`o`.`closure`,`o`.`total`,`o`.`totpaid`,`o`.`paymentlog`
338 + FROM `#__vikbooking_busy` AS `b`
339 + LEFT JOIN `#__vikbooking_ordersbusy` AS `ob` ON `b`.`id`=`ob`.`idbusy`
340 + LEFT JOIN `#__vikbooking_orders` AS `o` ON `ob`.`idorder`=`o`.`id`
341 + WHERE `b`.`idroom` IN (" . implode(', ', $reqids) . ") AND (`b`.`checkin`>=" . $tsstart . " OR `b`.`checkout`>=" . $tsstart . ") AND (`b`.`checkin`<=" . $endts . " OR `b`.`checkout`<=" . $tsstart . ");";
342 + $dbo->setQuery($q);
343 + $rbusy = $dbo->loadAssocList();
344 +
345 + $ridbusy = [];
346 + foreach ($rbusy as $rb) {
347 + if (!isset($ridbusy[$rb['idroom']])) {
348 + $ridbusy[$rb['idroom']] = [];
349 + }
350 + $ridbusy[$rb['idroom']][] = $rb;
351 + }
352 +
353 + // collect additional information for each booking
354 + $extra_info_map = [];
355 + foreach ($ridbusy as $rid => $roomres) {
356 + foreach ($roomres as $k => $res) {
357 + if (empty($res['idorder'])) {
358 + continue;
359 + }
360 + if (isset($extra_info_map[$res['idorder']])) {
361 + // merge existing extra information
362 + $ridbusy[$rid][$k] = array_merge($res, $extra_info_map[$res['idorder']]);
363 + continue;
364 + }
365 + // get booking extra information
366 + $q = "SELECT `oc`.`idcustomer`,`c`.`first_name`,`c`.`last_name`,`c`.`pic`
367 + FROM `#__vikbooking_customers_orders` AS `oc`
368 + LEFT JOIN `#__vikbooking_customers` AS `c` ON `oc`.`idcustomer`=`c`.`id`
369 + WHERE `oc`.`idorder`={$res['idorder']}";
370 + $dbo->setQuery($q, 0, 1);
371 + $extra_info = $dbo->loadAssoc();
372 + $extra_info = $extra_info ?: [];
373 + // merge and map booking extra information
374 + $ridbusy[$rid][$k] = array_merge($res, $extra_info);
375 + $extra_info_map[$res['idorder']] = $extra_info;
376 + }
377 + }
378 +
379 + foreach ($req_room_ids as $rid) {
380 + $booked_dates[$rid] = $ridbusy[$rid] ?? [];
381 + }
382 +
383 + // free memory up
384 + unset($ridbusy, $rbusy);
385 +
386 + /**
387 + * Load room day notes for the requested dates
388 + *
389 + * @since 1.13.5
390 + */
391 + $rdaynotes = VikBooking::getCriticalDatesInstance()->loadRoomDayNotes(date('Y-m-d', $tsstart), date('Y-m-d', $endts));
392 +
393 + /**
394 + * Build room-ota relations for pricing alterations, if any.
395 + *
396 + * @since 1.17.2 (J) - 1.7.2 (WP)
397 + */
398 + $room_ota_relations = [];
399 + foreach ($req_room_ids as $rid) {
400 + // always get a new instance of the VikChannelManagerLogos class
401 + $vcm_logos = VikBooking::getVcmChannelsLogo('', true);
402 + // load channels (firsr) and accounts (after) for this listing
403 + $room_ota_channels = is_object($vcm_logos) && method_exists($vcm_logos, 'getVboRoomLogosMapped') ? $vcm_logos->getVboRoomLogosMapped($rid) : [];
404 + $room_ota_accounts = is_object($vcm_logos) && method_exists($vcm_logos, 'getRoomOtaAccounts') ? $vcm_logos->getRoomOtaAccounts() : [];
405 + // filter channels not available as accounts (i.e. iCal)
406 + if (count($room_ota_channels) != count(($room_ota_accounts[$rid] ?? []))) {
407 + $ota_account_names = array_map('strtolower', array_column(($room_ota_accounts[$rid] ?? []), 'channel'));
408 + $room_ota_channels = array_filter($room_ota_channels, function($chid) use ($ota_account_names) {
409 + return in_array(strtolower($chid), $ota_account_names);
410 + }, ARRAY_FILTER_USE_KEY);
411 + }
412 + if ($room_ota_channels && ($room_ota_accounts[$rid] ?? [])) {
413 + $room_ota_relations[$rid] = [
414 + 'channels' => $room_ota_channels,
415 + 'accounts' => $room_ota_accounts[$rid],
416 + ];
417 + }
418 + }
419 +
420 + $this->all_rooms = $all_rooms;
421 + $this->categories = $categories;
422 + $this->reqcats = $reqcats;
423 + $this->all_restrictions = $all_restrictions;
424 + $this->roomrows = $roomrows;
425 + $this->seasons_cal_nights = $seasons_cal_nights;
426 + $this->seasons_cal = $seasons_cal;
427 + $this->tsstart = $tsstart;
428 + $this->roomrates = $roomrates;
429 + $this->booked_dates = $booked_dates;
430 + $this->req_room_ids = $req_room_ids;
431 + $this->firstroom = $roomid;
432 + $this->festivities = $festivities;
433 + $this->rdaynotes = $rdaynotes;
434 + $this->room_ota_relations = $room_ota_relations;
435 + $this->max_days = $MAX_DAYS;
436 +
437 + // Display the template
438 + parent::display($tpl);
439 + }
440 +
441 + /**
442 + * Sets the toolbar
443 + */
444 + protected function addToolBar()
445 + {
446 + JToolBarHelper::title(JText::translate('VBMAINRATESOVERVIEWTITLE'), 'vikbooking');
447 + if (JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
448 + JToolBarHelper::addNew('newseason', JText::translate('VBMAINSEASONSNEW'));
449 + JToolBarHelper::spacer();
450 + JToolBarHelper::addNew('newrestriction', JText::translate('VBMAINRESTRICTIONNEW'));
451 + JToolBarHelper::spacer();
452 + }
453 + JToolBarHelper::cancel( 'cancel', JText::translate('VBBACK'));
454 + JToolBarHelper::spacer();
455 + }
456 +}