PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.7.2
VikBooking Hotel Booking Engine & PMS v1.7.2
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
vikbooking / admin / views / ratesoverv / tmpl / default.php

default.php in VikBooking Hotel Booking Engine & PMS 1.7.2, at admin/views/ratesoverv/tmpl/default.php

3,591 lines 140.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 $all_rooms = $this->all_rooms;
14 $categories = $this->categories;
15 $roomrows = $this->roomrows;
16 $seasoncal_nights = $this->seasons_cal_nights;
17 $seasons_cal = $this->seasons_cal;
18 $tsstart = $this->tsstart;
19 $roomrates = $this->roomrates;
20
21 // JS lang defs
22 JText::script('VBO_BOOKNOW');
23 JText::script('VBPVIEWORDERSTHREE');
24 JText::script('VBEDITORDERTHREE');
25 JText::script('VBDAYS');
26 JText::script('VBDAY');
27 JText::script('VBMAILADULTS');
28 JText::script('VBMAILADULT');
29 JText::script('VBMAILCHILDREN');
30 JText::script('VBMAILCHILD');
31 JText::script('VBO_MISSING_SUBUNIT');
32 JText::script('VBRATESOVWRATESCALCULATORCALC');
33 JText::script('VBRATESOVWRATESCALCULATORCALCING');
34 JText::script('VBOVCMRATESRES');
35 JText::script('VBO_IS_DERIVED_RATE');
36 JText::script('VBO_PLEASE_FILL_FIELDS');
37 JText::script('VBO_RATES_AND_RESTR');
38 JText::script('VBSAVE');
39 JText::script('VBANNULLA');
40 JText::script('VBAPPLY');
41
42 $document = JFactory::getDocument();
43
44 $vbo_app = VikBooking::getVboApplication();
45 $vbo_app->loadSelect2();
46 $vbo_app->loadDatePicker();
47
48 $pdebug = VikRequest::getint('e4j_debug', '', 'request');
49
50 $vbo_df = VikBooking::getDateFormat();
51 $datesep = VikBooking::getDateSeparator();
52 $df = $vbo_df == "%d/%m/%Y" ? 'd/m/Y' : ($vbo_df == "%m/%d/%Y" ? 'm/d/Y' : 'Y/m/d');
53 $juidf = $vbo_df == "%d/%m/%Y" ? 'dd/mm/yy' : ($vbo_df == "%m/%d/%Y" ? 'mm/dd/yy' : 'yy/mm/dd');
54
55 $currencysymb = VikBooking::getCurrencySymb();
56 list($currency_digits, $currency_decimals, $currency_thousands) = explode(':', VikBooking::getNumberFormatData());
57
58 $MAX_DAYS = $this->max_days;
59 $pcheckinh = 0;
60 $pcheckinm = 0;
61 $pcheckouth = 0;
62 $pcheckoutm = 0;
63 $timeopst = VikBooking::getTimeOpenStore();
64 if (is_array($timeopst)) {
65 $opent = VikBooking::getHoursMinutes($timeopst[0]);
66 $closet = VikBooking::getHoursMinutes($timeopst[1]);
67 $pcheckinh = $opent[0];
68 $pcheckinm = $opent[1];
69 $pcheckouth = $closet[0];
70 $pcheckoutm = $closet[1];
71 }
72
73 /**
74 * Check if season records should be preloaded. Beware of the
75 * hundreds of MBs of server's memory that could be used for
76 * pre-loading and pre-caching records in favour of CPU.
77 *
78 * @since 1.16.10 (J) - 1.6.10 (WP)
79 */
80 $cached_seasons = [];
81 $cmd_precache_seasons = JFactory::getApplication()->input->getInt('pre_cache_seasons');
82 $can_precache_seasons = $cmd_precache_seasons === 1 ? 1 : VBOFactory::getConfig()->getInt('pre_cache_seasons', 0);
83 if ($cmd_precache_seasons === 0 || $cmd_precache_seasons === 1) {
84 // update configuration record
85 VBOFactory::getConfig()->set('pre_cache_seasons', $cmd_precache_seasons);
86 $can_precache_seasons = $cmd_precache_seasons;
87 }
88 if ($can_precache_seasons) {
89 $from_info = getdate($tsstart);
90 $season_from_ts = mktime($pcheckinh, $pcheckinm, 0, $from_info['mon'], $from_info['mday'], $from_info['year']);
91 $season_to_ts = mktime($pcheckouth, $pcheckoutm, 0, $from_info['mon'], $from_info['mday'] + $MAX_DAYS, $from_info['year']);
92 $cached_seasons = VikBooking::getDateSeasonRecords($season_from_ts, $season_to_ts, $this->req_room_ids);
93 }
94
95 $mb_support = function_exists('mb_substr');
96 $short_wdays = [
97 JText::translate('VBSUNDAY'),
98 JText::translate('VBMONDAY'),
99 JText::translate('VBTUESDAY'),
100 JText::translate('VBWEDNESDAY'),
101 JText::translate('VBTHURSDAY'),
102 JText::translate('VBFRIDAY'),
103 JText::translate('VBSATURDAY'),
104 ];
105 $short_mons = [
106 JText::translate('VBMONTHONE'),
107 JText::translate('VBMONTHTWO'),
108 JText::translate('VBMONTHTHREE'),
109 JText::translate('VBMONTHFOUR'),
110 JText::translate('VBMONTHFIVE'),
111 JText::translate('VBMONTHSIX'),
112 JText::translate('VBMONTHSEVEN'),
113 JText::translate('VBMONTHEIGHT'),
114 JText::translate('VBMONTHNINE'),
115 JText::translate('VBMONTHTEN'),
116 JText::translate('VBMONTHELEVEN'),
117 JText::translate('VBMONTHTWELVE'),
118 ];
119 foreach ($short_wdays as $k => $v) {
120 if ($mb_support) {
121 $short_wdays[$k] = mb_substr($v, 0, 3, 'UTF-8');
122 } else {
123 $short_wdays[$k] = substr($v, 0, 3);
124 }
125 }
126 foreach ($short_mons as $k => $v) {
127 if ($mb_support) {
128 $short_mons[$k] = mb_substr($v, 0, 3, 'UTF-8');
129 } else {
130 $short_mons[$k] = substr($v, 0, 3);
131 }
132 }
133 $json_short_wdays = json_encode($short_wdays);
134 $json_short_mons = json_encode($short_mons);
135 $json_room_ota_rels = json_encode($this->room_ota_relations);
136
137 // add to DOM the necessary script declaration
138 $document->addScriptDeclaration(
139 <<<JS
140 var vboMapWdays = $json_short_wdays;
141 var vboMapMons = $json_short_mons;
142 var vboRoomOtaRels = $json_room_ota_rels;
143 var vbo_currency_symbol = "$currencysymb";
144 var vbo_currency_digits = "$currency_digits";
145 var vbo_currency_decimals = "$currency_decimals";
146 var vbo_currency_thousands = "$currency_thousands";
147 JS
148 );
149
150 $price_types_show = true;
151 $los_show = true;
152 $cookie = JFactory::getApplication()->input->cookie;
153 $cookie_tab = $cookie->get('vboRovwRab', 'cal', 'string');
154 $collapse_status = $cookie->get('vboRovwColl', 0, 'int');
155
156 // access room helper to detect LOS rates
157 $room_helper = VBORoomHelper::getInstance();
158
159 // derived rate plans data
160 $derived_rates_info = [];
161 foreach (VikBooking::getAvailabilityInstance()->loadRatePlans() as $rate_plan) {
162 if ($rate_plan['derived_id'] && $rate_plan['derived_data']) {
163 $derived_rates_info[$rate_plan['id']] = $rate_plan;
164 }
165 }
166
167 ?>
168
169 <div class="vbo-ratesoverview-top-outer">
170 <div class="vbo-ratesoverview-top-outer-inner vbo-toggle-small">
171 <?php echo $vbo_app->printYesNoButtons('vbo_collapse', JText::translate('VBYES'), JText::translate('VBNO'), (!$collapse_status ? 1 : 0), 1, 0, "vboToggleCollapse();"); ?>
172 <span onclick="jQuery(this).parent().find('input').trigger('click');"><?php echo JText::translate('VBO_EXPAND'); ?></span>
173 </div>
174 </div>
175
176 <div class="vbo-ratesoverview-top-container<?php echo $collapse_status ? ' collapsed' : ''; ?>">
177 <div class="vbo-ratesoverview-roomsel-block">
178 <form method="get" action="index.php?option=com_vikbooking" name="vboratesovwform">
179 <input type="hidden" name="option" value="com_vikbooking" />
180 <input type="hidden" name="task" value="ratesoverv" />
181 <div class="vbo-ratesoverview-roomsel-entry vbo-ratesoverview-roomsel-entry-chrooms">
182 <label for="roomsel"><?php VikBookingIcons::e('bed'); ?> <?php echo JText::translate('VBPVIEWORDERSTHREE'); ?></label>
183 <select name="cid[]" <?php echo count($all_rooms) > 1 ? 'multiple="multiple"' : 'onchange="document.vboratesovwform.submit();"' ?> id="roomsel" style="min-width: 160px; max-width: 250px;">
184 <?php
185 foreach ($all_rooms as $room) {
186 ?>
187 <option value="<?php echo $room['id']; ?>"<?php echo in_array($room['id'], $this->req_room_ids) ? ' selected="selected"' : ''; ?>><?php echo $room['name']; ?></option>
188 <?php
189 }
190
191 /**
192 * Use the optgroup to separate rooms from categories in the
193 * drop down menu, only if all_rooms > 1 e categories > 1.
194 *
195 * @since 1.13
196 */
197 if (count($all_rooms) > 1 && count($categories) > 1) {
198 ?>
199 <optgroup label="<?php echo addslashes(JText::translate('VBOCATEGORYFILTER')); ?>">
200 <?php
201 foreach ($categories as $cat) {
202 // we use negative values for the IDs of the categories
203 $cat_id = ($cat['id'] - ($cat['id'] * 2));
204 ?>
205 <option value="<?php echo $cat_id; ?>"><?php echo (in_array($cat['id'], $this->reqcats) ? '- ' : '') . $cat['name']; ?></option>
206 <?php
207 }
208 ?>
209 </optgroup>
210 <?php
211 }
212 //
213 ?>
214 </select>
215 <button type="button" class="btn vbo-config-btn" title="<?php echo htmlspecialchars(JText::translate('VBODRIVERLOAD')); ?>" onclick="document.vboratesovwform.submit();"><i class="vboicn-loop2"></i></button>
216 <?php
217 if (count($all_rooms) > 1 && count($all_rooms) < 50 && count($this->req_room_ids) < count($all_rooms)) {
218 // display select all rooms button
219 ?>
220 <div class="vbo-roverview-selall-rooms">
221 <button type="button" class="btn vbo-config-btn" onclick="vboSelectAllRooms(this);"><?php VikBookingIcons::e('layer-group'); ?> <?php echo JText::translate('VBOSELECTALL'); ?></button>
222 </div>
223 <?php
224 }
225 ?>
226 </div>
227 <div class="vbo-ratesoverview-roomsel-entry vbo-ratesoverview-roomsel-entry-calc">
228 <label for="checkindate" onclick="vboCheckCollapse();"><?php VikBookingIcons::e('calculator'); ?> <?php echo JText::translate('VBRATESOVWRATESCALCULATOR'); ?></label>
229 <div class="vbo-ratesoverview-roomsel-entry-calc-inner"<?php echo $collapse_status ? ' style="display: none;"' : ''; ?>>
230 <span class="vbo-ratesoverview-entryinline vbo-ratesoverview-rcalc-srooms">
231 <select name="roomselcalc" id="roomselcalc" style="max-width: 250px;">
232 <?php
233 foreach ($all_rooms as $room) {
234 ?>
235 <option value="<?php echo $room['id']; ?>"<?php echo $room['id'] == $roomrows[$this->firstroom]['id'] ? ' selected="selected"' : ''; ?>><?php echo $room['name']; ?></option>
236 <?php
237 }
238 ?>
239 </select>
240 </span>
241 <span class="vbo-ratesoverview-entryinline">
242 <div class="vbo-field-calendar">
243 <div class="input-append">
244 <input type="text" autocomplete="off" value="" class="vbo-roverv-rcalc-cal" size="10" id="checkindate" name="checkindate" placeholder="<?php echo JText::translate('VBPICKUPAT'); ?>" />
245 <button type="button" class="btn btn-secondary" id="checkindate-trig"><?php VikBookingIcons::e('calendar', 'icn-nomargin'); ?></button>
246 </div>
247 </div>
248 </span>
249 <span class="vbo-ratesoverview-entryinline">
250 <div class="vbo-field-calendar">
251 <div class="input-append">
252 <input type="text" autocomplete="off" value="" class="vbo-roverv-rcalc-cal" size="10" id="checkoutdate" name="checkoutdate" placeholder="<?php echo JText::translate('VBRELEASEAT'); ?>" />
253 <button type="button" class="btn btn-secondary" id="checkoutdate-trig"><?php VikBookingIcons::e('calendar', 'icn-nomargin'); ?></button>
254 </div>
255 </div>
256 </span>
257 <span class="vbo-ratesoverview-entryinline"><span><?php echo JText::translate('VBRATESOVWRATESCALCNUMNIGHTS'); ?></span> <input type="number" id="vbo-numnights" value="1" min="1" max="365" step="1" /></span>
258 <span class="vbo-ratesoverview-entryinline"><span><?php echo JText::translate('VBRATESOVWRATESCALCNUMADULTS'); ?></span> <input type="number" id="vbo-numadults" value="<?php echo $roomrows[$this->firstroom]['fromadult']; ?>" step="1"/></span>
259 <span class="vbo-ratesoverview-entryinline"><span><?php echo JText::translate('VBRATESOVWRATESCALCNUMCHILDREN'); ?></span> <input type="number" id="vbo-numchildren" value="<?php echo $roomrows[$this->firstroom]['fromchild']; ?>" step="1"/></span>
260 <span class="vbo-ratesoverview-entryinline"><button type="button" class="btn vbo-config-btn" id="vbo-ratesoverview-calculate"><?php echo JText::translate('VBRATESOVWRATESCALCULATORCALC'); ?></button></span>
261 </div>
262
263 <div class="vbo-ratesoverview-calculation-response"></div>
264
265 </div>
266 <div class="vbo-ratesoverview-roomsel-entry vbo-ratesoverview-roomsel-entry-forecast"<?php echo (!empty($cookie_tab) && $cookie_tab != 'cal' ? ' style="display: none;"' : ''); ?>>
267 <label onclick="vboCheckCollapse();"><?php VikBookingIcons::e('cloud-sun-rain'); ?> <?php echo JText::translate('VBOFORECAST'); ?></label>
268 <div class="vbo-roverv-forecast-inner"<?php echo $collapse_status ? ' style="display: none;"' : ''; ?>>
269 <?php echo JLayoutHelper::render('reports.occupancy', array('vbo_page' => 'ratesoverv', 'room_ids' => $this->req_room_ids)); ?>
270 </div>
271 </div>
272 <div class="vbo-ratesoverview-roomsel-entry vbo-ratesoverview-roomsel-entry-los"<?php echo (!empty($cookie_tab) && $cookie_tab == 'cal' ? ' style="display: none;"' : ''); ?>>
273 <label><?php echo JText::translate('VBRATESOVWNUMNIGHTSACT'); ?></label>
274 <?php
275 foreach ($seasoncal_nights as $numnights) {
276 ?>
277 <span class="vbo-ratesoverview-numnight" id="numnights<?php echo $numnights; ?>"><?php echo $numnights; ?></span>
278 <input type="hidden" name="nights_cal[]" id="inpnumnights<?php echo $numnights; ?>" value="<?php echo $numnights; ?>" />
279 <?php
280 }
281 ?>
282 <input type="number" id="vbo-addnumnight" value="<?php echo isset($numnights) ? ($numnights + 1) : 1; ?>" min="1"/>
283 <span id="vbo-addnumnight-act"><?php VikBookingIcons::e('plus-square'); ?></span>
284 <button type="button" class="btn vbo-config-btn vbo-apply-los-btn" onclick="document.vboratesovwform.submit();"><?php echo JText::translate('VBRATESOVWAPPLYLOS'); ?></button>
285 </div>
286 </form>
287 </div>
288 <div class="vbo-ratesoverview-right-block">
289 <div class="vbo-ratesoverview-right-inner"></div>
290 </div>
291 </div>
292
293 <div class="vbo-ratesoverview-bottom-container">
294 <?php
295 foreach ($roomrows as $rid => $roomrow) {
296 if (count($this->req_room_ids) < 2) {
297 ?>
298 <div class="vbo-ratesoverview-bottom-head">
299 <div class="vbo-ratesoverview-roomdetails">
300 <h3 class="vbo-ratesoverview-roomname"><?php echo $roomrow['name']; ?></h3>
301 </div>
302 <div class="vbo-ratesoverview-tabscont">
303 <div class="vbo-ratesoverview-tab-cal <?php echo (!empty($cookie_tab) && $cookie_tab == 'cal' ? 'vbo-ratesoverview-tab-active' : 'vbo-ratesoverview-tab-unactive'); ?>"><i class="vboicn-calendar"></i> <?php echo JText::translate('VBRATESOVWTABCALENDAR'); ?></div>
304 <div class="vbo-ratesoverview-tab-los <?php echo (!empty($cookie_tab) && $cookie_tab == 'cal' ? 'vbo-ratesoverview-tab-unactive' : 'vbo-ratesoverview-tab-active'); ?>"><i class="vboicn-clock"></i> <?php echo JText::translate('VBRATESOVWTABLOS'); ?></div>
305 </div>
306 </div>
307 <?php
308 }
309 ?>
310
311 <div class="vbo-ratesoverview-caltab-cont" style="display: <?php echo count($this->req_room_ids) > 1 || (!empty($cookie_tab) && $cookie_tab == 'cal') ? 'block' : 'none'; ?>;">
312 <?php
313 if (count($this->req_room_ids) > 1) {
314 // display room name here when multiple rooms as well as the OBP toggle
315 if (!isset($vbo_show_toggle_obp)) {
316 // toggle button for showing/hiding the occupancy based pricing rules
317 $vbo_show_toggle_obp = 1;
318 $obp_status = $cookie->get('vboRovwObp', 1, 'int');
319 ?>
320 <div class="vbo-ratesoverview-obp-toggle" data-obpstartstatus="<?php echo $obp_status; ?>" style="display: none;">
321 <div class="vbo-ratesoverview-obp-toggle-inner vbo-toggle-small">
322 <?php echo $vbo_app->printYesNoButtons('show_obp', JText::translate('VBYES'), JText::translate('VBNO'), $obp_status, 1, 0, "vboToggleOBPRows();"); ?>
323 <span class="vbo-ratesoverview-obp-toggle-txt" onclick="jQuery(this).parent().find('input').trigger('click');"><?php echo JText::translate('VBOROVERVOBP'); ?></span>
324 </div>
325 </div>
326 <?php
327 }
328 ?>
329 <div class="vbo-ratesoverview-roomdetails">
330 <h3 class="vbo-ratesoverview-roomname" data-idroom="<?php echo $roomrow['id']; ?>"><?php echo $roomrow['name']; ?></h3>
331 </div>
332 <?php
333 } else {
334 // room name node is needed for JS
335 ?>
336 <div class="vbo-ratesoverview-roomdetails" style="display: none;">
337 <h3 class="vbo-ratesoverview-roomname" data-idroom="<?php echo $roomrow['id']; ?>"><?php echo $roomrow['name']; ?></h3>
338 </div>
339 <?php
340 }
341 ?>
342 <div class="vbo-ratesoverview-caltab-wrapper">
343 <div class="vbo-table-responsive">
344 <table class="vboverviewtable vbratesoverviewtable vbo-table" data-idroom="<?php echo $rid; ?>">
345 <tbody>
346 <tr class="vbo-roverviewrowone">
347 <td class="bluedays skip-bluedays-click">
348 <form name="vbratesoverview" method="post" action="index.php?option=com_vikbooking&amp;task=ratesoverv">
349 <div class="vbo-roverview-datecmd-top">
350 <div class="vbo-roverview-datecmd-date">
351 <span>
352 <?php VikBookingIcons::e('calendar'); ?>
353 <input type="text" autocomplete="off" value="<?php echo date($df, $tsstart); ?>" class="vbodatepicker" name="startdate" />
354 </span>
355 </div>
356 </div>
357 </form>
358 </td>
359 <?php
360 $nowts = getdate($tsstart);
361 $days_labels = array(
362 JText::translate('VBSUN'),
363 JText::translate('VBMON'),
364 JText::translate('VBTUE'),
365 JText::translate('VBWED'),
366 JText::translate('VBTHU'),
367 JText::translate('VBFRI'),
368 JText::translate('VBSAT')
369 );
370 $long_days_labels = array(
371 JText::translate('VBSUNDAY'),
372 JText::translate('VBMONDAY'),
373 JText::translate('VBTUESDAY'),
374 JText::translate('VBWEDNESDAY'),
375 JText::translate('VBTHURSDAY'),
376 JText::translate('VBFRIDAY'),
377 JText::translate('VBSATURDAY')
378 );
379 $months_labels = array(
380 JText::translate('VBMONTHONE'),
381 JText::translate('VBMONTHTWO'),
382 JText::translate('VBMONTHTHREE'),
383 JText::translate('VBMONTHFOUR'),
384 JText::translate('VBMONTHFIVE'),
385 JText::translate('VBMONTHSIX'),
386 JText::translate('VBMONTHSEVEN'),
387 JText::translate('VBMONTHEIGHT'),
388 JText::translate('VBMONTHNINE'),
389 JText::translate('VBMONTHTEN'),
390 JText::translate('VBMONTHELEVEN'),
391 JText::translate('VBMONTHTWELVE')
392 );
393 $long_months_labels = $months_labels;
394 foreach ($months_labels as $i => $v) {
395 $months_labels[$i] = function_exists('mb_substr') ? mb_substr($v, 0, 3, 'UTF-8') : substr($v, 0, 3);
396 }
397 $cell_count = 0;
398 $weekend_arr = array(0, 6);
399 while ($cell_count < $MAX_DAYS) {
400 $style = '';
401 $curdayymd = date('Y-m-d', $nowts[0]);
402 $read_day = $days_labels[$nowts['wday']] . ' ' . $nowts['mday'] . ' ' . $months_labels[$nowts['mon']-1] . ' ' . $nowts['year'];
403
404 $cell_classes = array(
405 'bluedays',
406 'cell-' . $nowts['mday'] . '-' . $nowts['mon'],
407 );
408
409 /**
410 * Critical dates defined at room-day level.
411 *
412 * @since 1.13.5
413 */
414 $rdaynote_keyid = $curdayymd . '_' . $rid . '_0';
415 $rdaynote_icn = '';
416 if (isset($this->rdaynotes[$rdaynote_keyid])) {
417 // note exists for this combination of date, room ID and subunit
418 array_push($cell_classes, 'vbo-roverw-roomdaynote-full');
419 $rdaynote_icn = '<i class="' . VikBookingIcons::i('sticky-note') . '"></i>';
420 }
421 //
422
423 if (in_array((int)$nowts['wday'], $weekend_arr)) {
424 array_push($cell_classes, 'vbo-roverw-tablewday-wend');
425 }
426 if (isset($this->festivities[$curdayymd])) {
427 array_push($cell_classes, 'vbo-roverv-festcell');
428 }
429 ?>
430 <td data-ymd="<?php echo $curdayymd; ?>" data-readymd="<?php echo $read_day; ?>" data-rid="<?php echo $rid; ?>" class="<?php echo implode(' ', $cell_classes); ?>"<?php echo $style; ?>>
431 <?php
432 if (!empty($rdaynote_icn)) {
433 ?>
434 <span class="vbo-roverw-roomdaynote-icn"><?php echo $rdaynote_icn; ?></span>
435 <?php
436 }
437 ?>
438 <span class="vbo-roverw-tablewday"><?php echo $days_labels[$nowts['wday']]; ?></span>
439 <span class="vbo-roverw-tablemday"><?php echo $nowts['mday']; ?></span>
440 <span class="vbo-roverw-tablemonth"><?php echo $months_labels[$nowts['mon']-1]; ?></span>
441 </td>
442 <?php
443 $next = $nowts['mday'] + 1;
444 $dayts = mktime(0, 0, 0, $nowts['mon'], $next, $nowts['year']);
445 $nowts = getdate($dayts);
446 $cell_count++;
447 }
448 ?>
449 <td class="vbo-roverv-gonext-cell" data-rid="<?php echo $rid; ?>" rowspan="<?php echo count($roomrates[$rid]) + 3; ?>">
450 <div class="vbo-roverv-gonext-cell-inner">
451 <button type="button" class="btn vbo-config-btn vbo-config-btn-rounded vbo-config-btn-large" onclick="vboDisplayNextDays(this);"><?php VikBookingIcons::e('angle-double-right'); ?></button>
452 </div>
453 </td>
454 </tr>
455 <?php
456 $room_obp_overrides = [];
457 $closed_roomrateplans = VikBooking::getRoomRplansClosingDates($roomrow['id']);
458 foreach ($roomrates[$rid] as $roomrate) {
459 $nowts = getdate($tsstart);
460 $cell_count = 0;
461 $rplan_minlos = !empty($roomrate['minlos']) && $roomrate['minlos'] > 0 ? $roomrate['minlos'] : 0;
462 $rplan_haslos = $room_helper->hasLosRecords($roomrate['idroom'], $roomrate['idprice'], true);
463
464 // check for derived rate plans
465 $derived_str = '';
466 $derived_icn = '';
467 if (isset($derived_rates_info[$roomrate['idprice']])) {
468 // build derived info string
469 $derived_data = $derived_rates_info[$roomrate['idprice']]['derived_data'] ?? [];
470 $derived_str .= ($derived_rates_info[$roomrate['idprice']]['parent_rate_name'] ?? '') . ' ';
471 $derived_str .= ($derived_data['type'] ?? 'percent') == 'absolute' ? $currencysymb . ' ' : '';
472 $derived_str .= ($derived_data['mode'] ?? 'discount') == 'discount' ? '-' : '+';
473 $derived_str .= $derived_data['value'] ?? 0;
474 $derived_str .= ($derived_data['type'] ?? 'percent') == 'percent' ? ' %' : '';
475 // set derived icon
476 $derived_icn = '<span class="badge badge-warning"><i class="' . VikBookingIcons::i('link') . '"></i></span> ';
477 }
478 ?>
479 <tr class="vbo-roverviewtablerow" id="vbo-roverw-<?php echo $roomrate['id'].'-'.$roomrate['idroom']; ?>">
480 <td class="vbo-roverv-rplan<?php echo $rplan_minlos || $rplan_haslos ? ' vbo-roverv-rplan-restricted' : ''; ?>" data-defrate="<?php echo $roomrate['cost']; ?>" data-roomname="<?php echo htmlspecialchars($roomrow['name']); ?>">
481 <span class="vbo-rplan-name<?php echo $derived_str ? ' vbo-rplan-is-derived' : ''; ?>" title="<?php echo $this->escape($derived_str); ?>"><?php echo $derived_icn . $roomrate['name']; ?></span>
482 <?php
483 if ($rplan_minlos || $rplan_haslos) {
484 ?>
485 <div class="vbo-roverv-rplan-restrictions<?php echo $rplan_haslos ? ' vbo-roverv-rplan-haslosrates' : ''; ?>">
486 <?php
487 if ($rplan_haslos) {
488 ?>
489 <span class="badge badge-info vbo-roverv-rplan-restrictions-los">LOS</span>
490 <?php
491 }
492 if ($rplan_minlos) {
493 ?>
494 <span class="vbo-roverv-rplan-restrictions-lbl"><?php echo JText::translate('VBOMINIMUMSTAY'); ?></span>
495 <span class="vbo-roverv-rplan-restrictions-val"><?php echo $rplan_minlos; ?></span>
496 <?php
497 }
498 ?>
499 </div>
500 <?php
501 }
502 ?>
503 </td>
504 <?php
505 while ($cell_count < $MAX_DAYS) {
506 $style = '';
507 $dclass = "vbo-roverw-rplan-on";
508 if (count($closed_roomrateplans) > 0 && array_key_exists($roomrate['idprice'], $closed_roomrateplans) && in_array(date('Y-m-d', $nowts[0]), $closed_roomrateplans[$roomrate['idprice']])) {
509 $dclass = "vbo-roverw-rplan-off";
510 }
511 $id_block = "cell-".$nowts['mday'].'-'.$nowts['mon']."-".$nowts['year']."-".$roomrate['idprice']."-".$roomrate['idroom'];
512 $dclass .= ' day-block';
513
514 $today_tsin = mktime($pcheckinh, $pcheckinm, 0, $nowts['mon'], $nowts['mday'], $nowts['year']);
515 $today_tsout = mktime($pcheckouth, $pcheckoutm, 0, $nowts['mon'], ($nowts['mday'] + 1), $nowts['year']);
516
517 $tars = VikBooking::applySeasonsRoom([$roomrate], $today_tsin, $today_tsout, [], $cached_seasons);
518
519 // store the OBP overrides for this day and rate plan
520 if (!empty($tars[0]['occupancy_ovr'])) {
521 if (!isset($room_obp_overrides[$nowts[0]])) {
522 $room_obp_overrides[$nowts[0]] = [];
523 }
524 $room_obp_overrides[$nowts[0]][] = [
525 'idprice' => $roomrate['idprice'],
526 'obp' => $tars[0]['occupancy_ovr'],
527 ];
528 }
529
530 ?>
531 <td align="center" class="<?php echo $dclass.' cell-'.$nowts['mday'].'-'.$nowts['mon']; ?>" id="<?php echo $id_block; ?>" data-vboprice="<?php echo $tars[0]['cost']; ?>" data-vbodate="<?php echo date('Y-m-d', $nowts[0]); ?>" data-vbodateread="<?php echo $days_labels[$nowts['wday']].', '.$months_labels[$nowts['mon']-1].' '.$nowts['mday']; ?>" data-vbospids="<?php echo (array_key_exists('spids', $tars[0]) && count($tars[0]['spids']) > 0 ? implode('-', $tars[0]['spids']) : ''); ?>"<?php echo $style; ?>>
532 <span class="vbo-rplan-currency"><?php echo $currencysymb; ?></span>
533 <span class="vbo-rplan-price"><?php echo $tars[0]['cost']; ?></span>
534 </td>
535 <?php
536
537 $next = $nowts['mday'] + 1;
538 $dayts = mktime(0, 0, 0, $nowts['mon'], $next, $nowts['year']);
539 $nowts = getdate($dayts);
540
541 $cell_count++;
542 }
543 ?>
544 </tr>
545 <?php
546 }
547
548 // Occupancy Based Pricing rules
549 $adultsdiff = array_reverse(VikBooking::loadRoomAdultsDiff($roomrow['id']), true);
550
551 // display the room OBP rules (must be defined at room-level or no overrides will be displayed)
552 foreach ($adultsdiff as $adults => $diffusageprice) {
553 $base_obp_rules = $diffusageprice;
554 $nowts = getdate($tsstart);
555 $cell_count = 0;
556 ?>
557 <tr class="vbo-roverviewtablerow-occupancy">
558 <td>
559 <span class="vbo-rplan-name vbo-rplan-name-occ">
560 <span><?php echo JText::translate('VBPVIEWOPTIONALSTHREE'); ?></span>
561 <span class="vbo-occ-label"><?php VikBookingIcons::e('male'); ?> x <?php echo $adults ?></span>
562 </span>
563 </td>
564 <?php
565 while ($cell_count < $MAX_DAYS) {
566 $style = '';
567 $dclass = "vbo-roverw-rplan-occ";
568
569 // check for OBP overrides
570 $compare_obp_ovr = [];
571 if (isset($room_obp_overrides[$nowts[0]]) && count($room_obp_overrides[$nowts[0]]) === count($roomrates[$rid])) {
572 // the OBP overrides affect all room rate plans, make sure the overrides are identical though
573 $compare_obp_ovr = $room_obp_overrides[$nowts[0]][0]['obp'];
574 foreach ($room_obp_overrides[$nowts[0]] as $room_obp_override) {
575 if ($compare_obp_ovr != $room_obp_override['obp']) {
576 // this OBP override does not match the first one
577 $compare_obp_ovr = [];
578 break;
579 }
580 }
581 }
582 if ($compare_obp_ovr && isset($compare_obp_ovr[$adults])) {
583 // display the OBP override value for this day
584 $diffusageprice = $compare_obp_ovr[$adults];
585 } else {
586 // display the OBP rules at room-level
587 $diffusageprice = $base_obp_rules;
588 }
589
590 if (($diffusageprice['value'] - floor($diffusageprice['value'])) <= 0) {
591 $diffusageprice['value'] = intval($diffusageprice['value']);
592 }
593
594 if ($diffusageprice['chdisc'] == 1) {
595 // charge
596 if ($diffusageprice['valpcent'] == 1) {
597 // fixed value
598 $occ_diff = '+ ' . $currencysymb . ' ' . $diffusageprice['value'];
599 } else {
600 // percentage value
601 $occ_diff = '+ ' . $diffusageprice['value'] . '%';
602 }
603 } else {
604 // discount
605 if ($diffusageprice['valpcent'] == 1) {
606 // fixed value
607 $occ_diff = '- ' . $currencysymb . ' ' . $diffusageprice['value'];
608 } else {
609 // percentage value
610 $occ_diff = '- ' . $diffusageprice['value'] . '%';
611 }
612 }
613
614 ?>
615 <td align="center" class="<?php echo $dclass; ?>"<?php echo $style; ?>>
616 <span class="vbo-roverv-priceoccdiff"><?php echo $occ_diff; ?></span>
617 </td>
618 <?php
619
620 $next = $nowts['mday'] + 1;
621 $dayts = mktime(0, 0, 0, $nowts['mon'], $next, $nowts['year']);
622 $nowts = getdate($dayts);
623
624 $cell_count++;
625 }
626 ?>
627 </tr>
628 <?php
629 }
630 ?>
631 <tr class="vbo-roverviewtableavrow">
632 <td><span class="vbo-roverview-roomunits"><?php echo $roomrow['units']; ?></span><span class="vbo-roverview-uleftlbl"><?php echo JText::translate('VBPCHOOSEBUSYCAVAIL'); ?></span></td>
633 <?php
634 $nowts = getdate($tsstart);
635 $cell_count = 0;
636 $last_booking_info = [];
637 $last_booking_tag = [];
638 $room_bookings_pool = [];
639 while ($cell_count < $MAX_DAYS) {
640 $style = '';
641 $is_checkin = false;
642 $dclass = "vbo-roverw-daynotbusy";
643 $id_block = "cell-".$nowts['mday'].'-'.$nowts['mon']."-".$nowts['year']."-".$nowts['wday']."-".$rid."-avail";
644
645 $prev_day_key = date('Y-m-d', strtotime('-1 day', $nowts[0]));
646 $cur_day_key = date('Y-m-d', $nowts[0]);
647 $totfound = 0;
648 $last_bid_data = [];
649 $bids_pool = [];
650
651 $day_booking_snake = '';
652 $day_booking_data = [];
653
654 foreach (($this->booked_dates[$roomrow['id']] ?? []) as $b) {
655 $tmpone = getdate($b['checkin']);
656 $ritts = mktime(0, 0, 0, $tmpone['mon'], $tmpone['mday'], $tmpone['year']);
657 $tmptwo = getdate($b['checkout']);
658 $conts = mktime(0, 0, 0, $tmptwo['mon'], $tmptwo['mday'], $tmptwo['year']);
659 if ($nowts[0] >= $ritts && $nowts[0] < $conts) {
660 // booking found
661 $dclass = "vbo-roverw-daybusy";
662 $last_bid_data = $b;
663 $totfound++;
664
665 // push bid to pool
666 $bid_str = '-' . $b['idorder'] . '-';
667 if (!in_array($bid_str, $bids_pool)) {
668 $bids_pool[] = $bid_str;
669 }
670
671 // push day booking details
672 if (!isset($room_bookings_pool[$roomrow['id']])) {
673 $room_bookings_pool[$roomrow['id']] = [];
674 }
675 if (!isset($room_bookings_pool[$roomrow['id']][$cur_day_key])) {
676 $room_bookings_pool[$roomrow['id']][$cur_day_key] = [];
677 }
678 if ($roomrow['units'] == 1 && !$b['closure'] && !($b['sharedcal'] ?? 0)) {
679 // push room day booking
680 $room_bookings_pool[$roomrow['id']][$cur_day_key][] = $b;
681 }
682
683 if ($nowts[0] == $ritts) {
684 // turn flag on
685 $is_checkin = true;
686 }
687 }
688 }
689
690 // count units left
691 $units_remaining = $roomrow['units'] - $totfound;
692
693 if ($units_remaining > 0 && $units_remaining < $roomrow['units'] && $roomrow['units'] > 1) {
694 // room with hotel-like inventory
695 $dclass .= " vbo-roverw-daybusypartially";
696 } elseif ($units_remaining <= 0 && $roomrow['units'] <= 1 && $last_bid_data) {
697 // single-unit listing
698
699 // gather the booking color tag data
700 $btag_style = '';
701 $binfo = ($last_booking_info['data'] ?? null) && ($last_booking_info['id'] ?? null) == $last_bid_data['idorder'] ? $last_booking_info['data'] : $last_bid_data;
702 if ($binfo) {
703 $bcolortag = ($last_booking_tag['data'] ?? null) && ($last_booking_tag['id'] ?? null) == $last_bid_data['idorder'] ? $last_booking_tag['data'] : VikBooking::applyBookingColorTag($binfo);
704 if ($bcolortag) {
705 // set values
706 $bcolortag['name'] = JText::translate($bcolortag['name']);
707 $btag_style = "background-color: ".$bcolortag['color']."; color: ".(array_key_exists('fontcolor', $bcolortag) ? $bcolortag['fontcolor'] : '#ffffff').";";
708 $dclass .= ' vbo-roverw-hascolortag';
709 // cache data to avoid duplicate queries
710 $last_booking_info = [
711 'id' => $last_bid_data['idorder'],
712 'data' => $binfo,
713 ];
714 $last_booking_tag = [
715 'id' => $last_bid_data['idorder'],
716 'data' => $bcolortag,
717 ];
718 }
719 }
720 if (!empty($btag_style)) {
721 $style = !empty($style) ? ' style="display: none; '.$btag_style.'"' : ' style="'.$btag_style.'"';
722 $style .= ' title="'.addslashes($bcolortag['name']).'"';
723 }
724
725 /**
726 * Build room-day-booking snake.
727 *
728 * @since 1.17.2 (J) - 1.7.2 (WP)
729 */
730 if ($room_bookings_pool[$roomrow['id']][$cur_day_key][0] ?? []) {
731 $day_booking_data = $room_bookings_pool[$roomrow['id']][$cur_day_key][0];
732 }
733 if ($room_bookings_pool[$roomrow['id']][$prev_day_key][0] ?? []) {
734 if (!$room_bookings_pool[$roomrow['id']][$prev_day_key][0]['closure'] && date('Y-m-d', $room_bookings_pool[$roomrow['id']][$prev_day_key][0]['checkout']) == $cur_day_key) {
735 // prepend checkout snake
736 $day_booking_snake .= '<div class="vbo-tableaux-booking vbo-tableaux-booking-singleunit vbo-tableaux-booking-checkout"><span>&nbsp;</span></div>';
737 }
738 }
739 if ($day_booking_data && !$day_booking_data['closure']) {
740 // build tableaux-style snake container for guest
741 $customer_descr = '';
742 if ($is_checkin) {
743 // customer details
744 if (!empty($day_booking_data['first_name']) || !empty($day_booking_data['last_name'])) {
745 // check if we need to display a profile picture or a channel logo
746 $booking_avatar_src = null;
747 $booking_avatar_alt = null;
748 if (!empty($day_booking_data['pic'])) {
749 // customer profile picture
750 $booking_avatar_src = strpos($day_booking_data['pic'], 'http') === 0 ? $day_booking_data['pic'] : VBO_SITE_URI . 'resources/uploads/' . $day_booking_data['pic'];
751 $booking_avatar_alt = basename($booking_avatar_src);
752 } elseif (!empty($day_booking_data['idorderota']) && !empty($day_booking_data['channel'])) {
753 // channel logo
754 $logo_helper = VikBooking::getVcmChannelsLogo($day_booking_data['channel'], $get_istance = true);
755 if ($logo_helper !== false) {
756 $booking_avatar_src = $logo_helper->getSmallLogoURL();
757 $booking_avatar_alt = $logo_helper->provenience;
758 }
759 }
760
761 if (!empty($booking_avatar_src)) {
762 // make sure the alt attribute is not too long in case of broken images
763 $booking_avatar_alt = !empty($booking_avatar_alt) && strlen($booking_avatar_alt) > 15 ? '...' . substr($booking_avatar_alt, -12) : $booking_avatar_alt;
764 // append booking avatar image
765 $customer_descr .= '<span class="vbo-tableaux-booking-avatar"><img src="' . $booking_avatar_src . '" class="vbo-tableaux-booking-avatar-img" ' . (!empty($booking_avatar_alt) ? 'alt="' . htmlspecialchars($booking_avatar_alt) . '" ' : '') . '/></span>';
766 }
767
768 // customer name
769 $customer_fullname = trim($day_booking_data['first_name'] . ' ' . $day_booking_data['last_name']);
770 if (strlen($customer_fullname) > 26) {
771 if (function_exists('mb_substr')) {
772 $customer_fullname = trim(mb_substr($customer_fullname, 0, 26, 'UTF-8')) . '..';
773 } else {
774 $customer_fullname = trim(substr($customer_fullname, 0, 26)) . '..';
775 }
776 }
777 $customer_descr .= '<span class="vbo-tableaux-guest-name">' . $customer_fullname . '</span>';
778 } else {
779 // parse the customer data string
780 $custdata_parts = explode("\n", $day_booking_data['custdata']);
781 $enoughinfo = false;
782 if (count($custdata_parts) > 2 && strpos($custdata_parts[0], ':') !== false && strpos($custdata_parts[1], ':') !== false) {
783 // get the first two fields
784 $custvalues = array();
785 foreach ($custdata_parts as $custdet) {
786 if (strlen($custdet) < 1) {
787 continue;
788 }
789 $custdet_parts = explode(':', $custdet);
790 if (count($custdet_parts) >= 2) {
791 unset($custdet_parts[0]);
792 array_push($custvalues, trim(implode(':', $custdet_parts)));
793 }
794 if (count($custvalues) > 1) {
795 break;
796 }
797 }
798 if (count($custvalues) > 1) {
799 $enoughinfo = true;
800 $customer_nominative = trim(implode(' ', $custvalues));
801 if (strlen($customer_nominative) > 26) {
802 if (function_exists('mb_substr')) {
803 $customer_nominative = trim(mb_substr($customer_nominative, 0, 26, 'UTF-8')) . '..';
804 } else {
805 $customer_nominative = trim(substr($customer_nominative, 0, 26)) . '..';
806 }
807 }
808 if (!empty($day_booking_data['idorderota']) && !empty($day_booking_data['channel'])) {
809 // add support for the channel logo for the imported OTA reservations with no customer record
810 $logo_helper = VikBooking::getVcmChannelsLogo($day_booking_data['channel'], $get_istance = true);
811 if ($logo_helper !== false) {
812 $booking_avatar_src = $logo_helper->getSmallLogoURL();
813 $booking_avatar_alt = $logo_helper->provenience;
814 // make sure the alt attribute is not too long in case of broken images
815 $booking_avatar_alt = !empty($booking_avatar_alt) && strlen($booking_avatar_alt) > 15 ? '...' . substr($booking_avatar_alt, -12) : $booking_avatar_alt;
816 // append booking avatar image
817 $customer_descr .= '<span class="vbo-tableaux-booking-avatar"><img src="' . $booking_avatar_src . '" class="vbo-tableaux-booking-avatar-img" ' . (!empty($booking_avatar_alt) ? 'alt="' . htmlspecialchars($booking_avatar_alt) . '" ' : '') . '/></span>';
818 }
819 }
820 // set customer nominative built
821 $customer_descr .= '<span class="vbo-tableaux-guest-name">' . $customer_nominative . '</span>';
822 }
823 }
824 if (!$enoughinfo) {
825 $customer_descr .= '<span class="vbo-tableaux-guest-name">#' . $day_booking_data['idorder'] . '</span>';
826 }
827 }
828 }
829 // set value
830 $day_booking_snake .= '<div class="vbo-tableaux-booking vbo-tableaux-booking-singleunit ' . ($is_checkin ? 'vbo-tableaux-booking-checkin' : 'vbo-tableaux-booking-stay') . '"' . ($is_checkin ? ' data-nights="' . $day_booking_data['days'] . '"' : '') . '><span>' . ($is_checkin ? $customer_descr : '&nbsp;') . '</span></div>';
831 }
832 }
833
834 if ($roomrow['units'] <= 1 && !$totfound) {
835 // no booked records
836 if ($room_bookings_pool[$roomrow['id']][$prev_day_key][0] ?? []) {
837 if (!$room_bookings_pool[$roomrow['id']][$prev_day_key][0]['closure'] && date('Y-m-d', $room_bookings_pool[$roomrow['id']][$prev_day_key][0]['checkout']) == $cur_day_key) {
838 // prepend checkout snake
839 $day_booking_snake .= '<div class="vbo-tableaux-booking vbo-tableaux-booking-singleunit vbo-tableaux-booking-checkout"><span>&nbsp;</span></div>';
840 }
841 }
842 }
843
844 ?>
845 <td align="center" class="vbo-grid-avcell <?php echo $dclass.' cell-'.$nowts['mday'].'-'.$nowts['mon']; ?>" id="<?php echo $id_block; ?>" data-bids="<?php echo implode(',', $bids_pool); ?>" data-vbodateread="<?php echo $days_labels[$nowts['wday']].', '.$months_labels[$nowts['mon']-1].' '.$nowts['mday']; ?>" data-curunits="<?php echo $units_remaining; ?>"<?php echo $style; ?>>
846 <?php
847 if (($day_booking_snake ?? '')) {
848 echo $day_booking_snake;
849 } else {
850 ?>
851 <span class="vbo-roverw-curunits"><?php echo $units_remaining; ?></span>
852 <?php
853 }
854 ?>
855 </td>
856 <?php
857
858 $next = $nowts['mday'] + 1;
859 $dayts = mktime(0, 0, 0, ($nowts['mon'] < 10 ? "0".$nowts['mon'] : $nowts['mon']), ($next < 10 ? "0".$next : $next), $nowts['year']);
860 $nowts = getdate($dayts);
861
862 $cell_count++;
863 }
864 ?>
865 </tr>
866 <?php
867 // VBO 1.11 - MinLOS restrictions row
868 ?>
869 <tr class="vbo-roverviewtablerow-restrs">
870 <td><span class="vbo-roverview-restrs"><?php echo JText::translate('VBOMINIMUMSTAY'); ?></span></td>
871 <?php
872 $nowts = getdate($tsstart);
873 $cell_count = 0;
874 $glob_minlos = VikBooking::getDefaultNightsCalendar();
875 $glob_minlos = $glob_minlos < 1 ? 1 : $glob_minlos;
876 while ($cell_count < $MAX_DAYS) {
877 $style = '';
878 $dclass = "vbo-roverw-rplan-restr";
879
880 $id_block = "cell-".$nowts['mday'].'-'.$nowts['mon']."-".$nowts['year']."-".$rid."-restr";
881
882 $today_tsin = mktime(0, 0, 0, $nowts['mon'], $nowts['mday'], $nowts['year']);
883 $today_tsout = mktime(0, 0, 0, $nowts['mon'], ($nowts['mday'] + 1), $nowts['year']);
884
885 $restr = VikBooking::parseSeasonRestrictions($today_tsin, $today_tsout, 1, (isset($this->all_restrictions[$rid]) ? $this->all_restrictions[$rid] : array()));
886 $minlos = count($restr) ? $restr['minlos'] : $glob_minlos;
887
888 ?>
889 <td align="center" class="<?php echo $dclass; ?>" id="<?php echo $id_block; ?>"<?php echo $style; ?>>
890 <span class="vbo-roverw-curminlos<?php echo $minlos > 1 ? ' vbo-roverw-curminlos-active' : ''; ?>"><?php echo $minlos; ?></span>
891 </td>
892 <?php
893
894 $next = $nowts['mday'] + 1;
895 $dayts = mktime(0, 0, 0, ($nowts['mon'] < 10 ? "0".$nowts['mon'] : $nowts['mon']), ($next < 10 ? "0".$next : $next), $nowts['year']);
896 $nowts = getdate($dayts);
897
898 $cell_count++;
899 }
900 ?>
901 </tr>
902 <?php
903 //
904 ?>
905 </tbody>
906 </table>
907 </div>
908 <div class="vbo-ratesoverview-period-container">
909 <div class="vbo-ratesoverview-period-inner">
910 <div class="vbo-ratesoverview-period-lbl">
911 <span><?php echo JText::translate('VBOROVWSELPERIOD'); ?></span>
912 </div>
913 <div class="vbo-ratesoverview-period-boxes">
914 <div class="vbo-ratesoverview-period-boxes-inner">
915 <div class="vbo-ratesoverview-period-box-left">
916 <div class="vbo-ratesoverview-period-box-lbl">
917 <span><?php echo JText::translate('VBOROVWSELPERIODFROM'); ?></span>
918 </div>
919 <div class="vbo-ratesoverview-period-box-val">
920 <div class="vbo-ratesoverview-period-from">
921 <span class="vbo-ratesoverview-period-wday"></span>
922 <span class="vbo-ratesoverview-period-mday"></span>
923 <span class="vbo-ratesoverview-period-month"></span>
924 </div>
925 <span class="vbo-ratesoverview-period-from-icon"><?php VikBookingIcons::e('calendar'); ?></span>
926 </div>
927 </div>
928 <div class="vbo-ratesoverview-period-box-right">
929 <div class="vbo-ratesoverview-period-box-lbl">
930 <span><?php echo JText::translate('VBOROVWSELPERIODTO'); ?></span>
931 </div>
932 <div class="vbo-ratesoverview-period-box-val">
933 <div class="vbo-ratesoverview-period-to">
934 <span class="vbo-ratesoverview-period-wday"></span>
935 <span class="vbo-ratesoverview-period-mday"></span>
936 <span class="vbo-ratesoverview-period-month"></span>
937 </div>
938 <span class="vbo-ratesoverview-period-to-icon"><?php VikBookingIcons::e('calendar'); ?></span>
939 </div>
940 </div>
941 </div>
942 <div class="vbo-ratesoverview-period-box-cals" style="display: none;">
943 <div class="vbo-ratesoverview-period-box-cals-inner">
944 <div class="vbo-ratesoverview-period-cal-left">
945 <h4><?php echo JText::translate('VBOROVWSELPERIODFROM'); ?></h4>
946 <div class="vbo-period-from" data-idroom="<?php echo $rid; ?>" data-roomname="<?php echo htmlspecialchars($roomrow['name']); ?>"></div>
947 <input type="hidden" class="vbo-period-from-val" value="" />
948 </div>
949 <div class="vbo-ratesoverview-period-cal-right">
950 <h4><?php echo JText::translate('VBOROVWSELPERIODTO'); ?></h4>
951 <div class="vbo-period-to" data-idroom="<?php echo $rid; ?>" data-roomname="<?php echo htmlspecialchars($roomrow['name']); ?>"></div>
952 <input type="hidden" class="vbo-period-to-val" value="" />
953 </div>
954 <div class="vbo-ratesoverview-period-cal-cmd">
955 <h4><?php echo JText::translate('VBOROVWSELRPLAN'); ?></h4>
956 <div class="vbo-ratesoverview-period-cal-cmd-inner">
957 <select class="vbo-selperiod-rplanid" onchange="vboUpdateRplan(this);">
958 <?php
959 foreach ($roomrates[$rid] as $krr => $roomrate) {
960 ?>
961 <option value="<?php echo $roomrate['idprice']; ?>" data-defrate="<?php echo $roomrate['cost']; ?>"<?php echo $krr < 1 ? ' selected="selected"' : ''; ?>><?php echo $roomrate['name']; ?></option>
962 <?php
963 }
964 ?>
965 </select>
966 </div>
967 </div>
968 </div>
969 </div>
970 </div>
971 </div>
972 <div class="vbo-ratesoverview-orphans-wrapper" id="vbo-ratesoverview-orphans-wrapper-<?php echo $rid; ?>" style="display: none;">
973 <div class="vbo-ratesoverview-orphans-lbl">
974 <span><?php echo $vbo_app->createPopover(array('title' => JText::translate('VBORPHANSFOUND'), 'content' => JText::translate('VBORPHANSFOUNDSHELP'), 'icon_class' => VikBookingIcons::i('exclamation-triangle'))); ?> <?php echo JText::translate('VBORPHANSFOUND'); ?></span>
975 </div>
976 <div class="vbo-ratesoverview-orphans-list" id="vbo-ratesoverview-orphans-list-<?php echo $rid; ?>"></div>
977 </div>
978 </div>
979 </div>
980 </div>
981
982 <?php
983 // start los pricing overview IF statement for just 1 room
984 if (count($this->req_room_ids) < 2) :
985 ?>
986 <div class="vbo-ratesoverview-lostab-cont"<?php echo (!empty($cookie_tab) && $cookie_tab == 'cal' ? ' style="display: none;"' : ''); ?>>
987 <?php
988 if (count($seasons_cal) > 0) {
989 //Special Prices Timeline
990 if (isset($seasons_cal['seasons']) && count($seasons_cal['seasons'])) {
991 ?>
992 <div class="vbo-timeline-container">
993 <ul id="vbo-timeline">
994 <?php
995 foreach ($seasons_cal['seasons'] as $ks => $timeseason) {
996 $s_val_diff = '';
997 if ($timeseason['val_pcent'] == 2) {
998 //percentage
999 $s_val_diff = (($timeseason['diffcost'] - abs($timeseason['diffcost'])) > 0.00 ? VikBooking::numberFormat($timeseason['diffcost']) : intval($timeseason['diffcost']))." %";
1000 } else {
1001 //absolute
1002 $s_val_diff = $currencysymb.''.VikBooking::numberFormat($timeseason['diffcost']);
1003 }
1004 $s_explanation = array();
1005 if (empty($timeseason['year'])) {
1006 $s_explanation[] = JText::translate('VBSEASONANYYEARS');
1007 }
1008 if (!empty($timeseason['losoverride'])) {
1009 $s_explanation[] = JText::translate('VBSEASONBASEDLOS');
1010 }
1011 ?>
1012 <li data-fromts="<?php echo $timeseason['from_ts']; ?>" data-tots="<?php echo $timeseason['to_ts']; ?>">
1013 <input type="radio" name="timeline" class="vbo-timeline-radio" id="vbo-timeline-dot<?php echo $ks; ?>" <?php echo $ks === 0 ? 'checked="checked"' : ''; ?>/>
1014 <div class="vbo-timeline-relative">
1015 <label class="vbo-timeline-label" for="vbo-timeline-dot<?php echo $ks; ?>"><?php echo $timeseason['spname']; ?></label>
1016 <span class="vbo-timeline-date"><?php echo VikBooking::formatSeasonDates($timeseason['from_ts'], $timeseason['to_ts']); ?></span>
1017 <span class="vbo-timeline-circle" onclick="Javascript: jQuery('#vbo-timeline-dot<?php echo $ks; ?>').trigger('click');"></span>
1018 </div>
1019 <div class="vbo-timeline-content">
1020 <p>
1021 <span class="vbo-seasons-calendar-slabel vbo-seasons-calendar-season-<?php echo $timeseason['type'] == 2 ? 'discount' : 'charge'; ?>"><?php echo $timeseason['type'] == 2 ? '-' : '+'; ?> <?php echo $s_val_diff; ?> <?php echo JText::translate('VBSEASONPERNIGHT'); ?></span>
1022 <br/>
1023 <?php
1024 if (count($s_explanation) > 0) {
1025 echo implode(' - ', $s_explanation);
1026 }
1027 ?>
1028 </p>
1029 </div>
1030 </li>
1031 <?php
1032 }
1033 ?>
1034 </ul>
1035 </div>
1036
1037 <script>
1038 jQuery(function() {
1039 jQuery('.vbo-timeline-container').css('min-height', (jQuery('.vbo-timeline-container').outerHeight() + 20));
1040 });
1041 </script>
1042 <?php
1043 }
1044 //
1045 //Begin Seasons Calendar
1046 ?>
1047 <div class="table-responsive">
1048 <table class="table vbo-seasons-calendar-table">
1049 <tr class="vbo-seasons-calendar-nightsrow">
1050 <td>&nbsp;</td>
1051 <?php
1052 foreach ($seasons_cal['offseason'] as $numnights => $ntars) {
1053 ?>
1054 <td><span><?php echo JText::sprintf(($numnights > 1 ? 'VBOSEASONCALNUMNIGHTS' : 'VBOSEASONCALNUMNIGHT'), $numnights); ?></span></td>
1055 <?php
1056 }
1057 ?>
1058 </tr>
1059 <tr class="vbo-seasons-calendar-offseasonrow">
1060 <td>
1061 <span class="vbo-seasons-calendar-offseasonname"><?php echo JText::translate('VBOSEASONSCALOFFSEASONPRICES'); ?></span>
1062 </td>
1063 <?php
1064 foreach ($seasons_cal['offseason'] as $numnights => $tars) {
1065 ?>
1066 <td>
1067 <div class="vbo-seasons-calendar-offseasoncosts">
1068 <?php
1069 foreach ($tars as $tar) {
1070 ?>
1071 <div class="vbo-seasons-calendar-offseasoncost">
1072 <?php
1073 if ($price_types_show) {
1074 ?>
1075 <span class="vbo-seasons-calendar-pricename"><?php echo $tar['name']; ?></span>
1076 <?php
1077 }
1078 ?>
1079 <span class="vbo-seasons-calendar-pricecost">
1080 <span class="vbo_currency"><?php echo $currencysymb; ?></span><span class="vbo_price"><?php echo VikBooking::numberFormat($tar['cost']); ?></span>
1081 </span>
1082 </div>
1083 <?php
1084 if (!$price_types_show) {
1085 break;
1086 }
1087 }
1088 ?>
1089 </div>
1090 </td>
1091 <?php
1092 }
1093 ?>
1094 </tr>
1095 <?php
1096 if (!isset($seasons_cal['seasons'])) {
1097 $seasons_cal['seasons'] = array();
1098 }
1099 foreach ($seasons_cal['seasons'] as $s_id => $s) {
1100 $restr_diff_nights = array();
1101 if ($los_show && array_key_exists($s_id, $seasons_cal['restrictions'])) {
1102 $restr_diff_nights = VikBooking::compareSeasonRestrictionsNights($seasons_cal['restrictions'][$s_id]);
1103 }
1104 $s_val_diff = '';
1105 if ($s['val_pcent'] == 2) {
1106 //percentage
1107 $s_val_diff = (($s['diffcost'] - abs($s['diffcost'])) > 0.00 ? VikBooking::numberFormat($s['diffcost']) : intval($s['diffcost']))." %";
1108 } else {
1109 //absolute
1110 $s_val_diff = $currencysymb.''.VikBooking::numberFormat($s['diffcost']);
1111 }
1112 ?>
1113 <tr class="vbo-seasons-calendar-seasonrow">
1114 <td>
1115 <div class="vbo-seasons-calendar-seasondates">
1116 <span class="vbo-seasons-calendar-seasonfrom"><?php echo date(str_replace("/", $datesep, $df), $s['from_ts']); ?></span>
1117 <span class="vbo-seasons-calendar-seasondates-separe">-</span>
1118 <span class="vbo-seasons-calendar-seasonto"><?php echo date(str_replace("/", $datesep, $df), $s['to_ts']); ?></span>
1119 </div>
1120 <div class="vbo-seasons-calendar-seasonchargedisc">
1121 <span class="vbo-seasons-calendar-slabel vbo-seasons-calendar-season-<?php echo $s['type'] == 2 ? 'discount' : 'charge'; ?>"><span class="vbo-seasons-calendar-operator"><?php echo $s['type'] == 2 ? '-' : '+'; ?></span><?php echo $s_val_diff; ?></span>
1122 </div>
1123 <span class="vbo-seasons-calendar-seasonname"><a href="index.php?option=com_vikbooking&amp;task=editseason&amp;cid[]=<?php echo $s['id']; ?>" target="_blank"><?php echo $s['spname']; ?></a></span>
1124 <?php
1125 if ($los_show && array_key_exists($s_id, $seasons_cal['restrictions']) && count($restr_diff_nights) == 0) {
1126 //Season Restrictions
1127 $season_restrictions = array();
1128 foreach ($seasons_cal['restrictions'][$s_id] as $restr) {
1129 $season_restrictions = $restr;
1130 break;
1131 }
1132 ?>
1133 <div class="vbo-seasons-calendar-restrictions">
1134 <?php
1135 if ($season_restrictions['minlos'] > 1) {
1136 ?>
1137 <span class="vbo-seasons-calendar-restriction-minlos"><?php echo JText::translate('VBORESTRMINLOS'); ?><span class="vbo-seasons-calendar-restriction-minlos-badge"><?php echo $season_restrictions['minlos']; ?></span></span>
1138 <?php
1139 }
1140 if (array_key_exists('maxlos', $season_restrictions) && $season_restrictions['maxlos'] > 1) {
1141 ?>
1142 <span class="vbo-seasons-calendar-restriction-maxlos"><?php echo JText::translate('VBORESTRMAXLOS'); ?><span class="vbo-seasons-calendar-restriction-maxlos-badge"><?php echo $season_restrictions['maxlos']; ?></span></span>
1143 <?php
1144 }
1145 if (array_key_exists('wdays', $season_restrictions) && count($season_restrictions['wdays']) > 0) {
1146 ?>
1147 <div class="vbo-seasons-calendar-restriction-wdays">
1148 <label><?php echo JText::translate((count($season_restrictions['wdays']) > 1 ? 'VBORESTRARRIVWDAYS' : 'VBORESTRARRIVWDAY')); ?></label>
1149 <?php
1150 foreach ($season_restrictions['wdays'] as $wday) {
1151 ?>
1152 <span class="vbo-seasons-calendar-restriction-wday"><?php echo VikBooking::sayWeekDay($wday); ?></span>
1153 <?php
1154 }
1155 ?>
1156 </div>
1157 <?php
1158 } elseif ((array_key_exists('cta', $season_restrictions) && count($season_restrictions['cta']) > 0) || (array_key_exists('ctd', $season_restrictions) && count($season_restrictions['ctd']) > 0)) {
1159 if (array_key_exists('cta', $season_restrictions) && count($season_restrictions['cta']) > 0) {
1160 ?>
1161 <div class="vbo-seasons-calendar-restriction-wdays vbo-seasons-calendar-restriction-cta">
1162 <label><?php echo JText::translate('VBORESTRWDAYSCTA'); ?></label>
1163 <?php
1164 foreach ($season_restrictions['cta'] as $wday) {
1165 ?>
1166 <span class="vbo-seasons-calendar-restriction-wday"><?php echo VikBooking::sayWeekDay(str_replace('-', '', $wday)); ?></span>
1167 <?php
1168 }
1169 ?>
1170 </div>
1171 <?php
1172 }
1173 if (array_key_exists('ctd', $season_restrictions) && count($season_restrictions['ctd']) > 0) {
1174 ?>
1175 <div class="vbo-seasons-calendar-restriction-wdays vbo-seasons-calendar-restriction-ctd">
1176 <label><?php echo JText::translate('VBORESTRWDAYSCTD'); ?></label>
1177 <?php
1178 foreach ($season_restrictions['ctd'] as $wday) {
1179 ?>
1180 <span class="vbo-seasons-calendar-restriction-wday"><?php echo VikBooking::sayWeekDay(str_replace('-', '', $wday)); ?></span>
1181 <?php
1182 }
1183 ?>
1184 </div>
1185 <?php
1186 }
1187 }
1188 ?>
1189 </div>
1190 <?php
1191 }
1192 ?>
1193 </td>
1194 <?php
1195 if (array_key_exists($s_id, $seasons_cal['season_prices']) && count($seasons_cal['season_prices'][$s_id]) > 0) {
1196 foreach ($seasons_cal['season_prices'][$s_id] as $numnights => $tars) {
1197 $show_day_cost = true;
1198 if ($los_show && array_key_exists($s_id, $seasons_cal['restrictions']) && array_key_exists($numnights, $seasons_cal['restrictions'][$s_id])) {
1199 if ($seasons_cal['restrictions'][$s_id][$numnights]['allowed'] === false) {
1200 $show_day_cost = false;
1201 }
1202 }
1203 ?>
1204 <td>
1205 <?php
1206 if ($show_day_cost) {
1207 ?>
1208 <div class="vbo-seasons-calendar-seasoncosts">
1209 <?php
1210 foreach ($tars as $tar) {
1211 //print the types of price that are not being modified by this special price with opacity
1212 $not_affected = (!array_key_exists('origdailycost', $tar));
1213 //
1214 ?>
1215 <div class="vbo-seasons-calendar-seasoncost<?php echo ($not_affected ? ' vbo-seasons-calendar-seasoncost-notaffected' : ''); ?>">
1216 <?php
1217 if ($price_types_show) {
1218 ?>
1219 <span class="vbo-seasons-calendar-pricename"><?php echo $tar['name']; ?></span>
1220 <?php
1221 }
1222 ?>
1223 <span class="vbo-seasons-calendar-pricecost">
1224 <span class="vbo_currency"><?php echo $currencysymb; ?></span><span class="vbo_price"><?php echo VikBooking::numberFormat($tar['cost']); ?></span>
1225 </span>
1226 </div>
1227 <?php
1228 if (!$price_types_show) {
1229 break;
1230 }
1231 }
1232 ?>
1233 </div>
1234 <?php
1235 } else {
1236 ?>
1237 <div class="vbo-seasons-calendar-seasoncosts-disabled"></div>
1238 <?php
1239 }
1240 ?>
1241 </td>
1242 <?php
1243 }
1244 }
1245 ?>
1246 </tr>
1247 <?php
1248 }
1249 ?>
1250 </table>
1251 </div>
1252 <?php
1253 //End Seasons Calendar
1254 } else {
1255 ?>
1256 <p class="vbo-warning"><?php echo JText::translate('VBOWARNNORATESROOM'); ?></p>
1257 <?php
1258 }
1259 ?>
1260 </div>
1261 <?php
1262 // end los pricing overview IF statement for just 1 room
1263 endif;
1264 }
1265 ?>
1266 </div>
1267 <?php
1268
1269 // overlay modal to change rates, restrictions etc..
1270 $vcm_enabled = VikBooking::vcmAutoUpdate();
1271 ?>
1272 <div class="vbo-ratesoverview-newratesrestr-helper" style="display: none;">
1273
1274 <div class="vbo-ratesoverview-newratesrestr-wrap">
1275 <div class="vbo-roverw-infoblock">
1276 <span id="rovervw-roomname"></span>
1277 <div class="vbo-roverw-inforates">
1278 <span id="rovervw-rplan"></span>
1279 <span id="rovervw-fromdate"></span> - <span id="rovervw-todate"></span>
1280 </div>
1281 </div>
1282 <div class="vbo-roverw-alldays">
1283 <div class="vbo-roverw-alldays-inner"></div>
1284 </div>
1285 <div class="vbo-roverw-setnewrate">
1286 <div class="vbo-roverw-setnewrate-title">
1287 <h4><?php VikBookingIcons::e('calculator'); ?> <?php echo JText::translate('VBO_RATES_AND_RESTR'); ?></h4>
1288 </div>
1289 <div class="vbo-roverw-flexnew">
1290 <div class="vbo-roverw-newrwrap">
1291 <h4><?php VikBookingIcons::e('edit'); ?> <?php echo JText::translate('VBRATESOVWSETNEWRATE'); ?></h4>
1292 <div class="vbo-roverw-newrcont">
1293 <label for="roverw-newrate" class="vbo-roverw-setnewrate-currency"><?php echo $currencysymb; ?></label>
1294 <input type="number" step="any" min="0" id="roverw-newrate" value="" placeholder="" size="7" />
1295 </div>
1296 </div>
1297 <div class="vbo-roverw-newrestr-wrap" style="display: none;">
1298 <div class="vbo-roverw-newrestrcont">
1299 <h4><?php VikBookingIcons::e('ban'); ?> <?php echo JText::translate('VBOMINIMUMSTAYSET'); ?></h4>
1300 <div class="vbo-roverw-newrestrcont-inner">
1301 <label for="roverw-newrestr" class="vbo-roverw-setnewrestr-lbl"><?php echo JText::translate('VBDAYS'); ?></label>
1302 <input type="number" step="1" min="0" id="roverw-newrestr" value="" size="7" />
1303 </div>
1304 </div>
1305 </div>
1306 </div>
1307 <div class="vbo-roverw-setnewrate-inner">
1308 <div class="vbo-roverw-setnewrate-vcm">
1309 <div class="vbo-roverw-setnewrate-vcm-head">
1310 <span class="<?php echo $vcm_enabled < 0 ? 'vbo-vcm-notinstalled' : 'vbo-vcm-installed'; ?>">
1311 <?php echo $vbo_app->createPopover(array('title' => JText::translate('VBOUPDRATESONCHANNELS'), 'content' => ($vcm_enabled < 0 ? JText::translate('VBCONFIGVCMAUTOUPDMISS') : JText::translate('VBOUPDRATESONCHANNELSHELP')), 'icon_class' => VikBookingIcons::i('rocket'))); ?>
1312 <?php echo JText::translate('VBOUPDRATESONCHANNELS'); ?>
1313 </span>
1314 </div>
1315 <div class="vbo-roverw-setnewrate-vcm-body vbo-toggle-small">
1316 <?php
1317 echo $vbo_app->printYesNoButtons('roverw-newrate-vcm', JText::translate('VBYES'), JText::translate('VBNO'), ($vcm_enabled > 0 ? 1 : 0), 1, 0, 'vboCheckVcmRestrictions();', ['blue']);
1318
1319 if ($vcm_enabled < 0) {
1320 // disable the toggle button when VCM is not available
1321 ?>
1322 <script type="text/javascript">
1323 jQuery(function() {
1324 jQuery('input[name="roverw-newrate-vcm"]').prop('disabled', true);
1325 });
1326 </script>
1327 <?php
1328 }
1329 ?>
1330 </div>
1331 <div class="vbo-roverw-setnewrate-vcm-otas"></div>
1332 </div>
1333 </div>
1334 </div>
1335 <div class="vbo-roverw-closeopenrp">
1336 <h4><i class="vboicn-switch"></i><?php echo JText::translate('VBRATESOVWCLOSEOPENRRP'); ?> <span id="rovervw-closeopen-rplan"></span> <span class="vbo-roverw-closeopenrp-arrow"><?php VikBookingIcons::e('chevron-down'); ?></span></h4>
1337 <div class="vbo-roverw-closeopenrp-btns">
1338 <button type="button" class="btn btn-danger" onclick="modRoomRatePlan('close');"><?php VikBookingIcons::e('lock'); ?> <?php echo JText::translate('VBRATESOVWCLOSERRP'); ?></button>
1339 <button type="button" class="btn btn-success" onclick="modRoomRatePlan('open');"><?php VikBookingIcons::e('lock-open'); ?> <?php echo JText::translate('VBRATESOVWOPENRRP'); ?></button>
1340 </div>
1341 </div>
1342 </div>
1343
1344 <div class="vbo-roverw-setnewrate-vcm-ota-pricing-alteration">
1345 <div class="vbo-roverw-setnewrate-vcm-ota-alteration-elem">
1346 <select data-alter-rule="rmodsop">
1347 <option value="1">+</option>
1348 <option value="0">-</option>
1349 </select>
1350 </div>
1351 <div class="vbo-roverw-setnewrate-vcm-ota-alteration-elem">
1352 <input type="number" value="" step="any" min="0" data-alter-rule="rmodsamount" />
1353 </div>
1354 <div class="vbo-roverw-setnewrate-vcm-ota-alteration-elem">
1355 <select data-alter-rule="rmodsval">
1356 <option value="1">%</option>
1357 <option value="0"><?php echo $currencysymb; ?></option>
1358 </select>
1359 </div>
1360 </div>
1361
1362 </div>
1363
1364 <form name="adminForm" id="adminForm" action="index.php" method="post">
1365 <input type="hidden" name="task" value="">
1366 <input type="hidden" name="option" value="com_vikbooking">
1367 </form>
1368
1369 <a id="vbo-base-booknow-link" style="display: none;" href="index.php?option=com_vikbooking&task=calendar&cid[]=&checkin=&checkout=&adults=&children=&idprice=&booknow=1"></a>
1370 <a class="vbo-basenavuri-details" href="index.php?option=com_vikbooking&task=editorder&goto=ratesoverv&cid[]=%d" style="display: none;"></a>
1371
1372 <script type="text/Javascript">
1373 function vboFormatCalDate(elem, idc) {
1374 var vb_period = elem.parent().find('.vbo-'+idc+'-val').val();
1375 if (!vb_period || !vb_period.length) {
1376 return false;
1377 }
1378 var vb_period_parts = vb_period.split("/");
1379 if ('%d/%m/%Y' == '<?php echo $vbo_df; ?>') {
1380 var period_date = new Date(vb_period_parts[2], (parseInt(vb_period_parts[1]) - 1), parseInt(vb_period_parts[0], 10), 0, 0, 0, 0);
1381 var data = [parseInt(vb_period_parts[0], 10), parseInt(vb_period_parts[1]), vb_period_parts[2]];
1382 } else if ('%m/%d/%Y' == '<?php echo $vbo_df; ?>') {
1383 var period_date = new Date(vb_period_parts[2], (parseInt(vb_period_parts[0]) - 1), parseInt(vb_period_parts[1], 10), 0, 0, 0, 0);
1384 var data = [parseInt(vb_period_parts[1], 10), parseInt(vb_period_parts[0]), vb_period_parts[2]];
1385 } else {
1386 var period_date = new Date(vb_period_parts[0], (parseInt(vb_period_parts[1]) - 1), parseInt(vb_period_parts[2], 10), 0, 0, 0, 0);
1387 var data = [parseInt(vb_period_parts[2], 10), parseInt(vb_period_parts[1]), vb_period_parts[0]];
1388 }
1389 var elcont = elem.closest('.vbo-ratesoverview-period-boxes').find('.vbo-ratesoverview-'+idc);
1390 elcont.find('.vbo-ratesoverview-period-wday').text(vboMapWdays[period_date.getDay()]);
1391 elcont.find('.vbo-ratesoverview-period-mday').text(period_date.getDate());
1392 elcont.find('.vbo-ratesoverview-period-month').text(vboMapMons[period_date.getMonth()]);
1393 elem.closest('.vbo-ratesoverview-period-boxes').find('.vbo-ratesoverview-'+idc+'-icon').hide();
1394 data.push(elem.closest('.vbo-ratesoverview-period-boxes').find('.vbo-selperiod-rplanid').val());
1395 data.push(elem.closest('.vbo-ratesoverview-period-boxes').find('.vbo-selperiod-rplanid option:selected').text());
1396 data.push(elem.closest('.vbo-ratesoverview-period-boxes').find('.vbo-selperiod-rplanid option:selected').attr('data-defrate'));
1397 data.push(elem.attr('data-idroom'));
1398 data.push(elem.attr('data-roomname'));
1399 var struct = getPeriodStructure(data);
1400 if (idc.indexOf('from') >= 0) {
1401 //period from date selected
1402 if (!vbolistener.pickFirst(struct)) {
1403 //first already picked: update it
1404 vbolistener.first = struct;
1405 }
1406 }
1407 if (idc.indexOf('to') >= 0) {
1408 //period to date selected
1409 if (!vbolistener.pickFirst(struct)) {
1410 //first already picked
1411 if ((vbolistener.first.isBeforeThan(struct) || vbolistener.first.isSameDay(struct)) && vbolistener.first.isSameRplan(struct) && vbolistener.first.isSameRoom(struct)) {
1412 //last > first: pick last
1413 if (vbolistener.pickLast(struct)) {
1414 showVboDialogPeriod();
1415 }
1416 }
1417 }
1418 }
1419 }
1420 function vbCalcNights() {
1421 var vbcheckin = document.getElementById('checkindate').value;
1422 var vbcheckout = document.getElementById('checkoutdate').value;
1423 if (!vbcheckin.length || !vbcheckout.length) {
1424 return;
1425 }
1426 var vbcheckinp = vbcheckin.split("-");
1427 var vbcheckoutp = vbcheckout.split("-");
1428 var vbinmonth = parseInt(vbcheckinp[1]);
1429 vbinmonth = vbinmonth - 1;
1430 var vbinday = parseInt(vbcheckinp[2], 10);
1431 var vbcheckind = new Date(vbcheckinp[0], vbinmonth, vbinday);
1432 var vboutmonth = parseInt(vbcheckoutp[1]);
1433 vboutmonth = vboutmonth - 1;
1434 var vboutday = parseInt(vbcheckoutp[2], 10);
1435 var vbcheckoutd = new Date(vbcheckoutp[0], vboutmonth, vboutday);
1436 var vbdivider = 1000 * 60 * 60 * 24;
1437 var vbints = vbcheckind.getTime();
1438 var vboutts = vbcheckoutd.getTime();
1439 if (vboutts > vbints) {
1440 var utc1 = Date.UTC(vbcheckind.getFullYear(), vbcheckind.getMonth(), vbcheckind.getDate());
1441 var utc2 = Date.UTC(vbcheckoutd.getFullYear(), vbcheckoutd.getMonth(), vbcheckoutd.getDate());
1442 var vbnights = Math.ceil((utc2 - utc1) / vbdivider);
1443 if (vbnights > 0) {
1444 jQuery('#vbo-numnights').val(vbnights);
1445 }
1446 }
1447 }
1448
1449 /**
1450 * Scrollable tables will show the arrows to navigate to the next N days.
1451 *
1452 * @since 1.15.0 (J) - 1.5.0 (WP)
1453 */
1454 function vboDisplayNextDays(btn) {
1455 var cell = jQuery(btn).closest('.vbo-roverv-gonext-cell');
1456 if (!cell || !cell.length) {
1457 return;
1458 }
1459 var rid = cell.attr('data-rid');
1460 if (!rid || !rid.length) {
1461 return;
1462 }
1463 var table = jQuery('table.vbratesoverviewtable[data-idroom="' + rid + '"]');
1464 if (!table || !table.length) {
1465 return;
1466 }
1467 // get the datepicker calendar instance for this room
1468 var calendar = table.find('input.vbodatepicker');
1469 try {
1470 // calculate the new date
1471 var cur_date = calendar.datepicker('getDate');
1472 var dobj = new Date(cur_date);
1473 dobj.setDate(dobj.getDate() + <?php echo $MAX_DAYS; ?>);
1474 // set the new date
1475 calendar.datepicker('setDate', dobj);
1476 // populate hidden form fields
1477 var parentform = calendar.closest('form');
1478 var roomsids = jQuery('#roomsel').val();
1479 if (roomsids) {
1480 if (!Array.isArray(roomsids)) {
1481 // if there is just one room type, the select is not multiple, so this is a string
1482 roomsids = [jQuery('#roomsel').val()];
1483 }
1484 jQuery.each(roomsids, function(k, v) {
1485 parentform.append('<input type="hidden" name="cid[]" value="' + v + '" />');
1486 });
1487 }
1488 // auto-submit form with new date
1489 parentform.submit();
1490 } catch(e) {
1491 console.error(e);
1492 alert('Calendar not found. Please change date manually.');
1493 }
1494 }
1495
1496 jQuery(function() {
1497 jQuery('.vbodatepicker').datepicker({
1498 showOn: 'focus',
1499 dateFormat: '<?php echo $juidf; ?>',
1500 minDate: '-1y',
1501 numberOfMonths: 2,
1502 changeMonth: true,
1503 changeYear: true,
1504 yearRange: '<?php echo (date('Y') - 1).':'.(date('Y') + 3); ?>',
1505 onSelect: function(selectedDate) {
1506 var parentform = jQuery(this).closest('form');
1507 var roomsids = jQuery('#roomsel').val();
1508 if (roomsids) {
1509 if (!Array.isArray(roomsids)) {
1510 // if there is just one room type, the select is not multiple, so this is a string
1511 roomsids = [jQuery('#roomsel').val()];
1512 }
1513 jQuery.each(roomsids, function(k, v) {
1514 parentform.append('<input type="hidden" name="cid[]" value="' + v + '" />');
1515 });
1516 }
1517 parentform.submit();
1518 }
1519 });
1520 jQuery('#checkindate').datepicker({
1521 showOn: 'focus',
1522 dateFormat: 'yy-mm-dd',
1523 minDate: '0d',
1524 numberOfMonths: 2,
1525 onSelect: function(selectedDate) {
1526 var nownights = parseInt(jQuery('#vbo-numnights').val());
1527 var nowcheckin = jQuery('#checkindate').datepicker('getDate');
1528 var nowcheckindate = new Date(nowcheckin.getTime());
1529 nowcheckindate.setDate(nowcheckindate.getDate() + nownights);
1530 jQuery('#checkoutdate').datepicker( 'option', 'minDate', nowcheckindate );
1531 jQuery('#checkoutdate').datepicker( 'setDate', nowcheckindate );
1532 vbCalcNights();
1533 }
1534 });
1535 jQuery('#checkoutdate').datepicker({
1536 showOn: 'focus',
1537 dateFormat: 'yy-mm-dd',
1538 minDate: '0d',
1539 numberOfMonths: 2,
1540 onSelect: function(selectedDate) {
1541 if (!jQuery('#checkoutdate').val().length) {
1542 return;
1543 }
1544 vbCalcNights();
1545 }
1546 });
1547 jQuery('#checkindate-trig, #checkoutdate-trig').click(function() {
1548 var jdp = jQuery(this).prev('input.hasDatepicker');
1549 if (jdp.length) {
1550 jdp.focus();
1551 }
1552 });
1553 jQuery('#vbo-numnights').on('change keyup', function() {
1554 if (!jQuery('#checkindate').length) {
1555 return;
1556 }
1557 var nownights = parseInt(jQuery(this).val());
1558 var nowcheckin = jQuery('#checkindate').datepicker('getDate');
1559 var nowcheckindate = new Date(nowcheckin.getTime());
1560 nowcheckindate.setDate(nowcheckindate.getDate() + nownights);
1561 jQuery('#checkoutdate').datepicker( 'option', 'minDate', nowcheckindate );
1562 jQuery('#checkoutdate').datepicker( 'setDate', nowcheckindate );
1563 });
1564 jQuery('.vbo-period-from').datepicker({
1565 dateFormat: '<?php echo $juidf; ?>',
1566 minDate: '0d',
1567 altField: '.vbo-period-from-val',
1568 onSelect: function(selectedDate) {
1569 jQuery(this).parent().find('.vbo-period-from-val').val(selectedDate);
1570 jQuery(this).closest('.vbo-ratesoverview-period-box-cals').find('.vbo-period-to').datepicker("option", "minDate", selectedDate);
1571 vboFormatCalDate(jQuery(this), 'period-from');
1572 }
1573 });
1574 jQuery('.vbo-period-to').datepicker({
1575 dateFormat: '<?php echo $juidf; ?>',
1576 minDate: '0d',
1577 altField: '.vbo-period-to-val',
1578 onSelect: function(selectedDate) {
1579 jQuery(this).parent().find('.vbo-period-to-val').val(selectedDate);
1580 jQuery(this).closest('.vbo-ratesoverview-period-box-cals').find('.vbo-period-from').datepicker("option", "maxDate", selectedDate);
1581 vboFormatCalDate(jQuery(this), 'period-to');
1582 }
1583 });
1584 jQuery('.vbo-ratesoverview-period-box-left, .vbo-ratesoverview-period-box-right').click(function() {
1585 jQuery(this).closest('.vbo-ratesoverview-period-boxes').find('.vbo-ratesoverview-period-box-cals').fadeToggle();
1586 });
1587 jQuery("#roomsel").select2({
1588 allowClear: true
1589 });
1590 jQuery("#roomselcalc").select2();
1591 });
1592 <?php
1593 if ($df == "Y/m/d") {
1594 ?>
1595 Date.prototype.format = "yy/mm/dd";
1596 <?php
1597 } elseif ($df == "m/d/Y") {
1598 ?>
1599 Date.prototype.format = "mm/dd/yy";
1600 <?php
1601 } else {
1602 ?>
1603 Date.prototype.format = "dd/mm/yy";
1604 <?php
1605 }
1606 ?>
1607 Date.prototype.datesep = "<?php echo addslashes(VikBooking::getDateSeparator()); ?>";
1608 var currencysymb = '<?php echo $currencysymb; ?>';
1609 var debug_mode = '<?php echo $pdebug; ?>';
1610 var vcm_exists = <?php echo VikBooking::vcmAutoUpdate(); ?>;
1611 var roverw_messages = {
1612 "setNewRatesMissing": "<?php echo addslashes(JText::translate('VBRATESOVWERRNEWRATE')); ?>",
1613 "modRplansMissing": "<?php echo addslashes(JText::translate('VBRATESOVWERRMODRPLANS')); ?>",
1614 "openSpLink": "<?php echo addslashes(JText::translate('VBRATESOVWOPENSPL')); ?>",
1615 "vcmRatesChanged": "<?php echo addslashes(JText::translate('VBRATESOVWVCMRCHANGED')); ?>",
1616 "vcmRatesChangedOpen": "<?php echo addslashes(JText::translate('VBRATESOVWVCMRCHANGEDOPEN')); ?>",
1617 "notesForRoom": "<?php echo addslashes(JText::translate('VBONOTESFORROOM')); ?>"
1618 };
1619 var vboFests = <?php echo json_encode($this->festivities); ?>;
1620 var vboRdayNotes = <?php echo json_encode($this->rdaynotes); ?>;
1621 </script>
1622
1623 <script type="text/Javascript">
1624 /**
1625 * Open a booking in a new tab.
1626 */
1627 function vboRovervOpenBooking(bid) {
1628 var open_url = jQuery('.vbo-basenavuri-details').attr('href');
1629 open_url = open_url.replace('%d', bid);
1630 // navigate in a new tab
1631 window.open(open_url, '_blank');
1632 }
1633
1634 /* Labels for months and weekdays */
1635 var vbowdays = <?php echo json_encode($long_days_labels); ?>;
1636 var vbomonths = <?php echo json_encode($long_months_labels); ?>;
1637 /* Dates selection - Start */
1638 var vbolistener = null;
1639
1640 jQuery(function() {
1641
1642 // fests
1643 jQuery(document.body).on("click", "td.bluedays", function() {
1644 if (jQuery(this).hasClass('skip-bluedays-click')) {
1645 return;
1646 }
1647 var ymd = jQuery(this).attr('data-ymd');
1648 var daytitle = jQuery(this).attr('data-readymd');
1649 if (jQuery(this).hasClass('vbo-roverv-festcell')) {
1650 // cell has fests
1651 if (!vboFests.hasOwnProperty(ymd)) {
1652 return;
1653 }
1654 vboRenderFests(ymd, daytitle);
1655 } else {
1656 // let the admin create a new fest
1657
1658 // define the modal buttons
1659 let cancel_btn = jQuery('<button></button>')
1660 .attr('type', 'button')
1661 .addClass('btn')
1662 .text(Joomla.JText._('VBANNULLA'))
1663 .on('click', function() {
1664 VBOCore.emitEvent('vbo-dismiss-modal-roverv-fests');
1665 });
1666
1667 let save_btn = jQuery('<button></button>')
1668 .attr('type', 'button')
1669 .addClass('btn btn-success')
1670 .text(Joomla.JText._('VBSAVE'))
1671 .on('click', function() {
1672 vboAddFest();
1673 });
1674
1675 let modal_body = VBOCore.displayModal({
1676 suffix: 'ratesoverv-fests',
1677 extra_class: 'vbo-modal-rounded vbo-modal-tall',
1678 title: '<?php VikBookingIcons::e('star'); ?> ' + daytitle,
1679 footer_left: cancel_btn,
1680 footer_right: save_btn,
1681 dismiss_event: 'vbo-dismiss-modal-roverv-fests',
1682 onDismiss: () => {
1683 jQuery('.vbo-ratesoverview-fests-wrap').appendTo('.vbo-ratesoverview-fests-helper');
1684 },
1685 });
1686
1687 // update ymd key for the selected date, useful for adding new fests
1688 jQuery('.vbo-overlay-fests-addnew').attr('data-ymd', ymd);
1689
1690 // unset content and display modal for just adding a new fest
1691 jQuery('.vbo-overlay-fests-list').html('');
1692 jQuery('.vbo-ratesoverview-fests-wrap').appendTo(modal_body);
1693 }
1694 if (jQuery(this).hasClass('vbo-roverw-roomdaynote-full')) {
1695 // display the room-day notes
1696 vboRenderRdayNotes(ymd, jQuery(this).attr('data-rid'));
1697 }
1698 });
1699
1700 // calendar-day selection listener
1701 vbolistener = new CalendarListener();
1702
1703 jQuery('.day-block').click(function() {
1704 pickBlock(jQuery(this).attr('id'));
1705 });
1706
1707 jQuery('.day-block').hover(
1708 function() {
1709 if (vbolistener.isFirstPicked() && !vbolistener.isLastPicked()) {
1710 var struct = initBlockStructure(jQuery(this).attr('id'));
1711 var all_blocks = getAllBlocksBetween(vbolistener.first, struct, false);
1712 if (all_blocks !== false) {
1713 jQuery.each(all_blocks, function(k, v) {
1714 if (!v.hasClass('block-picked-middle')) {
1715 v.addClass('block-picked-middle');
1716 }
1717 });
1718 jQuery(this).addClass('block-picked-end');
1719 }
1720 }
1721 },
1722 function() {
1723 if (!vbolistener.isLastPicked()) {
1724 jQuery('.day-block').removeClass('block-picked-middle block-picked-end');
1725 }
1726 }
1727 );
1728
1729 jQuery("body").on("click", ".vbo-roverw-daymod-infospids", function() {
1730 if (jQuery(this).hasClass('vbo-roverw-daymod-infospids-on')) {
1731 // dismiss modal
1732 VBOCore.emitEvent('vbo-ratesoverview-spids-details-dismiss');
1733 } else {
1734 // show modal
1735 let spids_modal = VBOCore.displayModal({
1736 suffix: 'vbo-ratesoverview-spids',
1737 extra_class: 'vbo-modal-rounded vbo-modal-dialog vbo-modal-nofooter',
1738 title: jQuery(this).attr('data-dt'),
1739 dismiss_event: 'vbo-ratesoverview-spids-details-dismiss',
1740 });
1741
1742 let spids_info = jQuery(this).next('.vbo-roverw-daymod-infospids-outcont').find('.vbo-roverw-daymod-infospids-incont').clone();
1743 spids_info.appendTo(spids_modal);
1744 }
1745 });
1746
1747 jQuery('.vbo-roverw-closeopenrp h4').click(function() {
1748 jQuery('.vbo-roverw-closeopenrp-btns').fadeToggle(400, function() {
1749 if (jQuery(this).is(':visible')) {
1750 jQuery('.vbo-roverw-closeopenrp-arrow').html('<?php VikBookingIcons::e('chevron-up'); ?>');
1751 } else {
1752 jQuery('.vbo-roverw-closeopenrp-arrow').html('<?php VikBookingIcons::e('chevron-down'); ?>');
1753 }
1754 });
1755 });
1756
1757 jQuery(document.body).on('click', '.vbo-ratesoverview-vcmwarn-close', function() {
1758 vcm_exists = 0;
1759 jQuery('.vbo-ratesoverview-right-inner').hide().html('');
1760 });
1761
1762 // show booking details
1763 jQuery('.vbo-roverw-daybusy[data-bids]').click(function() {
1764 var date_bids = jQuery(this).attr('data-bids');
1765 if (!date_bids || !date_bids.length) {
1766 return;
1767 }
1768
1769 // get room name, date and day-bids
1770 var rid = jQuery(this).closest('table').attr('data-idroom');
1771 var room_name = jQuery('.vbo-ratesoverview-roomname[data-idroom="' + rid + '"]').text();
1772 var date_read = jQuery(this).attr('data-vbodateread');
1773 var def_bicon = '<?php VikBookingIcons::e('user', 'vbo-dashboard-guest-activity-avatar-icon'); ?>';
1774 var closure_i = '<?php VikBookingIcons::e('ban', 'vbo-dashboard-guest-activity-avatar-icon'); ?>';
1775
1776 // display modal with booking details
1777 var rday_bookings_modal_body = VBOCore.displayModal({
1778 suffix: 'overv-rdaybookings',
1779 extra_class: 'vbo-modal-rounded vbo-modal-tall',
1780 title: room_name + ' - ' + date_read,
1781 dismiss_event: 'vbo-dismiss-modal-roverv-rdaybookings',
1782 loading_event: 'vbo-loading-modal-roverv-rdaybookings',
1783 loading_body: '<?php VikBookingIcons::e('circle-notch', 'fa-spin fa-fw'); ?>',
1784 });
1785
1786 // show loading
1787 VBOCore.emitMultitaskEvent('vbo-loading-modal-roverv-rdaybookings');
1788
1789 // make the request to get the bookings information
1790 VBOCore.doAjax(
1791 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=getbookingsinfo'); ?>",
1792 {
1793 status: 'any',
1794 idorders: date_bids,
1795 tmpl: 'component'
1796 },
1797 (res) => {
1798 // stop loading
1799 VBOCore.emitMultitaskEvent('vbo-loading-modal-roverv-rdaybookings');
1800 try {
1801 var obj_res = typeof res === 'string' ? JSON.parse(res) : res;
1802 // build the HTML response
1803 var rday_bookings_html = '';
1804 rday_bookings_html += '<div class="vbo-dashboard-guests-latest">' + "\n";
1805 rday_bookings_html += ' <div class="vbo-dashboard-guest-messages-list">' + "\n";
1806 for (var b in obj_res) {
1807 if (!obj_res.hasOwnProperty(b)) {
1808 continue;
1809 }
1810 // nights and guests
1811 var nights_guests = [
1812 obj_res[b]['roomsnum'] + ' ' + Joomla.JText._((obj_res[b]['roomsnum'] > 1 ? 'VBPVIEWORDERSTHREE' : 'VBEDITORDERTHREE')),
1813 obj_res[b]['days'] + ' ' + Joomla.JText._((obj_res[b]['days'] > 1 ? 'VBDAYS' : 'VBDAY')),
1814 obj_res[b]['tot_adults'] + ' ' + Joomla.JText._((obj_res[b]['tot_adults'] > 1 ? 'VBMAILADULTS' : 'VBMAILADULT'))
1815 ];
1816 if (obj_res[b]['tot_children'] > 0) {
1817 nights_guests.push(obj_res[b]['tot_children'] + ' ' + Joomla.JText._((obj_res[b]['tot_children'] > 1 ? 'VBMAILCHILDREN' : 'VBMAILCHILD')));
1818 }
1819
1820 // build booking structure
1821 rday_bookings_html += ' <div class="vbo-dashboard-guest-activity vbo-w-guestmessages-message" data-idorder="' + obj_res[b]['id'] + '" onclick="vboRovervOpenBooking(\'' + obj_res[b]['id'] + '\');">' + "\n";
1822 rday_bookings_html += ' <div class="vbo-dashboard-guest-activity-avatar">' + "\n";
1823 if (obj_res[b]['avatar_src']) {
1824 rday_bookings_html += '<img class="vbo-dashboard-guest-activity-avatar-profile" src="' + obj_res[b]['avatar_src'] + '" alt="' + obj_res[b]['avatar_alt'] + '" />' + "\n";
1825 } else if (obj_res[b]['closure']) {
1826 rday_bookings_html += closure_i + "\n";
1827 } else {
1828 rday_bookings_html += def_bicon + "\n";
1829 }
1830 rday_bookings_html += ' </div>' + "\n";
1831 rday_bookings_html += ' <div class="vbo-dashboard-guest-activity-content">' + "\n";
1832 rday_bookings_html += ' <div class="vbo-dashboard-guest-activity-content-head">' + "\n";
1833 rday_bookings_html += ' <div class="vbo-dashboard-guest-activity-content-info-details">' + "\n";
1834 rday_bookings_html += ' <h4 class="vbo-w-guestmessages-message-gtitle">' + (!obj_res[b]['closure'] ? obj_res[b]['cinfo'] : obj_res[b]['closure_txt']) + '</h4>' + "\n";
1835 rday_bookings_html += ' <div class="vbo-dashboard-guest-activity-content-info-icon">' + "\n";
1836 rday_bookings_html += ' <span class="badge badge-info">' + obj_res[b]['id'] + '</span> ' + "\n";
1837 rday_bookings_html += ' <span class="badge badge-' + (obj_res[b]['status'] == 'confirmed' ? 'success' : 'warning') + '">' + obj_res[b]['status_lbl'] + '</span>' + "\n";
1838 rday_bookings_html += ' <span class="vbo-w-guestmessages-message-staydates">' + "\n";
1839 rday_bookings_html += ' <?php VikBookingIcons::e('calendar-alt'); ?>' + "\n";
1840 rday_bookings_html += ' <span class="vbo-w-guestmessages-message-staydates-in">' + obj_res[b]['checkin_short'] + '</span>' + "\n";
1841 rday_bookings_html += ' <span class="vbo-w-guestmessages-message-staydates-sep">-</span>' + "\n";
1842 rday_bookings_html += ' <span class="vbo-w-guestmessages-message-staydates-out">' + obj_res[b]['checkout_short'] + '</span>' + "\n";
1843 rday_bookings_html += ' </span>' + "\n";
1844 rday_bookings_html += ' </div>' + "\n";
1845 rday_bookings_html += ' </div>' + "\n";
1846 rday_bookings_html += ' <div class="vbo-dashboard-guest-activity-content-info-date">' + "\n";
1847 rday_bookings_html += ' <span>' + obj_res[b]['book_date'] + '</span>' + "\n";
1848 rday_bookings_html += ' <span>' + obj_res[b]['book_time'] + '</span>' + "\n";
1849 rday_bookings_html += ' </div>' + "\n";
1850 rday_bookings_html += ' </div>' + "\n";
1851 rday_bookings_html += ' <div class="vbo-dashboard-guest-activity-content-info-msg">' + "\n";
1852 rday_bookings_html += ' <div><?php VikBookingIcons::e('bed'); ?> ' + nights_guests.join(', ') + '</div>' + "\n";
1853 if (obj_res[b].hasOwnProperty('sub_units_data')) {
1854 rday_bookings_html += ' <div class="vbo-rdaybooking-subunits">' + "\n";
1855 for (let sub_rname in obj_res[b]['sub_units_data']) {
1856 if (!obj_res[b]['sub_units_data'].hasOwnProperty(sub_rname)) {
1857 continue;
1858 }
1859 rday_bookings_html += '<span class="label label-success">' + (obj_res[b]['roomsnum'] > 1 ? sub_rname + ' ' : '') + '#' + obj_res[b]['sub_units_data'][sub_rname] + '</span>' + "\n";
1860 }
1861 rday_bookings_html += ' </div>' + "\n";
1862 }
1863 rday_bookings_html += ' </div>' + "\n";
1864 rday_bookings_html += ' </div>' + "\n";
1865 rday_bookings_html += ' </div>' + "\n";
1866 }
1867 rday_bookings_html += ' </div>' + "\n";
1868 rday_bookings_html += '</div>' + "\n";
1869
1870 // append the response
1871 rday_bookings_modal_body.html(rday_bookings_html);
1872 } catch(err) {
1873 console.error('could not parse JSON response', err, res);
1874 alert('Could not parse JSON response');
1875 }
1876 },
1877 (err) => {
1878 // stop loading and display alert message
1879 VBOCore.emitMultitaskEvent('vbo-loading-modal-roverv-rdaybookings');
1880 console.error(err);
1881 alert(err.responseText);
1882 }
1883 );
1884 });
1885
1886 // register listener for the "input" event on the "set new rate" input field of type number
1887 document.querySelector('#roverw-newrate').addEventListener('input', VBOCore.debounceEvent((e) => {
1888 // dispatch the event to calculate the new OTA pricing value
1889 VBOCore.emitEvent('vbo-roverv-setnewrate-calc-ota-pricing', {rate: e.target.value});
1890 }, 200));
1891
1892 // register listener for when a new rate is set to update what will be the OTA pricing value
1893 document.addEventListener('vbo-roverv-setnewrate-calc-ota-pricing', VBOCore.debounceEvent((e) => {
1894 if (!e || !e.detail || !e.detail.rate) {
1895 // invalid event data
1896 return;
1897 }
1898
1899 // get the new PMS rate
1900 let rate_amount = parseFloat(e.detail.rate);
1901
1902 // access the currency object
1903 let currencyObj = VBOCore.getCurrency({
1904 symbol: vbo_currency_symbol,
1905 digits: vbo_currency_digits,
1906 decimals: vbo_currency_decimals,
1907 thousands: vbo_currency_thousands,
1908 noDecimals: 1,
1909 });
1910
1911 // scan all OTA alteration rules, if any
1912 document.querySelectorAll('.vbo-roverw-setnewrate-ota-pricing-currentvalue[data-alteration]').forEach((elem) => {
1913 // channel alteration string
1914 let alter_string = elem.getAttribute('data-alteration');
1915 if (!alter_string) {
1916 alter_string = '+0%';
1917 }
1918
1919 // default alteration factors (no pricing alteration rules)
1920 let alter_op = alter_string.substr(0, 1);
1921 let alter_val = alter_string.substr(-1, 1);
1922 let alter_amount = parseFloat(alter_string.replace(alter_op, '').replace(alter_val, ''));
1923
1924 // calculate what the rate will be on the OTA
1925 let ota_rate_amount = rate_amount;
1926
1927 if (!isNaN(alter_amount) && Math.abs(alter_amount) > 0) {
1928 if (alter_op == '+') {
1929 // increase rate
1930 if (alter_val == '%') {
1931 // percent
1932 let amount_inc = currencyObj.multiply(alter_amount, 0.01);
1933 amount_inc = currencyObj.multiply(rate_amount, amount_inc);
1934 ota_rate_amount = currencyObj.sum(rate_amount, amount_inc);
1935 } else {
1936 // absolute
1937 ota_rate_amount = currencyObj.sum(rate_amount, alter_amount);
1938 }
1939 } else {
1940 // discount rate
1941 if (alter_val == '%') {
1942 // percent
1943 let amount_inc = currencyObj.multiply(alter_amount, 0.01);
1944 amount_inc = currencyObj.multiply(rate_amount, amount_inc);
1945 ota_rate_amount = currencyObj.diff(rate_amount, amount_inc);
1946 } else {
1947 // absolute
1948 ota_rate_amount = currencyObj.diff(rate_amount, alter_amount);
1949 }
1950 }
1951 }
1952
1953 // get the element containing the calculated ota pricing
1954 let will_alter_elem = elem
1955 .closest('.vbo-roverw-setnewrate-vcm-ota-pricing-startvalue')
1956 .querySelector('.vbo-roverw-setnewrate-ota-pricing-willvalue');
1957
1958 // define the currency options
1959 let ota_currency_options = {};
1960
1961 // check if the channel requires a specific currency
1962 let ota_currency_data = will_alter_elem.getAttribute('data-currency');
1963 if (ota_currency_data) {
1964 // decode currency data instructions
1965 try {
1966 ota_currency_data = JSON.parse(ota_currency_data);
1967 } catch (e) {
1968 ota_currency_data = {};
1969 }
1970
1971 // set custom currency options
1972 if (ota_currency_data['symbol']) {
1973 ota_currency_options['symbol'] = ota_currency_data['symbol'];
1974 }
1975 if (ota_currency_data['decimals']) {
1976 ota_currency_options['digits'] = ota_currency_data['decimals'];
1977 }
1978 if (ota_currency_data['decimals_sep']) {
1979 ota_currency_options['decimals'] = ota_currency_data['decimals_sep'];
1980 }
1981 if (ota_currency_data['thousands_sep']) {
1982 ota_currency_options['thousands'] = ota_currency_data['thousands_sep'];
1983 }
1984 }
1985
1986 // set calculated OTA rate value
1987 will_alter_elem.innerHTML = currencyObj.format(ota_rate_amount, ota_currency_options);
1988 });
1989 }, 200));
1990
1991 });
1992
1993 /**
1994 * Room-day notes
1995 */
1996 function vboRenderRdayNotes(day, rid) {
1997 // compose room-day notes information (no sub-units)
1998 var notes_html = '';
1999 var keyid = day + '_' + rid + '_0';
2000 if (vboRdayNotes.hasOwnProperty(keyid) && vboRdayNotes[keyid]['info'] && vboRdayNotes[keyid]['info'].length) {
2001 for (var i = 0; i < vboRdayNotes[keyid]['info'].length; i++) {
2002 var note_data = vboRdayNotes[keyid]['info'][i];
2003 notes_html += '<div class="vbo-overlay-fest-details vbo-roverv-roomdaynotes-note-details">';
2004 notes_html += ' <div class="vbo-fest-info vbo-roverv-roomdaynotes-note-info">';
2005 notes_html += ' <div class="vbo-fest-name vbo-roverv-roomdaynotes-note-name">' + note_data['name'] + '</div>';
2006 notes_html += ' <div class="vbo-fest-desc vbo-roverv-roomdaynotes-note-desc">' + note_data['descr'].replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '<br />' + '$2') + '</div>';
2007 notes_html += ' </div>';
2008 notes_html += '</div>';
2009 }
2010 }
2011 // append room-day notes to the list of festivities
2012 if (notes_html.length) {
2013 notes_html = '<h4 class="vbo-roverw-roomdaynotes-title">' + roverw_messages.notesForRoom + '</h4>' + notes_html;
2014 jQuery('.vbo-overlay-fests-list').append(notes_html);
2015 }
2016 }
2017
2018 /**
2019 * Fests
2020 */
2021 function vboRenderFests(day, daytitle) {
2022 // compose fests information
2023 let fests_html = '';
2024 if (vboFests[day] && vboFests[day]['festinfo'] && vboFests[day]['festinfo'].length) {
2025 for (var i = 0; i < vboFests[day]['festinfo'].length; i++) {
2026 let fest = vboFests[day]['festinfo'][i];
2027 fests_html += '<div class="vbo-overlay-fest-details">';
2028 fests_html += ' <div class="vbo-fest-info">';
2029 fests_html += ' <div class="vbo-fest-name">' + fest['trans_name'] + '</div>';
2030 fests_html += ' <div class="vbo-fest-desc">' + fest['descr'].replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + '<br />' + '$2') + '</div>';
2031 fests_html += ' </div>';
2032 fests_html += ' <div class="vbo-fest-cmds">';
2033 fests_html += ' <button type="button" class="btn btn-danger" onclick="vboRemoveFest(\'' + day + '\', \'' + i + '\', \'' + fest['type'] + '\', this);"><?php VikBookingIcons::e('trash-alt', 'icn-nomargin'); ?></button>';
2034 fests_html += ' </div>';
2035 fests_html += '</div>';
2036 }
2037 }
2038
2039 // update ymd key for the selected date, useful for adding new fests
2040 jQuery('.vbo-overlay-fests-addnew').attr('data-ymd', day);
2041
2042 // set content
2043 jQuery('.vbo-overlay-fests-list').html(fests_html);
2044
2045 if (daytitle) {
2046 // define the modal buttons
2047 let cancel_btn = jQuery('<button></button>')
2048 .attr('type', 'button')
2049 .addClass('btn')
2050 .text(Joomla.JText._('VBANNULLA'))
2051 .on('click', function() {
2052 VBOCore.emitEvent('vbo-dismiss-modal-roverv-fests');
2053 });
2054
2055 let save_btn = jQuery('<button></button>')
2056 .attr('type', 'button')
2057 .addClass('btn btn-success')
2058 .text(Joomla.JText._('VBSAVE'))
2059 .on('click', function() {
2060 vboAddFest();
2061 });
2062
2063 let modal_body = VBOCore.displayModal({
2064 suffix: 'ratesoverv-fests',
2065 extra_class: 'vbo-modal-rounded vbo-modal-tall',
2066 title: '<?php VikBookingIcons::e('star'); ?> ' + daytitle,
2067 footer_left: cancel_btn,
2068 footer_right: save_btn,
2069 dismiss_event: 'vbo-dismiss-modal-roverv-fests',
2070 onDismiss: () => {
2071 jQuery('.vbo-ratesoverview-fests-wrap').appendTo('.vbo-ratesoverview-fests-helper');
2072 },
2073 });
2074
2075 jQuery('.vbo-ratesoverview-fests-wrap').appendTo(modal_body);
2076 }
2077 }
2078
2079 function vboRemoveFest(day, index, fest_type, that) {
2080 if (!confirm('<?php echo addslashes(JText::translate('VBDELCONFIRM')); ?>')) {
2081 return false;
2082 }
2083 var elem = jQuery(that);
2084 // make the AJAX request to the controller to remove this fest from the DB
2085 VBOCore.doAjax(
2086 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=remove_fest'); ?>",
2087 {
2088 tmpl: "component",
2089 dt: day,
2090 ind: index,
2091 type: fest_type
2092 },
2093 (res) => {
2094 if (res.indexOf('e4j.ok') >= 0) {
2095 // delete fest also from the json-decode array of objects
2096 if (vboFests[day] && vboFests[day]['festinfo']) {
2097 // use splice to remove the desired index from array, or delete would not make the length of the array change
2098 vboFests[day]['festinfo'].splice(index, 1);
2099 // re-build indexes of delete buttons, fundamental for removing the right index at next click
2100 vboRenderFests(day);
2101 if (!vboFests[day]['festinfo'].length) {
2102 // delete also this date object from fests
2103 delete vboFests[day];
2104 // no more fests, remove the class for this date from all cells
2105 jQuery('td.bluedays[data-ymd="'+day+'"]').removeClass('vbo-roverv-festcell');
2106 }
2107 }
2108 elem.closest('.vbo-overlay-fest-details').remove();
2109 } else {
2110 console.log(res);
2111 alert('Invalid response');
2112 }
2113 },
2114 (err) => {
2115 alert('Request failed');
2116 }
2117 );
2118 }
2119
2120 function vboAddFest() {
2121 var ymd = jQuery('.vbo-overlay-fests-addnew').attr('data-ymd');
2122 var fest_name = jQuery('#vbo-newfest-name').val();
2123 var fest_descr = jQuery('#vbo-newfest-descr').val();
2124 if (!fest_name.length) {
2125 alert(Joomla.JText._('VBO_PLEASE_FILL_FIELDS'));
2126 return false;
2127 }
2128
2129 // make the AJAX request to the controller to add the festivity to the DB
2130 VBOCore.doAjax(
2131 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=add_fest'); ?>",
2132 {
2133 tmpl: "component",
2134 dt: ymd,
2135 type: "custom",
2136 name: fest_name,
2137 descr: fest_descr
2138 },
2139 (res) => {
2140 // parse the JSON response that contains the fest object for the passed date
2141 try {
2142 var stored_fest = JSON.parse(res);
2143 if (!vboFests.hasOwnProperty(stored_fest['dt'])) {
2144 // we need to add the proper class to all cells to show that there is a fest
2145 jQuery('td.bluedays[data-ymd="'+stored_fest['dt']+'"]').addClass('vbo-roverv-festcell');
2146 }
2147 vboFests[stored_fest['dt']] = stored_fest;
2148 hideVboDialogFests();
2149 // reset input fields
2150 jQuery('#vbo-newfest-name').val('');
2151 jQuery('#vbo-newfest-descr').val('');
2152 } catch (e) {
2153 console.log(res);
2154 alert('Invalid response');
2155 return false;
2156 }
2157 },
2158 (err) => {
2159 alert('Request failed');
2160 }
2161 );
2162 }
2163
2164 /**
2165 * Fests dialog
2166 */
2167 function hideVboDialogFests() {
2168 VBOCore.emitEvent('vbo-dismiss-modal-roverv-fests');
2169 }
2170
2171 function checkInvokeVcm() {
2172 // trigger restriction and related checks
2173 vboCheckVcmRestrictions();
2174
2175 if (!vbolistener || !vbolistener.first || !vbolistener.first.rplan) {
2176 return;
2177 }
2178 var rplanid = vbolistener.first.rplan;
2179 var idroom = vbolistener.first.idroom;
2180 var curval = document.querySelector('input[name="roverw-newrate-vcm"]').value;
2181 if (parseInt(curval) < 0) {
2182 return;
2183 }
2184 var buiscuits = document.cookie;
2185 if (!buiscuits.length) {
2186 return;
2187 }
2188 var vcmmatch = "vboVcmRov"+idroom+rplanid+"=";
2189 if (buiscuits.indexOf(vcmmatch) >= 0) {
2190 // last cookie does not terminate with ; so just use 0 to compare
2191 vcmmatch += "0";
2192 if (buiscuits.indexOf(vcmmatch) >= 0) {
2193 jQuery('input[name="roverw-newrate-vcm"]').prop('checked', false);
2194 } else {
2195 jQuery('input[name="roverw-newrate-vcm"]').prop('checked', true);
2196 }
2197
2198 // re-trigger elements update
2199 vboCheckVcmRestrictions();
2200 }
2201 }
2202
2203 /**
2204 * Restrictions can be updated only if VCM is available and toggled ON, because
2205 * the creation and transmission is made through the Connector Class of VCM.
2206 * On top of that, the OTA pricing alteration rule overrides will toggle a status class.
2207 *
2208 * @since 1.11
2209 */
2210 function vboCheckVcmRestrictions() {
2211 if (!jQuery('input[name="roverw-newrate-vcm"]').prop('disabled') && jQuery('input[name="roverw-newrate-vcm"]').prop('checked')) {
2212 jQuery('#roverw-newrestr').val('');
2213 jQuery('.vbo-roverw-newrestr-wrap').show();
2214 jQuery('.vbo-roverw-setnewrate-vcm-ota-relation').removeClass('vbo-roverw-setnewrate-vcm-ota-relation-disabled');
2215 } else {
2216 jQuery('.vbo-roverw-newrestr-wrap').hide();
2217 jQuery('.vbo-roverw-setnewrate-vcm-ota-relation').addClass('vbo-roverw-setnewrate-vcm-ota-relation-disabled');
2218 }
2219 }
2220
2221 function showVboDialog() {
2222 var format = new Date().format;
2223 format = format.replace(new RegExp("/", 'g'), new Date().datesep);
2224 jQuery("#rovervw-roomname").html('<?php VikBookingIcons::e('building'); ?> ' + vbolistener.first.roomName);
2225 jQuery("#rovervw-rplan").html(vbolistener.first.rplanName);
2226 jQuery("#rovervw-closeopen-rplan").html('"'+vbolistener.first.rplanName+'"');
2227 jQuery("#rovervw-fromdate").html(vbolistener.first.toDate(format));
2228 jQuery("#rovervw-todate").html(vbolistener.last.toDate(format));
2229 jQuery(".vbo-roverw-alldays-inner").html("");
2230 var all_blocks = getAllBlocksBetween(vbolistener.first, vbolistener.last, true);
2231 if (all_blocks !== false) {
2232 var newdayscont = '';
2233 var highestrate = vbolistener.first.defRate;
2234 var allblocksclosed = true;
2235 jQuery.each(all_blocks, function(k, v) {
2236 let date_read = jQuery(v).attr('data-vbodateread');
2237 if (!v.hasClass('vbo-roverw-rplan-off')) {
2238 allblocksclosed = false;
2239 }
2240 var spids = jQuery(v).attr("data-vbospids").split("-");
2241 var spids_det = '';
2242 if (jQuery(v).attr("data-vbospids").length > 0 && spids.length > 0) {
2243 spids_det += "<div class=\"vbo-roverw-daymod-infospids\" data-dt=\"" + date_read + "\"><span><i class=\"<?php echo VikBookingIcons::i('info-circle'); ?>\"></i></span></div>";
2244 spids_det += "<div class=\"vbo-roverw-daymod-infospids-outcont\">";
2245 spids_det += "<div class=\"vbo-roverw-daymod-infospids-incont\"><ul>";
2246 for (var x = 0; x < spids.length; x++) {
2247 spids_det += "<li><a target=\"_blank\" href=\"<?php echo VBOPlatformDetection::isWordPress() ? 'admin.php' : 'index.php'; ?>?option=com_vikbooking&task=editseason&cid[]="+spids[x]+"\">"+roverw_messages.openSpLink.replace("%d", spids[x])+"</a></li>";
2248 }
2249 spids_det += "</ul></div></div>";
2250 }
2251 var dayrate = parseFloat(v.find('.vbo-rplan-price').text());
2252 if (!isNaN(dayrate) && dayrate > highestrate) {
2253 highestrate = dayrate;
2254 }
2255
2256 let date_parts = date_read.split(',');
2257 let wday = date_parts[0].trim();
2258 let mon = date_parts[1].replace(/[^a-z]/ig, '');
2259 let mday = date_parts[1].replace(/[^0-9]/g, '');
2260
2261 newdayscont += "<div class=\"vbo-roverw-daymod\">";
2262 newdayscont += " <div class=\"vbo-roverw-daymod-inner\">";
2263 newdayscont += " <div class=\"vbo-roverw-daymod-innercell\">";
2264 newdayscont += " <span class=\"vbo-roverw-daydate\">";
2265 newdayscont += " <span class=\"vbo-roverw-daydate-wdaymday\">";
2266 newdayscont += " <span class=\"vbo-roverw-daydate-wday\">" + wday + "</span>";
2267 newdayscont += " <span class=\"vbo-roverw-daydate-mday\">" + mday + "</span>";
2268 newdayscont += " </span>";
2269 newdayscont += " <span class=\"vbo-roverw-daydate-month\">" + mon + "</span>";
2270 newdayscont += " </span>";
2271 newdayscont += " <span class=\"vbo-roverw-dayprice\">" + v.html() + "</span>" + spids_det;
2272 newdayscont += " </div>";
2273 newdayscont += " </div>";
2274 newdayscont += "</div>";
2275 });
2276 jQuery(".vbo-roverw-alldays-inner").html(newdayscont);
2277 jQuery("#roverw-newrate").attr("placeholder", vbolistener.first.defRate).val(highestrate);
2278 // if all selected blocks have a closed rate plan, show it in red in the name of the rate plan because this will be triggered to VCM if "Apply" is clicked
2279 if (allblocksclosed) {
2280 jQuery("#rovervw-rplan").html('<span style="color: #f00"><i class="<?php echo VikBookingIcons::i('ban'); ?>"></i> '+vbolistener.first.rplanName+'</span>');
2281 }
2282 }
2283
2284 // populate OTA relations
2285 vboRatesOvervSetRoomOtaRelations(vbolistener.first.idroom, vbolistener.first.rplan);
2286
2287 // VCM check
2288 checkInvokeVcm();
2289
2290 // define the modal buttons
2291 let cancel_btn = jQuery('<button></button>')
2292 .attr('type', 'button')
2293 .addClass('btn')
2294 .text(Joomla.JText._('VBANNULLA'))
2295 .on('click', function() {
2296 VBOCore.emitEvent('vbo-dismiss-modal-roverv-newratesrestr');
2297 });
2298
2299 let save_btn = jQuery('<button></button>')
2300 .attr('type', 'button')
2301 .addClass('btn btn-success')
2302 .html('<?php VikBookingIcons::e('check-circle'); ?>' + Joomla.JText._('VBAPPLY'))
2303 .on('click', function() {
2304 setNewRates();
2305 });
2306
2307 // display modal
2308 let modal_body = VBOCore.displayModal({
2309 suffix: 'ratesoverv-newratesrestr',
2310 extra_class: 'vbo-modal-rounded vbo-modal-tall',
2311 title: Joomla.JText._('VBO_RATES_AND_RESTR'),
2312 lock_scroll: true,
2313 draggable: true,
2314 footer_left: cancel_btn,
2315 footer_right: save_btn,
2316 dismiss_event: 'vbo-dismiss-modal-roverv-newratesrestr',
2317 loading_event: 'vbo-loading-modal-roverv-newratesrestr',
2318 loading_body: '<?php VikBookingIcons::e('refresh', 'fa-spin fa-3x fa-fw'); ?>',
2319 onDismiss: () => {
2320 // cancel dates selection
2321 vbolistener.clear();
2322 jQuery('.day-block').removeClass('block-picked-start block-picked-middle block-picked-end');
2323
2324 // reset period selection
2325 jQuery('.vbo-ratesoverview-period-from').find('span').text('');
2326 jQuery('.vbo-ratesoverview-period-from-icon').show();
2327 jQuery('.vbo-ratesoverview-period-to').find('span').text('');
2328 jQuery('.vbo-ratesoverview-period-to-icon').show();
2329
2330 // reset room-ota relations
2331 jQuery('.vbo-roverw-setnewrate-vcm-otas').html('');
2332
2333 // move the element back to its original position
2334 jQuery('.vbo-ratesoverview-newratesrestr-wrap').appendTo('.vbo-ratesoverview-newratesrestr-helper');
2335 },
2336 });
2337
2338 jQuery('.vbo-ratesoverview-newratesrestr-wrap').appendTo(modal_body);
2339 }
2340
2341 function showVboDialogPeriod() {
2342 var format = new Date().format;
2343 format = format.replace(new RegExp("/", 'g'), new Date().datesep);
2344 jQuery('.vbo-ratesoverview-period-box-cals').fadeOut();
2345 jQuery("#rovervw-roomname").html('<?php VikBookingIcons::e('building'); ?> ' + vbolistener.first.roomName);
2346 jQuery("#rovervw-rplan").html(vbolistener.first.rplanName);
2347 jQuery("#rovervw-closeopen-rplan").html('"'+vbolistener.first.rplanName+'"');
2348 jQuery("#rovervw-fromdate").html(vbolistener.first.toDate(format));
2349 jQuery("#rovervw-todate").html(vbolistener.last.toDate(format));
2350 jQuery(".vbo-roverw-alldays-inner").html("");
2351 // reset default new price and placeholder
2352 jQuery("#roverw-newrate").attr("placeholder", "").val("");
2353 // check if all selected blocks are closed
2354 var all_blocks = getAllBlocksBetween(vbolistener.first, vbolistener.last, true);
2355 if (all_blocks !== false) {
2356 var allblocksclosed = true;
2357 jQuery.each(all_blocks, function(k, v) {
2358 if (!v.hasClass('vbo-roverw-rplan-off')) {
2359 allblocksclosed = false;
2360 return false;
2361 }
2362 });
2363 if (allblocksclosed) {
2364 jQuery("#rovervw-rplan").html('<span style="color: #f00"><i class="<?php echo VikBookingIcons::i('ban'); ?>"></i> '+vbolistener.first.rplanName+'</span>');
2365 }
2366 }
2367
2368 // populate OTA relations
2369 vboRatesOvervSetRoomOtaRelations(vbolistener.first.idroom, vbolistener.first.rplan);
2370
2371 // VCM check
2372 checkInvokeVcm();
2373
2374 // define the modal buttons
2375 let cancel_btn = jQuery('<button></button>')
2376 .attr('type', 'button')
2377 .addClass('btn')
2378 .text(Joomla.JText._('VBANNULLA'))
2379 .on('click', function() {
2380 VBOCore.emitEvent('vbo-dismiss-modal-roverv-newratesrestr');
2381 });
2382
2383 let save_btn = jQuery('<button></button>')
2384 .attr('type', 'button')
2385 .addClass('btn btn-success')
2386 .html('<?php VikBookingIcons::e('check-circle'); ?>' + Joomla.JText._('VBAPPLY'))
2387 .on('click', function() {
2388 setNewRates();
2389 });
2390
2391 // display modal
2392 let modal_body = VBOCore.displayModal({
2393 suffix: 'ratesoverv-newratesrestr',
2394 extra_class: 'vbo-modal-rounded vbo-modal-tall',
2395 title: Joomla.JText._('VBO_RATES_AND_RESTR'),
2396 lock_scroll: true,
2397 draggable: true,
2398 footer_left: cancel_btn,
2399 footer_right: save_btn,
2400 dismiss_event: 'vbo-dismiss-modal-roverv-newratesrestr',
2401 loading_event: 'vbo-loading-modal-roverv-newratesrestr',
2402 loading_body: '<?php VikBookingIcons::e('refresh', 'fa-spin fa-3x fa-fw'); ?>',
2403 onDismiss: () => {
2404 // cancel dates selection
2405 vbolistener.clear();
2406 jQuery('.day-block').removeClass('block-picked-start block-picked-middle block-picked-end');
2407
2408 // reset period selection
2409 jQuery('.vbo-ratesoverview-period-from').find('span').text('');
2410 jQuery('.vbo-ratesoverview-period-from-icon').show();
2411 jQuery('.vbo-ratesoverview-period-to').find('span').text('');
2412 jQuery('.vbo-ratesoverview-period-to-icon').show();
2413
2414 // reset room-ota relations
2415 jQuery('.vbo-roverw-setnewrate-vcm-otas').html('');
2416
2417 // move the element back to its original position
2418 jQuery('.vbo-ratesoverview-newratesrestr-wrap').appendTo('.vbo-ratesoverview-newratesrestr-helper');
2419 },
2420 });
2421
2422 jQuery('.vbo-ratesoverview-newratesrestr-wrap').appendTo(modal_body);
2423 }
2424
2425 function vboCheckVcmRatesChanges() {
2426 if (vcm_exists < 1) {
2427 return false;
2428 }
2429
2430 VBOCore.doAjax(
2431 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=checkvcmrateschanges'); ?>",
2432 {
2433 tmpl: "component",
2434 e4j_debug: debug_mode
2435 },
2436 (res) => {
2437 if (res.indexOf('e4j.error') >= 0 ) {
2438 console.log(res);
2439 alert(res.replace("e4j.error.", ""));
2440 jQuery('.vbo-ratesoverview-right-inner').hide();
2441 } else {
2442 //display the VCM link for updating the rates on the OTAs
2443 var obj_res = JSON.parse(res);
2444 var esitcont = "";
2445 if (obj_res.changesCount > 0 && obj_res.hasOwnProperty('changesData') && obj_res.changesData.hasOwnProperty('dfrom')) {
2446 esitcont += "<span class=\"vbo-ratesoverview-vcmwarn-close\"> <i class=\"vboicn-cancel-circle\"></i></span>";
2447 esitcont += "<span class=\"vbo-ratesoverview-vcmwarn-count\"><i class=\"vboicn-notification\"></i> <span>"+roverw_messages.vcmRatesChanged.replace("%d", obj_res.changesCount)+"</span></span>";
2448 esitcont += "<span class=\"vbo-ratesoverview-vcmwarn-open\"><a href=\"index.php?option=com_vikchannelmanager&amp;task=ratespush&amp;vbosess=1\" class=\"btn btn-primary\">"+roverw_messages.vcmRatesChangedOpen+"</a></span>";
2449 jQuery('.vbo-ratesoverview-right-inner').html(esitcont).fadeIn();
2450 } else {
2451 jQuery('.vbo-ratesoverview-right-inner').hide().html('');
2452 }
2453 }
2454 },
2455 (err) => {
2456 console.error(err);
2457 jQuery('.vbo-ratesoverview-right-inner').hide();
2458 }
2459 );
2460 }
2461
2462 /* Delay and launch the check VCM rates modification function, when the page loads */
2463 setTimeout(function() {
2464 vboCheckVcmRatesChanges();
2465 }, 1000);
2466 /* - */
2467
2468 /**
2469 * Displays the results of the Channel Manager update request for rates/restrictions.
2470 * Supports multiple rate plans due to derived/linkage rules.
2471 */
2472 function renderChannelManagerResult(vcm_response) {
2473 if (!Array.isArray(vcm_response)) {
2474 // make sure the result is a list of result objects
2475 vcm_response = [vcm_response];
2476 }
2477
2478 // compose modal body
2479 var htmlres = '<div class="vbo-vcm-rates-res-container">';
2480
2481 vcm_response.forEach((obj) => {
2482 htmlres += '<div class="vbo-vcm-rates-res-rplan-wrap">';
2483
2484 if (obj.hasOwnProperty('rplan_name')) {
2485 htmlres += '<div class="vbo-vcm-rates-res-rplan-data">';
2486 htmlres += '<strong>' + obj['rplan_name'] + '</strong>';
2487 if (obj.hasOwnProperty('is_derived') && obj['is_derived']) {
2488 htmlres += ' <span class="label label-info">' + Joomla.JText._('VBO_IS_DERIVED_RATE') + '</span>';
2489 }
2490 htmlres += '</div>';
2491 }
2492
2493 if (obj.hasOwnProperty('channels_success')) {
2494 htmlres += '<div class="vbo-vcm-rates-res-success">';
2495 for (var ch_id in obj['channels_success']) {
2496 htmlres += '<div class="vbo-vcm-rates-res-channel">';
2497 htmlres += ' <div class="vbo-vcm-rates-res-channel-esit">';
2498 htmlres += ' <i class="<?php echo VikBookingIcons::i('check'); ?>"></i>';
2499 htmlres += ' </div>';
2500 htmlres += ' <div class="vbo-vcm-rates-res-channel-logo">';
2501 if (obj['channels_updated'].hasOwnProperty(ch_id) && obj['channels_updated'][ch_id]['logo'].length) {
2502 htmlres += '<img src="'+obj['channels_updated'][ch_id]['logo']+'" />';
2503 } else {
2504 htmlres += '<span>'+obj['channels_success'][ch_id]+'</span>';
2505 }
2506 htmlres += ' </div>';
2507 htmlres += '</div>';
2508 }
2509
2510 if (obj.hasOwnProperty('channels_bkdown')) {
2511 htmlres += '<div class="vbo-vcm-rates-res-bkdown">';
2512 htmlres += ' <div><pre>'+obj['channels_bkdown']+'</pre></div>';
2513 htmlres += '</div>';
2514 }
2515 htmlres += '</div>';
2516 }
2517
2518 if (obj.hasOwnProperty('channels_warnings')) {
2519 htmlres += '<div class="vbo-vcm-rates-res-warning">';
2520 for (var ch_id in obj['channels_warnings']) {
2521 htmlres += '<div class="vbo-vcm-rates-res-channel">';
2522 htmlres += ' <div class="vbo-vcm-rates-res-channel-esit">';
2523 htmlres += ' <i class="<?php echo VikBookingIcons::i('exclamation-triangle'); ?>"></i>';
2524 htmlres += ' </div>';
2525 htmlres += ' <div class="vbo-vcm-rates-res-channel-logo">';
2526 if (obj['channels_updated'].hasOwnProperty(ch_id) && obj['channels_updated'][ch_id]['logo'].length) {
2527 htmlres += '<img src="'+obj['channels_updated'][ch_id]['logo']+'" />';
2528 } else if (obj['channels_updated'].hasOwnProperty(ch_id)) {
2529 htmlres += '<span>'+obj['channels_updated'][ch_id]['name']+'</span>';
2530 }
2531 htmlres += ' </div>';
2532 htmlres += ' <div class="vbo-vcm-rates-res-channel-det">';
2533 htmlres += ' <pre>'+obj['channels_warnings'][ch_id]+'</pre>';
2534 htmlres += ' </div>';
2535 htmlres += '</div>';
2536 }
2537 htmlres += '</div>';
2538 }
2539
2540 if (obj.hasOwnProperty('channels_errors')) {
2541 htmlres += '<div class="vbo-vcm-rates-res-error">';
2542 for (var ch_id in obj['channels_errors']) {
2543 htmlres += '<div class="vbo-vcm-rates-res-channel">';
2544 htmlres += ' <div class="vbo-vcm-rates-res-channel-esit">';
2545 htmlres += ' <i class="<?php echo VikBookingIcons::i('times'); ?>"></i>';
2546 htmlres += ' </div>';
2547 htmlres += ' <div class="vbo-vcm-rates-res-channel-logo">';
2548 if (obj['channels_updated'].hasOwnProperty(ch_id) && obj['channels_updated'][ch_id]['logo'].length) {
2549 htmlres += ' <img src="'+obj['channels_updated'][ch_id]['logo']+'" />';
2550 } else if (obj['channels_updated'].hasOwnProperty(ch_id)) {
2551 htmlres += ' <span>'+obj['channels_updated'][ch_id]['name']+'</span>';
2552 }
2553 htmlres += ' </div>';
2554 htmlres += ' <div class="vbo-vcm-rates-res-channel-det">';
2555 htmlres += ' <pre>'+obj['channels_errors'][ch_id]+'</pre>';
2556 htmlres += ' </div>';
2557 htmlres += '</div>';
2558 }
2559 htmlres += '</div>';
2560 }
2561
2562 htmlres += '</div>';
2563 });
2564
2565 // close container
2566 htmlres += '</div>';
2567
2568 // display results within a modal window
2569 VBOCore.displayModal({
2570 suffix: 'vbo-vcm-rates-res',
2571 extra_class: 'vbo-modal-rounded vbo-modal-tall',
2572 title: Joomla.JText._('VBOVCMRATESRES'),
2573 body: htmlres,
2574 draggable: true,
2575 });
2576 }
2577
2578 function vboRatesOvervSetRoomOtaRelations(room_id, rplan_id) {
2579 // the room-ota relations wrapper
2580 let wrapper = jQuery('.vbo-roverw-setnewrate-vcm-otas');
2581
2582 // always empty the wrapper
2583 wrapper.html('');
2584
2585 if (!room_id || !rplan_id || !vboRoomOtaRels.hasOwnProperty(room_id)) {
2586 // nothing to render
2587 return;
2588 }
2589
2590 // start counter
2591 let ota_ch_counter = 0;
2592
2593 // build and append room-OTA relations
2594 for (const ota_name in vboRoomOtaRels[room_id]['channels']) {
2595 // build ota readable name
2596 let ota_read_name = ota_name;
2597 ota_read_name = ota_read_name.replace(/api$/, '');
2598 ota_read_name = ota_read_name.replace(/^(google)(hotel|vr)$/i, '$1 $2');
2599
2600 // build room-ota relation block and elements
2601 let ota_block = jQuery('<div></div>');
2602 ota_block.addClass('vbo-roverw-setnewrate-vcm-ota-relation');
2603
2604 let ota_block_inner = jQuery('<div></div>');
2605 ota_block_inner
2606 .addClass('vbo-roverw-setnewrate-vcm-ota-relation-pricing')
2607 .attr('data-ota', (ota_name + '').toLowerCase());
2608
2609 let ota_block_channel = jQuery('<div></div>');
2610 ota_block_channel
2611 .addClass('vbo-roverw-setnewrate-vcm-ota-relation-channel')
2612 .attr('data-otaid', vboRoomOtaRels[room_id]['accounts'][ota_ch_counter]['idchannel'])
2613 .append('<img src="' + vboRoomOtaRels[room_id]['channels'][ota_name] + '" />')
2614 .append('<span>' + ota_read_name + '</span>');
2615
2616 let ota_pricing_value = jQuery('<span></span>');
2617 ota_pricing_value
2618 .addClass('vbo-roverw-setnewrate-vcm-ota-pricing-startvalue')
2619 .html('<?php VikBookingIcons::e('circle-notch', 'fa-spin fa-fw'); ?>')
2620 .on('click', function() {
2621 jQuery(this)
2622 .closest('.vbo-roverw-setnewrate-vcm-ota-relation-pricing')
2623 .find('.vbo-roverw-setnewrate-vcm-ota-channel-pricing')
2624 .toggle();
2625 });
2626
2627 let ota_block_pricing = jQuery('<div></div>');
2628 ota_block_pricing
2629 .addClass('vbo-roverw-setnewrate-vcm-ota-channel-pricing')
2630 .css('display', 'none')
2631 .append(jQuery('.vbo-roverw-setnewrate-vcm-ota-pricing-alteration').first().clone());
2632
2633 // register "input" event for select/input elements to control the channel alteration rule overrides
2634 ota_block_pricing.find('select, input').on('input', function() {
2635 let input_elem = jQuery(this);
2636
2637 // get the current channel alteration command
2638 let ota_alteration_command = input_elem
2639 .closest('.vbo-roverw-setnewrate-vcm-ota-relation-pricing')
2640 .find('.vbo-roverw-setnewrate-ota-pricing-currentvalue[data-alteration]')
2641 .attr('data-alteration');
2642
2643 // access alteration rule and input value
2644 let rmod_type = input_elem.attr('data-alter-rule');
2645 let rmod_value = input_elem.val();
2646
2647 if (!ota_alteration_command || !rmod_type || !(rmod_value + '').length) {
2648 return;
2649 }
2650
2651 // check what pricing factor was changed
2652 if (rmod_type == 'rmodsop') {
2653 // increase or decrease rate
2654 let command_old_val = ota_alteration_command.substr(0, 1);
2655 let command_new_val = parseInt(rmod_value) == 1 ? '+' : '-';
2656 ota_alteration_command = ota_alteration_command.replace(command_old_val, command_new_val);
2657 } else if (rmod_type == 'rmodsamount') {
2658 // amount
2659 let command_op = ota_alteration_command.substr(0, 1);
2660 let command_val = ota_alteration_command.substr(-1, 1);
2661 let command_old_val = ota_alteration_command.replace(command_op, '').replace(command_val, '');
2662 let command_new_val = parseFloat(rmod_value);
2663 ota_alteration_command = ota_alteration_command.replace(command_old_val, command_new_val);
2664 } else if (rmod_type == 'rmodsval') {
2665 // percent or absolute
2666 let command_old_val = ota_alteration_command.substr(-1, 1);
2667 let command_new_val = parseInt(rmod_value) == 1 ? '%' : '*';
2668 ota_alteration_command = ota_alteration_command.replace(command_old_val, command_new_val);
2669 }
2670
2671 // get current currency options
2672 let currencyObj = VBOCore.getCurrency();
2673 let orig_currency_options = currencyObj.getOptions();
2674
2675 // check if the channel requires a specific currency
2676 let ota_currency_data = input_elem
2677 .closest('.vbo-roverw-setnewrate-vcm-ota-relation-pricing')
2678 .find('.vbo-roverw-setnewrate-ota-pricing-willvalue')
2679 .attr('data-currency');
2680 if (ota_currency_data) {
2681 // decode currency data instructions
2682 try {
2683 ota_currency_data = JSON.parse(ota_currency_data);
2684 } catch (e) {
2685 ota_currency_data = {};
2686 }
2687 }
2688
2689 // define the current channel alteration string (readable)
2690 let ota_alteration_string = ota_alteration_command;
2691
2692 // finalize the current channel alteration string (readable)
2693 let ota_alteration_val = ota_alteration_string.substr(-1, 1);
2694 if (ota_alteration_val != '%') {
2695 ota_alteration_string = ota_alteration_string.replace(ota_alteration_val, '') + ((ota_currency_data && ota_currency_data?.symbol ? ota_currency_data.symbol : '') || vbo_currency_symbol);
2696 }
2697
2698 // update the alteration rule command attribute
2699 input_elem
2700 .closest('.vbo-roverw-setnewrate-vcm-ota-relation-pricing')
2701 .find('.vbo-roverw-setnewrate-ota-pricing-currentvalue[data-alteration]')
2702 .attr('data-alteration', ota_alteration_command);
2703
2704 // update the alteration rule string tag text
2705 input_elem
2706 .closest('.vbo-roverw-setnewrate-vcm-ota-relation-pricing')
2707 .find('.vbo-roverw-setnewrate-ota-pricing-currentvalue[data-alteration]')
2708 .html(ota_alteration_string);
2709
2710 // get the current rate to set
2711 let current_room_rate = jQuery('#roverw-newrate').val();
2712 if (current_room_rate) {
2713 // dispatch the event to trigger the re-calculation of the OTA rates
2714 VBOCore.emitEvent('vbo-roverv-setnewrate-calc-ota-pricing', {
2715 rate: current_room_rate,
2716 });
2717 }
2718 });
2719
2720 // append elements to wrapper
2721 ota_block_channel.append(ota_pricing_value);
2722 ota_block_inner.append(ota_block_channel);
2723 ota_block_inner.append(ota_block_pricing);
2724 ota_block.append(ota_block_inner);
2725 wrapper.append(ota_block);
2726
2727 // increase OTA channel counter
2728 ota_ch_counter++;
2729 }
2730
2731 // trigger an AJAX request to load the current alteration rules, if any
2732 VBOCore.doAjax(
2733 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=pricing.loadOtaAlterationRules'); ?>",
2734 {
2735 room_id: room_id,
2736 rate_id: rplan_id,
2737 },
2738 (res) => {
2739 var obj_res = typeof res === 'string' ? JSON.parse(res) : res;
2740 let alter_room_rates = obj_res['rmod'] == '1' || obj_res['rmod'] == 1;
2741
2742 // scan all room OTAs
2743 jQuery('.vbo-roverw-setnewrate-vcm-otas').find('.vbo-roverw-setnewrate-vcm-ota-relation').each(function(key, elem) {
2744 // get the current OTA identifier and whether pricing is altered
2745 let ota_wrap = jQuery(elem);
2746 let ota_id = ota_wrap.find('.vbo-roverw-setnewrate-vcm-ota-relation-channel').attr('data-otaid');
2747 let alter_ota_rates = alter_room_rates && obj_res['channels'] && (obj_res['channels'].includes(ota_id) || obj_res['channels'].includes(parseInt(ota_id)));
2748
2749 // check if the current channel is using a different currency
2750 let ota_currency_data = {};
2751 if (obj_res.hasOwnProperty('cur_rplans') && obj_res['cur_rplans'].hasOwnProperty(ota_id)) {
2752 let ota_check_currency = obj_res['cur_rplans'][ota_id];
2753 if (obj_res.hasOwnProperty('currency_data_options') && obj_res['currency_data_options'].hasOwnProperty(ota_check_currency)) {
2754 // set custom currency data returned
2755 ota_currency_data = obj_res['currency_data_options'][ota_check_currency];
2756 }
2757 }
2758
2759 // build pricing alteration strings
2760 let alteration_command = '';
2761 let alteration_string = '';
2762
2763 // default alteration factors (no pricing alteration rules)
2764 let alter_op = '+';
2765 let alter_amount = '0';
2766 let alter_val = '%';
2767
2768 if (alter_ota_rates) {
2769 // check how rates are altered for this channel
2770 if (obj_res.hasOwnProperty('rmod_channels') && obj_res['rmod_channels'].hasOwnProperty(ota_id)) {
2771 // ota-level pricing alteration rule
2772 if (parseInt(obj_res['rmod_channels'][ota_id]['rmod']) == 1) {
2773 alter_op = parseInt(obj_res['rmod_channels'][ota_id]['rmodop']) == 1 ? '+' : '-';
2774 alter_amount = obj_res['rmod_channels'][ota_id]['rmodamount'];
2775 alter_val = parseInt(obj_res['rmod_channels'][ota_id]['rmodop']) == 1 ? '%' : '*';
2776 }
2777 } else {
2778 // room-level pricing alteration rule
2779 alter_op = parseInt(obj_res['rmodop']) == 1 ? '+' : '-';
2780 alter_amount = obj_res['rmodamount'] || '0';
2781 alter_val = parseInt(obj_res['rmodop']) == 1 ? '%' : '*';
2782 }
2783 }
2784
2785 // construct alteration strings
2786 alteration_command = alter_op + (alter_amount + '') + (alter_val + '');
2787 alteration_string = alter_op + (alter_amount + '') + (alter_val == '%' ? '%' : (ota_currency_data?.symbol || vbo_currency_symbol));
2788
2789 // stop room-ota loading and set alteration string
2790 let alteration_elem = jQuery('<span></span>');
2791 alteration_elem
2792 .addClass('vbo-roverw-setnewrate-ota-pricing-currentvalue')
2793 .attr('data-alteration', alteration_command)
2794 .html(alteration_string);
2795
2796 let will_alter_elem = jQuery('<span></span>').addClass('vbo-roverw-setnewrate-ota-pricing-willvalue');
2797
2798 if (ota_currency_data.symbol) {
2799 // set currency data object
2800 will_alter_elem.attr('data-currency', JSON.stringify(ota_currency_data));
2801 }
2802
2803 // set elements
2804 ota_wrap
2805 .find('.vbo-roverw-setnewrate-vcm-ota-pricing-startvalue')
2806 .html('')
2807 .append(will_alter_elem)
2808 .append(alteration_elem)
2809 .append('<?php VikBookingIcons::e('edit', 'edit-ota-pricing'); ?>');
2810
2811 // populate default values for input element overrides
2812 ota_wrap.find('select[data-alter-rule="rmodsop"]').val(alter_op == '+' ? 1 : 0);
2813 ota_wrap.find('input[data-alter-rule="rmodsamount"]').val(parseInt(alter_amount) > 0 ? alter_amount : '');
2814 ota_wrap.find('select[data-alter-rule="rmodsval"]').val(alter_val == '%' ? 1 : 0);
2815 });
2816
2817 // check the current rate value
2818 let current_room_rate = jQuery('#roverw-newrate').val();
2819 if (current_room_rate) {
2820 // dispatch the event to allow the actual calculation of the OTA rate
2821 VBOCore.emitEvent('vbo-roverv-setnewrate-calc-ota-pricing', {
2822 rate: current_room_rate,
2823 room_id: room_id,
2824 rate_id: rplan_id,
2825 });
2826 }
2827 },
2828 (err) => {
2829 alert(err.responseText || 'Request Failed');
2830 }
2831 );
2832 }
2833
2834 function setNewRates() {
2835 var all_blocks = getAllBlocksBetween(vbolistener.first, vbolistener.last, true);
2836 var toval = jQuery("#roverw-newrate").val();
2837 var tovalint = parseFloat(toval);
2838 var invoke_vcm = jQuery('input[name="roverw-newrate-vcm"]').is(':checked') ? 1 : 0;
2839 var setminlos = jQuery('#roverw-newrestr').val();
2840 var closerplan = 0;
2841
2842 if (all_blocks === false || !toval.length || isNaN(tovalint) || tovalint <= 0) {
2843 alert(roverw_messages.setNewRatesMissing);
2844 return false;
2845 }
2846
2847 // set cookie to remember the action to invoke VCM for this combination of room-rateplan
2848 var nd = new Date();
2849 nd.setTime(nd.getTime() + (365*24*60*60*1000));
2850 document.cookie = "vboVcmRov"+vbolistener.first.idroom+vbolistener.first.rplan+"="+invoke_vcm+"; expires=" + nd.toUTCString() + "; path=/; SameSite=Lax";
2851 // check whether all blocks have closed the rate plan
2852 var allblocksclosed = true;
2853 jQuery.each(all_blocks, function(k, v) {
2854 if (!v.hasClass('vbo-roverw-rplan-off')) {
2855 allblocksclosed = false;
2856 // break
2857 return false;
2858 }
2859 });
2860 closerplan = allblocksclosed ? 1 : closerplan;
2861
2862 // check the OTA pricing alteration rules, if any
2863 let ota_pricing = {};
2864 if (invoke_vcm) {
2865 // scan all OTA alteration rules, if any
2866 document.querySelectorAll('.vbo-roverw-setnewrate-ota-pricing-currentvalue[data-alteration]').forEach((elem) => {
2867 // channel alteration string
2868 let alter_string = elem.getAttribute('data-alteration');
2869 if (!alter_string) {
2870 alter_string = '';
2871 }
2872
2873 // access the parent node to get the OTA channel identifier
2874 let ota_id = elem
2875 .closest('.vbo-roverw-setnewrate-vcm-ota-relation-channel[data-otaid]')
2876 .getAttribute('data-otaid');
2877
2878 if (!ota_id || !alter_string || alter_string == '+0%' || alter_string == '+0*') {
2879 // avoid pushing an empty alteration command
2880 return;
2881 }
2882
2883 // push OTA pricing alteration command
2884 ota_pricing[ota_id] = alter_string;
2885 });
2886 }
2887
2888 if (!Object.keys(ota_pricing).length) {
2889 // unset the object for the request
2890 ota_pricing = null;
2891 }
2892
2893 // show loading
2894 VBOCore.emitEvent('vbo-loading-modal-roverv-newratesrestr');
2895
2896 VBOCore.doAjax(
2897 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=pricing.setnewrates'); ?>",
2898 {
2899 tmpl: "component",
2900 e4j_debug: debug_mode,
2901 id_room: vbolistener.first.idroom,
2902 id_price: vbolistener.first.rplan,
2903 rate: toval,
2904 vcm: invoke_vcm,
2905 minlos: setminlos,
2906 fromdate: vbolistener.first.toDate("yy-mm-dd"),
2907 todate: vbolistener.last.toDate("yy-mm-dd"),
2908 rateclosed: closerplan,
2909 ota_pricing: ota_pricing,
2910 },
2911 (res) => {
2912 if (typeof res === 'string' && res.indexOf('e4j.error') === 0) {
2913 alert(res.replace('e4j.error.', ''));
2914 // stop loading
2915 VBOCore.emitEvent('vbo-loading-modal-roverv-newratesrestr');
2916 // abort
2917 return;
2918 }
2919
2920 try {
2921 // display new rates in all_blocks IDs
2922 var restr_set = false;
2923 var obj_res = typeof res === 'string' ? JSON.parse(res) : res;
2924
2925 jQuery.each(obj_res, function(k, v) {
2926 if (k == 'vcm') {
2927 return true;
2928 }
2929
2930 var elem = jQuery("#cell-"+k+"-"+vbolistener.first.idroom);
2931 if (elem.length) {
2932 elem.find(".vbo-rplan-price").html(v.cost);
2933 var spids = '';
2934 if (v.hasOwnProperty('spids')) {
2935 jQuery.each(v.spids, function(spk, spv) {
2936 spids += spv+'-';
2937 });
2938 //right trim dash
2939 spids = spids.replace(/-+$/, '');
2940 }
2941 elem.attr('data-vbospids', spids);
2942 // check if restrictions were set
2943 if (v.hasOwnProperty('newminlos')) {
2944 // always convert v.newminlos to a string to avoid errors with indexOf
2945 var newminlos = v.newminlos+'';
2946 if (newminlos.indexOf('e4j.error') >= 0) {
2947 // an error occurred
2948 alert(newminlos.replace("e4j.error.", ""));
2949 } else {
2950 // get cell identifier part
2951 restr_set = true;
2952 var cell_parts = k.split('-');
2953 var restr_elem = jQuery('#cell-'+cell_parts[0]+'-'+cell_parts[1]+'-'+cell_parts[2]+'-'+vbolistener.first.idroom+'-restr');
2954 if (restr_elem.length) {
2955 var restr_cont = restr_elem.find('.vbo-roverw-curminlos');
2956 restr_cont.html(newminlos);
2957 if (parseInt(newminlos) > 1) {
2958 restr_cont.addClass('vbo-roverw-curminlos-active');
2959 } else {
2960 restr_cont.removeClass('vbo-roverw-curminlos-active');
2961 }
2962 }
2963 // attempt to remove an eventual orphan list
2964 var orphan_elem = jQuery('.vbo-ratesoverview-orphan-dt[data-dt="'+cell_parts[0]+'-'+cell_parts[1]+'-'+cell_parts[2]+'-'+vbolistener.first.idroom+'"]');
2965 if (orphan_elem.length) {
2966 if (orphan_elem.length < 2) {
2967 jQuery('#vbo-ratesoverview-orphans-wrapper-'+vbolistener.first.idroom).fadeOut(400, function() {
2968 orphan_elem.remove();
2969 });
2970 } else {
2971 orphan_elem.remove();
2972 }
2973 }
2974 }
2975 }
2976 //
2977 }
2978 });
2979
2980 // stop loading
2981 VBOCore.emitEvent('vbo-loading-modal-roverv-newratesrestr');
2982
2983 // dismiss modal
2984 VBOCore.emitEvent('vbo-dismiss-modal-roverv-newratesrestr');
2985
2986 if (obj_res.hasOwnProperty('vcm')) {
2987 // display CM results
2988 renderChannelManagerResult(obj_res['vcm']);
2989 } else {
2990 setTimeout(function() {
2991 vboCheckVcmRatesChanges();
2992 }, 500);
2993 }
2994
2995 if (restr_set) {
2996 // re-calculate orphans after setting the restriction
2997 setTimeout(function() {
2998 vboCheckOrphans();
2999 }, 200);
3000 }
3001 } catch(err) {
3002 throw new Error(err);
3003 }
3004 },
3005 (err) => {
3006 alert(err.responseText || 'Request Failed');
3007 // stop loading
3008 VBOCore.emitEvent('vbo-loading-modal-roverv-newratesrestr');
3009 }
3010 );
3011 }
3012
3013 function modRoomRatePlan(mode) {
3014 var all_blocks = getAllBlocksBetween(vbolistener.first, vbolistener.last, true);
3015 if (all_blocks !== false && mode.length > 0) {
3016 // start loading
3017 VBOCore.emitEvent('vbo-loading-modal-roverv-newratesrestr');
3018
3019 VBOCore.doAjax(
3020 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=modroomrateplans'); ?>",
3021 {
3022 tmpl: "component",
3023 e4j_debug: debug_mode,
3024 id_room: vbolistener.first.idroom,
3025 id_price: vbolistener.first.rplan,
3026 type: mode,
3027 fromdate: vbolistener.first.toDate("yy-mm-dd"),
3028 todate: vbolistener.last.toDate("yy-mm-dd")
3029 },
3030 (res) => {
3031 if (res.indexOf('e4j.error') >= 0 ) {
3032 console.log(res);
3033 alert(res.replace("e4j.error.", ""));
3034 // stop loading
3035 VBOCore.emitEvent('vbo-loading-modal-roverv-newratesrestr');
3036 } else {
3037 // apply new classes in all_blocks IDs
3038 var obj_res = JSON.parse(res);
3039 jQuery.each(obj_res, function(k, v) {
3040 var elem = jQuery("#cell-"+k+"-"+vbolistener.first.idroom);
3041 if (elem.length) {
3042 elem.removeClass(v.oldcls).addClass(v.newcls);
3043 }
3044 });
3045
3046 // stop loading
3047 VBOCore.emitEvent('vbo-loading-modal-roverv-newratesrestr');
3048
3049 // dismiss modal
3050 VBOCore.emitEvent('vbo-dismiss-modal-roverv-newratesrestr');
3051
3052 setTimeout(function() {
3053 vboCheckVcmRatesChanges();
3054 }, 500);
3055 }
3056 },
3057 (err) => {
3058 alert("Request Failed");
3059 // stop loading
3060 VBOCore.emitEvent('vbo-loading-modal-roverv-newratesrestr');
3061 }
3062 );
3063 } else {
3064 alert(roverw_messages.modRplansMissing);
3065 return false;
3066 }
3067 }
3068
3069 function vboUpdateRplan(that) {
3070 if (vbolistener === null || vbolistener.first === null) {
3071 return true;
3072 }
3073 vbolistener.first.rplan = jQuery(that).val();
3074 vbolistener.first.rplanName = jQuery(that).find('option:selected').text();
3075 vbolistener.first.defRate = jQuery(that).find('option:selected').attr('data-defrate');
3076 }
3077
3078 function pickBlock(id) {
3079 var struct = initBlockStructure(id);
3080
3081 if (!vbolistener.pickFirst(struct)) {
3082 // first already picked
3083 if ((vbolistener.first.isBeforeThan(struct) || vbolistener.first.isSameDay(struct)) && vbolistener.first.isSameRplan(struct) && vbolistener.first.isSameRoom(struct)) {
3084 // last > first : pick last
3085 if (vbolistener.pickLast(struct)) {
3086 var all_blocks = getAllBlocksBetween(vbolistener.first, vbolistener.last, false);
3087 if (all_blocks !== false) {
3088 jQuery.each(all_blocks, function(k, v){
3089 if ( !v.hasClass('block-picked-middle') ) {
3090 v.addClass('block-picked-middle');
3091 }
3092 });
3093 jQuery('#'+vbolistener.last.id).addClass('block-picked-end');
3094 showVboDialog();
3095 }
3096 }
3097 } else {
3098 // last < first : clear selection
3099 vbolistener.clear();
3100 jQuery('.day-block').removeClass('block-picked-start block-picked-middle block-picked-end');
3101 }
3102 } else {
3103 // first picked
3104 jQuery('#'+vbolistener.first.id).addClass('block-picked-start');
3105 }
3106 }
3107
3108 function getAllBlocksBetween(start, end, outers_included) {
3109 if (!start.isSameRplan(end) || !start.isSameRoom(end)) {
3110 return false;
3111 }
3112
3113 if (start.isAfterThan(end)) {
3114 return false;
3115 }
3116
3117 var queue = new Array();
3118
3119 if (outers_included) {
3120 queue.push(jQuery('#'+start.id));
3121 }
3122
3123 if (start.isSameDay(end)) {
3124 return queue;
3125 }
3126
3127 var node = jQuery('#'+start.id).next();
3128 var end_id = jQuery('#'+end.id).attr('id');
3129 while (node.length > 0 && node.attr('id') != end_id) {
3130 queue.push(node);
3131 node = node.next();
3132 }
3133
3134 if (outers_included) {
3135 queue.push(jQuery('#'+end.id));
3136 }
3137
3138 return queue;
3139 }
3140
3141 function getPeriodStructure(data) {
3142 return {
3143 "day": parseInt(data[0]),
3144 "month": parseInt(data[1]),
3145 "year": parseInt(data[2]),
3146 "rplan": data[3],
3147 "idroom": data[6],
3148 "roomName": data[7],
3149 "rplanName": data[4],
3150 "defRate": data[5],
3151 "id": "cell-"+parseInt(data[0])+"-"+parseInt(data[1])+"-"+parseInt(data[2])+"-"+data[3]+"-"+data[6],
3152 "isSameDay": function(block) {
3153 return (this.month == block.month && this.day == block.day && this.year == block.year);
3154 },
3155 "isBeforeThan": function(block) {
3156 return (
3157 (this.year < block.year) ||
3158 (this.year == block.year && this.month < block.month) ||
3159 (this.year == block.year && this.month == block.month && this.day < block.day)
3160 );
3161 },
3162 "isAfterThan": function(block) {
3163 return (
3164 (this.year > block.year) ||
3165 (this.year == block.year && this.month > block.month) ||
3166 (this.year == block.year && this.month == block.month && this.day > block.day)
3167 );
3168 },
3169 "isSameRplan": function(block) {
3170 return (this.rplan == block.rplan);
3171 },
3172 "isSameRoom": function(block) {
3173 return (this.idroom == block.idroom);
3174 },
3175 "toDate": function(format) {
3176 return format.replace(
3177 'dd', (this.day < 10 ? '0' : '' ) + this.day
3178 ).replace(
3179 'mm', (this.month < 10 ? '0' : '') + this.month
3180 ).replace(
3181 'yy', this.year
3182 );
3183 }
3184 };
3185 }
3186
3187 function initBlockStructure(id) {
3188 var s = id.split("-");
3189 if (s.length != 6) {
3190 return {};
3191 }
3192 var elem = jQuery("#"+id);
3193 return {
3194 "day": parseInt(s[1]),
3195 "month": parseInt(s[2]),
3196 "year": parseInt(s[3]),
3197 "rplan": s[4],
3198 "idroom": s[5],
3199 "roomName": elem.parent("tr").find("td").first().attr("data-roomname"),
3200 "rplanName": elem.parent("tr").find("td").first().find(".vbo-rplan-name").text(),
3201 "defRate": elem.parent("tr").find("td").first().attr("data-defrate"),
3202 "id": id,
3203 "isSameDay": function(block) {
3204 return (this.month == block.month && this.day == block.day && this.year == block.year);
3205 },
3206 "isBeforeThan": function(block) {
3207 return (
3208 (this.year < block.year) ||
3209 (this.year == block.year && this.month < block.month) ||
3210 (this.year == block.year && this.month == block.month && this.day < block.day)
3211 );
3212 },
3213 "isAfterThan": function(block) {
3214 return (
3215 (this.year > block.year) ||
3216 (this.year == block.year && this.month > block.month) ||
3217 (this.year == block.year && this.month == block.month && this.day > block.day)
3218 );
3219 },
3220 "isSameRplan": function(block) {
3221 return (this.rplan == block.rplan);
3222 },
3223 "isSameRoom": function(block) {
3224 return (this.idroom == block.idroom);
3225 },
3226 "toDate": function(format) {
3227 return format.replace(
3228 'dd', (this.day < 10 ? '0' : '') + this.day
3229 ).replace(
3230 'mm', (this.month < 10 ? '0' : '') + this.month
3231 ).replace(
3232 'yy', this.year
3233 );
3234 }
3235 };
3236 }
3237
3238 function CalendarListener() {
3239 this.first = null;
3240 this.last = null;
3241 }
3242
3243 CalendarListener.prototype.pickFirst = function(struct) {
3244 if (!this.isFirstPicked()) {
3245 this.first = struct;
3246 return true;
3247 }
3248 return false;
3249 }
3250
3251 CalendarListener.prototype.pickLast = function(struct) {
3252 if (!this.isLastPicked() && this.isFirstPicked()) {
3253 this.last = struct;
3254 return true;
3255 }
3256 return false;
3257 }
3258
3259 CalendarListener.prototype.clear = function() {
3260 this.first = null;
3261 this.last = null;
3262 }
3263
3264 CalendarListener.prototype.isFirstPicked = function() {
3265 return this.first != null;
3266 }
3267
3268 CalendarListener.prototype.isLastPicked = function() {
3269 return this.last != null;
3270 }
3271
3272 /* Dates selection - End */
3273
3274 function vboToggleOBPRows() {
3275 jQuery('tr.vbo-roverviewtablerow-occupancy').toggle();
3276 var newcookieval = 0;
3277 if (jQuery('tr.vbo-roverviewtablerow-occupancy').length && jQuery('tr.vbo-roverviewtablerow-occupancy').is(':visible')) {
3278 newcookieval = 1;
3279 }
3280 var nd = new Date();
3281 nd.setTime(nd.getTime() + (365*24*60*60*1000));
3282 document.cookie = "vboRovwObp=" + newcookieval + "; expires=" + nd.toUTCString() + "; path=/; SameSite=Lax";
3283 }
3284
3285 function vboToggleCollapse() {
3286 var expand = jQuery('input[name="vbo_collapse"]').is(':checked');
3287 var collapse = expand ? 0 : 1;
3288 if (expand) {
3289 jQuery('.vbo-ratesoverview-roomsel-entry-calc-inner, .vbo-roverv-forecast-inner').show();
3290 jQuery('.vbo-ratesoverview-top-container').removeClass('collapsed');
3291 } else {
3292 jQuery('.vbo-ratesoverview-roomsel-entry-calc-inner, .vbo-roverv-forecast-inner').hide();
3293 jQuery('.vbo-ratesoverview-top-container').addClass('collapsed');
3294 }
3295 var nd = new Date();
3296 nd.setTime(nd.getTime() + (365*24*60*60*1000));
3297 document.cookie = "vboRovwColl=" + collapse + "; expires=" + nd.toUTCString() + "; path=/; SameSite=Lax";
3298 }
3299
3300 function vboCheckCollapse() {
3301 if (jQuery('.vbo-ratesoverview-roomsel-entry-calc-inner').is(':visible') && jQuery('.vbo-roverv-forecast-inner').is(':visible')) {
3302 // all elements are visible, expanded
3303 return;
3304 }
3305 jQuery('input[name="vbo_collapse"]').trigger('click');
3306 }
3307
3308 function vboSelectAllRooms(btn) {
3309 var sel_elem = jQuery('#roomsel');
3310 sel_elem.find('option').prop('selected', true);
3311 sel_elem.trigger('change');
3312 // hide button
3313 jQuery(btn).parent().remove();
3314 // auto-submit form
3315 document.vboratesovwform.submit();
3316 }
3317
3318 var timeline_height_set = false;
3319
3320 jQuery(function() {
3321 // register to the event emitted when a new booking is created through an admin widget
3322 document.addEventListener('vbo_new_booking_created', (e) => {
3323 if (!e || !e.detail || !e.detail.hasOwnProperty('bid') || !e.detail['bid']) {
3324 // do nothing
3325 return;
3326 }
3327 // reload the page to display the new booking just created
3328 location.reload();
3329 });
3330
3331 // toggle OBP rows
3332 if (jQuery('.vbo-ratesoverview-obp-toggle').length && jQuery('tr.vbo-roverviewtablerow-occupancy').length) {
3333 jQuery('.vbo-ratesoverview-obp-toggle').show();
3334 var initial = jQuery('.vbo-ratesoverview-obp-toggle').attr('data-obpstartstatus');
3335 if (initial == '0') {
3336 jQuery('tr.vbo-roverviewtablerow-occupancy').hide();
3337 }
3338 }
3339 //
3340 jQuery(".vbo-ratesoverview-tab-los").click(function() {
3341 var nd = new Date();
3342 nd.setTime(nd.getTime() + (365*24*60*60*1000));
3343 document.cookie = "vboRovwRab=los; expires=" + nd.toUTCString() + "; path=/; SameSite=Lax";
3344 jQuery(this).removeClass("vbo-ratesoverview-tab-unactive").addClass("vbo-ratesoverview-tab-active");
3345 jQuery(".vbo-ratesoverview-tab-cal").removeClass("vbo-ratesoverview-tab-active").addClass("vbo-ratesoverview-tab-unactive");
3346 jQuery(".vbo-ratesoverview-roomsel-entry-los").show();
3347 jQuery(".vbo-ratesoverview-roomsel-entry-forecast").hide();
3348 jQuery(".vbo-ratesoverview-caltab-cont").hide();
3349 jQuery(".vbo-ratesoverview-lostab-cont").fadeIn();
3350 if (!timeline_height_set) {
3351 jQuery('.vbo-timeline-container').css('min-height', (jQuery('.vbo-timeline-container').outerHeight() + 20));
3352 timeline_height_set = true;
3353 }
3354 });
3355 jQuery(".vbo-ratesoverview-tab-cal").click(function() {
3356 var nd = new Date();
3357 nd.setTime(nd.getTime() + (365*24*60*60*1000));
3358 document.cookie = "vboRovwRab=cal; expires=" + nd.toUTCString() + "; path=/; SameSite=Lax";
3359 jQuery(this).removeClass("vbo-ratesoverview-tab-unactive").addClass("vbo-ratesoverview-tab-active");
3360 jQuery(".vbo-ratesoverview-tab-los").removeClass("vbo-ratesoverview-tab-active").addClass("vbo-ratesoverview-tab-unactive");
3361 jQuery(".vbo-ratesoverview-roomsel-entry-los").hide();
3362 jQuery(".vbo-ratesoverview-roomsel-entry-forecast").show();
3363 jQuery(".vbo-ratesoverview-lostab-cont").hide();
3364 jQuery(".vbo-ratesoverview-caltab-cont").fadeIn();
3365 });
3366 if (window.location.hash == '#tabcal') {
3367 jQuery(".vbo-ratesoverview-tab-cal").trigger("click");
3368 }
3369 jQuery("body").on("click", ".vbo-ratesoverview-numnight", function() {
3370 var inpnight = jQuery(this).attr('id');
3371 if (jQuery('.vbo-ratesoverview-numnight').length > 1) {
3372 jQuery('#inp'+inpnight).remove();
3373 jQuery(this).remove();
3374 }
3375 });
3376 jQuery("body").on("dblclick", ".vbo-calcrates-rateblock", function() {
3377 if (jQuery(this).parent('.vbo-ratesoverview-calculation-response-room').find('.vbo-calcrates-rateblock').length < 2) {
3378 // remove the whole container as there is just one rate plan
3379 jQuery(this).parent('.vbo-ratesoverview-calculation-response-room').remove();
3380 } else {
3381 // remove only this rate plan
3382 jQuery(this).remove();
3383 }
3384 });
3385 jQuery('#vbo-addnumnight-act').click(function() {
3386 var setnights = jQuery('#vbo-addnumnight').val();
3387 if (parseInt(setnights) > 0) {
3388 var los_exists = false;
3389 jQuery('.vbo-ratesoverview-numnight').each(function() {
3390 if (parseInt(jQuery(this).text()) == parseInt(setnights)) {
3391 los_exists = true;
3392 }
3393 });
3394 if (!los_exists) {
3395 jQuery('.vbo-ratesoverview-numnight').last().after("<span class=\"vbo-ratesoverview-numnight\" id=\"numnights"+setnights+"\">"+setnights+"</span><input type=\"hidden\" name=\"nights_cal[]\" id=\"inpnumnights"+setnights+"\" value=\""+setnights+"\" />");
3396 } else {
3397 jQuery('#vbo-addnumnight').val((parseInt(setnights) + 1));
3398 }
3399 }
3400 });
3401 jQuery('#vbo-ratesoverview-calculate').click(function() {
3402 jQuery(this).text(Joomla.JText._('VBRATESOVWRATESCALCULATORCALCING')).prop('disabled', true);
3403 var checkindate = jQuery("#checkindate").val();
3404 if (!(checkindate.length > 0)) {
3405 checkindate = '<?php echo date('Y-m-d') ?>';
3406 jQuery("#checkindate").val(checkindate);
3407 }
3408 var nights = jQuery("#vbo-numnights").val();
3409 var adults = jQuery("#vbo-numadults").val();
3410 var children = jQuery("#vbo-numchildren").val();
3411 var idroom = jQuery("#roomselcalc").val();
3412 // always remove warning messages
3413 jQuery(".vbo-ratesoverview-calculation-response").find('.vbo-warning').remove();
3414 if (jQuery("#vbo-ratesoverview-calculation-response-room"+idroom).length) {
3415 // remove previous containers for this room
3416 jQuery("#vbo-ratesoverview-calculation-response-room"+idroom).remove();
3417 }
3418 if (!jQuery(".vbo-ratesoverview-calculation-response-room").length) {
3419 // if no rooms responses, empty the whole container
3420 jQuery('.vbo-ratesoverview-calculation-response').html('');
3421 }
3422
3423 VBOCore.doAjax(
3424 "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=calc_rates'); ?>",
3425 {
3426 tmpl: "component",
3427 id_room: idroom,
3428 checkin: checkindate,
3429 num_nights: nights,
3430 num_adults: adults,
3431 num_children: children
3432 },
3433 (res) => {
3434 res = JSON.parse(res);
3435 res = res[0];
3436 if (res.indexOf('e4j.error') >= 0 ) {
3437 jQuery(".vbo-ratesoverview-calculation-response").html("<p class='vbo-warning'>" + res.replace("e4j.error.", "") + "</p>").fadeIn();
3438 } else {
3439 var titlecont = '<span class="vbo-ratesoverview-calculation-response-room-name">'+jQuery("#roomselcalc option:selected").text() + '</span> ' + checkindate + ' - ' + jQuery("#checkoutdate").val() + ', ' + nights + ' <?php echo addslashes(JText::translate('VBRATESOVWRATESCALCNUMNIGHTS')); ?>, ' + adults + ' <?php echo addslashes(JText::translate('VBRATESOVWRATESCALCNUMADULTS')); ?>';
3440 var newcont = '<div class="vbo-ratesoverview-calculation-response-room" id="vbo-ratesoverview-calculation-response-room'+idroom+'"><h4>'+titlecont+'</h4>'+res+'</div>';
3441 // check whether the content should be appended
3442 if (jQuery(".vbo-ratesoverview-calculation-response").find('.vbo-ratesoverview-calculation-response-room').length) {
3443 newcont = jQuery(".vbo-ratesoverview-calculation-response").html() + newcont;
3444 }
3445 //
3446 jQuery(".vbo-ratesoverview-calculation-response").html(newcont).fadeIn();
3447 // loop over every room response and pricing to append the book-now button for the page calendar
3448 var base_booknow_link_orig = jQuery('#vbo-base-booknow-link').attr('href');
3449 jQuery('.vbo-calcrates-rateblock').each(function(k, v) {
3450 var elem = jQuery(v);
3451 var base_booknow_link = base_booknow_link_orig;
3452 // remove existing button
3453 elem.find('.vbo-room-booknow-rct').remove();
3454 //
3455 var b_idprice = elem.attr('data-idprice');
3456 base_booknow_link = base_booknow_link.replace('idprice=', 'idprice=' + b_idprice);
3457 var b_idroom = elem.attr('data-idroom');
3458 base_booknow_link = base_booknow_link.replace('cid[]=', 'cid[]=' + b_idroom);
3459 var b_checkin = elem.attr('data-checkin');
3460 base_booknow_link = base_booknow_link.replace('checkin=', 'checkin=' + b_checkin);
3461 var b_checkout = elem.attr('data-checkout');
3462 base_booknow_link = base_booknow_link.replace('checkout=', 'checkout=' + b_checkout);
3463 var b_adults = elem.attr('data-adults');
3464 base_booknow_link = base_booknow_link.replace('adults=', 'adults=' + b_adults);
3465 var b_children = elem.attr('data-children');
3466 base_booknow_link = base_booknow_link.replace('children=', 'children=' + b_children);
3467 var booknow = '<a href="' + base_booknow_link + '" class="btn btn-primary vbo-room-booknow-rct" target="_blank">' + Joomla.JText._('VBO_BOOKNOW') + '</a>';
3468 elem.append(booknow);
3469 });
3470 //
3471 }
3472 jQuery('#vbo-ratesoverview-calculate').text(Joomla.JText._('VBRATESOVWRATESCALCULATORCALC')).prop('disabled', false);
3473 },
3474 (err) => {
3475 console.error(err);
3476 jQuery(".vbo-ratesoverview-calculation-response").fadeOut();
3477 jQuery('#vbo-ratesoverview-calculate').text(Joomla.JText._('VBRATESOVWRATESCALCULATORCALC')).prop('disabled', false);
3478 alert("Error Performing Ajax Request");
3479 }
3480 );
3481 });
3482
3483 /* Orphans Calculation */
3484 vboCheckOrphans();
3485 //
3486 });
3487
3488 function vboCheckOrphans() {
3489 /* Orphans Calculation - Start */
3490 var calc_method = '<?php echo VikBooking::orphansCalculation(); ?>';
3491 jQuery('table.vbratesoverviewtable').each(function() {
3492 var current_room = jQuery(this).attr('data-idroom');
3493 var orphans_pool = new Array;
3494 var avcells = jQuery(this).find('tr.vbo-roverviewtableavrow').find('td');
3495 var restrcells = jQuery(this).find('tr.vbo-roverviewtablerow-restrs').find('td');
3496 avcells.each(function(k, v) {
3497 if (!jQuery(v).attr('data-curunits')) {
3498 // continue
3499 return true;
3500 }
3501 var todayav = parseInt(jQuery(v).attr('data-curunits'));
3502 if (!restrcells.hasOwnProperty(k) || isNaN(todayav) || todayav < 1) {
3503 // continue, no restriction cell found or no availability for this day
3504 return true;
3505 }
3506 var todayminlos = parseInt(jQuery(restrcells[k]).find('.vbo-roverw-curminlos').text());
3507 if (isNaN(todayminlos) || todayminlos < 2) {
3508 // continue, no min los > 1 set for this day
3509 return true;
3510 }
3511 // check if any night after today, until min los, is fully booked
3512 var cell_queue = new Array;
3513 var hasorphans = false;
3514 var forward_count = 0;
3515 cell_queue.push(v);
3516 for (var i = 1; i < todayminlos; i++) {
3517 if (!avcells.hasOwnProperty((k + i))) {
3518 // break loop, no info for this day after
3519 break;
3520 }
3521 var tomorrowav = parseInt(jQuery(avcells[(k + i)]).attr('data-curunits'));
3522 if (isNaN(tomorrowav) || tomorrowav != 0) {
3523 // continue, availability found for tomorrow, we need a non available next-day
3524 continue;
3525 }
3526 // orphan found for this day
3527 hasorphans = true;
3528 forward_count = i;
3529 break;
3530 }
3531 // backward calculation method only if "prevnext".
3532 var backward_count = 0;
3533 for (var i = 1; i <= todayminlos; i++) {
3534 if (!avcells.hasOwnProperty((k - i))) {
3535 // break loop, no info for this prev day
3536 break;
3537 }
3538 var yesterdayav = parseInt(jQuery(avcells[(k - i)]).attr('data-curunits'));
3539 if (isNaN(yesterdayav) || yesterdayav != 0) {
3540 // increase free nights going backward
3541 backward_count++;
3542 }
3543 }
3544 if (calc_method == 'prevnext' && hasorphans && backward_count > 0 && (backward_count >= todayminlos || (backward_count + forward_count) >= todayminlos)) {
3545 // this should not be an orphan date because of enough free days back, or enough free days in between
3546 hasorphans = false;
3547 }
3548
3549 if (hasorphans && cell_queue.length) {
3550 for (var i in cell_queue) {
3551 if (cell_queue.hasOwnProperty(i)) {
3552 // push the cell
3553 orphans_pool.push(cell_queue[i]);
3554 }
3555 }
3556 }
3557 });
3558 if (orphans_pool.length) {
3559 var orphans_list = '';
3560 for (var i in orphans_pool) {
3561 if (!orphans_pool.hasOwnProperty(i)) {
3562 continue;
3563 }
3564 var idparts = jQuery(orphans_pool[i]).attr('id').split('-');
3565 orphans_list += '<div class="vbo-ratesoverview-orphan-dt" data-dt="'+idparts[1]+'-'+idparts[2]+'-'+idparts[3]+'-'+current_room+'">'+vbowdays[idparts[4]]+', '+vbomonths[(parseInt(idparts[2]) - 1)]+' '+idparts[1]+' '+idparts[3]+'</div>';
3566 }
3567 jQuery('#vbo-ratesoverview-orphans-list-'+current_room).html(orphans_list);
3568 jQuery('#vbo-ratesoverview-orphans-wrapper-'+current_room).fadeIn();
3569 }
3570 });
3571 /* Orphans Calculation - End */
3572 }
3573 </script>
3574
3575 <div class="vbo-ratesoverview-fests-helper" style="display: none;">
3576 <div class="vbo-ratesoverview-fests-wrap">
3577 <div class="vbo-overlay-fests-list"></div>
3578 <div class="vbo-overlay-fests-addnew" data-ymd="">
3579 <h4><?php echo JText::translate('VBOADDCUSTOMFESTTODAY'); ?></h4>
3580 <div class="vbo-overlay-fests-addnew-elem">
3581 <label for="vbo-newfest-name"><?php echo JText::translate('VBPVIEWPLACESONE'); ?></label>
3582 <input type="text" id="vbo-newfest-name" value="" />
3583 </div>
3584 <div class="vbo-overlay-fests-addnew-elem">
3585 <label for="vbo-newfest-descr"><?php echo JText::translate('VBPLACEDESCR'); ?></label>
3586 <textarea id="vbo-newfest-descr"></textarea>
3587 </div>
3588 </div>
3589 </div>
3590 </div>
3591