PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.7.1
VikBooking Hotel Booking Engine & PMS v1.7.1
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 / helpers / widgets / finance.php

finance.php in VikBooking Hotel Booking Engine & PMS 1.7.1, at admin/helpers/widgets/finance.php

1,695 lines 64.3 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 /**
14 * Class handler for admin widget "finance".
15 *
16 * @since 1.16.0 (J) - 1.6.0 (WP)
17 */
18 class VikBookingAdminWidgetFinance extends VikBookingAdminWidget
19 {
20 /**
21 * The instance counter of this widget. Since we do not load individual parameters
22 * for each widget's instance, we use a static counter to determine its settings.
23 *
24 * @var int
25 */
26 protected static $instance_counter = -1;
27
28 /**
29 * Class constructor will define the widget name and identifier.
30 */
31 public function __construct()
32 {
33 // call parent constructor
34 parent::__construct();
35
36 $this->widgetName = JText::translate('VBO_W_FINANCE_TITLE');
37 $this->widgetDescr = JText::translate('VBO_W_FINANCE_DESCR');
38 $this->widgetId = basename(__FILE__, '.php');
39
40 // define widget and icon and style name
41 $this->widgetIcon = '<i class="' . VikBookingIcons::i('piggy-bank') . '"></i>';
42 $this->widgetStyleName = 'red';
43 }
44
45 /**
46 * Preload the necessary CSS/JS assets.
47 *
48 * @return void
49 */
50 public function preload()
51 {
52 // load assets for datepicker
53 $this->vbo_app->loadDatePicker();
54
55 // load assets for contextual menu
56 $this->vbo_app->loadContextMenuAssets();
57
58 // check for daily welcome message with stats
59 $this->setupWelcomeStats();
60
61 // lang vars for JS
62 JText::script('VBO_COMPARE_WITH_LAST_Y');
63 JText::script('VBO_COMPARE_WITH_PREV_Q');
64 JText::script('VBO_COMPARE_WITH_PREV_M');
65 JText::script('VBO_COMPARE_WITH_PREV_W');
66 JText::script('VBO_COMPARE_WITH_PREV_D');
67 JText::script('VBNOTRACKINGS');
68 JText::script('VBO_ERR_LOAD_RESULTS');
69 JText::script('VBNOROOMSFOUND');
70 JText::script('VBO_SEARCHING');
71 JText::script('VBROOMFILTER');
72 }
73
74 /**
75 * Custom method for this widget only to load the financial statistics.
76 * The method is called by the admin controller through an AJAX request.
77 * The visibility should be public, it should not exit the process, and
78 * any content sent to output will be returned to the AJAX response.
79 * In this case we return an array because this method requires "return":1.
80 *
81 * It's the actual rendering of the widget which also allows navigation.
82 */
83 public function loadFinancialStats()
84 {
85 $app = JFactory::getApplication();
86
87 $wrapper = $app->input->getString('wrapper', '');
88
89 $fromdate = $app->input->getString('fromdate', '');
90 $todate = $app->input->getString('todate', '');
91 $step = $app->input->getString('step', 'quarter');
92 $date_dir = $app->input->getInt('date_dir', 0);
93 $room_ids = array_filter((array) $app->input->getInt('room_ids', []));
94
95 if (empty($fromdate)) {
96 // default to current quarter (3 full months)
97 $now_info = getdate();
98 $end_info = getdate(mktime(0, 0, 0, ($now_info['mon'] + 2), 1, $now_info['year']));
99 $to_ts = mktime(23, 59, 59, $end_info['mon'], date('t', $end_info[0]), $end_info['year']);
100 $fromdate = date('Y-m-d', mktime(0, 0, 0, $now_info['mon'], 1, $now_info['year']));
101 $todate = date('Y-m-d', $to_ts);
102 }
103
104 // convert dates to timestamps to support custom date formats
105 $from_ts = VikBooking::getDateTimestamp($fromdate);
106 $to_ts = VikBooking::getDateTimestamp($todate, 23, 59, 59);
107
108 // check dates navigation and step
109 $step_name = '';
110 if ($date_dir > 0 || $date_dir < 0) {
111 // calculate forward or backward dates for navigation
112 list($from_ts, $to_ts, $step_name) = $this->calcDatesNavigation($from_ts, $to_ts, $step, $date_dir);
113 }
114
115 // convert final dates to Y-m-d
116 $fromdate = date('Y-m-d', $from_ts);
117 $todate = date('Y-m-d', $to_ts);
118
119 // stats calculation type
120 $calc_type = !strcasecmp($step, 'booking_dates') ? 'booking_dates' : 'stay_dates';
121
122 // access the finance helper object
123 $finance = VBOTaxonomyFinance::getInstance();
124
125 // currency symbol
126 $currencysymb = VikBooking::getCurrencySymb();
127
128 // get the financial stats for the requested dates
129 try {
130 $stats = $finance->getStats($fromdate, $todate, $room_ids, $calc_type);
131 } catch (Exception $e) {
132 // make the AJAX request fail nicely
133 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
134 }
135
136 // language string identifier for step-comparison
137 $js_step_comp_str = 'VBO_COMPARE_WITH_PREV_Q';
138 if ($step == 'month') {
139 $js_step_comp_str = 'VBO_COMPARE_WITH_PREV_M';
140 } elseif ($step == 'week') {
141 $js_step_comp_str = 'VBO_COMPARE_WITH_PREV_W';
142 } elseif (!strcasecmp($step, 'booking_dates')) {
143 $js_step_comp_str = 'VBO_COMPARE_WITH_PREV_D';
144 }
145
146 // start output buffering
147 ob_start();
148
149 if ($room_ids) {
150 // get the details of the rooms filtered
151 $rooms_filtered = VikBooking::getAvailabilityInstance()->loadRooms($room_ids);
152
153 if ($rooms_filtered) {
154 ?>
155 <div class="vbo-widget-finance-rooms-filtered-cont">
156 <?php echo implode(', ', array_column($rooms_filtered, 'name')); ?>
157 </div>
158 <?php
159 }
160 }
161
162 ?>
163 <div class="vbo-widget-finance-data-blocks">
164
165 <div class="vbo-widget-finance-data-block" data-typestat="gross_revenue">
166 <div class="vbo-widget-finance-stat">
167 <div class="vbo-widget-finance-stat-info">
168 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBO_GROSS_BOOKING_VALUE'); ?></span>
169 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
170 </div>
171 <div class="vbo-widget-finance-stat-amount">
172 <span class="vbo-widget-finance-stat-amount-value vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo $currencysymb . ' ' . VikBooking::numberFormat($stats['gross_revenue']); ?>">
173 <span class="vbo-currency"><?php echo $currencysymb; ?></span>
174 <span class="vbo-price"><?php echo $finance->numberFormatShort($stats['gross_revenue']); ?></span>
175 </span>
176 </div>
177 </div>
178 </div>
179
180 <div class="vbo-widget-finance-data-block" data-typestat="taxes">
181 <div class="vbo-widget-finance-stat">
182 <div class="vbo-widget-finance-stat-info">
183 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBOINVCOLTAX'); ?></span>
184 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
185 </div>
186 <div class="vbo-widget-finance-stat-amount">
187 <span class="vbo-widget-finance-stat-amount-value vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo $currencysymb . ' ' . VikBooking::numberFormat($stats['taxes']); ?>">
188 <span class="vbo-currency"><?php echo $currencysymb; ?></span>
189 <span class="vbo-price"><?php echo $finance->numberFormatShort($stats['taxes']); ?></span>
190 </span>
191 </div>
192 </div>
193 </div>
194
195 <div class="vbo-widget-finance-data-block" data-typestat="cmms">
196 <div class="vbo-widget-finance-stat">
197 <div class="vbo-widget-finance-stat-info">
198 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBTOTALCOMMISSIONS'); ?></span>
199 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
200 </div>
201 <div class="vbo-widget-finance-stat-amount">
202 <span class="vbo-widget-finance-stat-amount-value vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo $currencysymb . ' ' . VikBooking::numberFormat($stats['cmms']); ?>">
203 <span class="vbo-currency"><?php echo $currencysymb; ?></span>
204 <span class="vbo-price"><?php echo $finance->numberFormatShort($stats['cmms']); ?></span>
205 </span>
206 </div>
207 </div>
208 </div>
209
210 <div class="vbo-widget-finance-data-block" data-typestat="revenue">
211 <div class="vbo-widget-finance-stat">
212 <div class="vbo-widget-finance-stat-info">
213 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBOREPORTREVENUE'); ?></span>
214 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
215 </div>
216 <div class="vbo-widget-finance-stat-amount">
217 <span class="vbo-widget-finance-stat-amount-value vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo $currencysymb . ' ' . VikBooking::numberFormat($stats['revenue']); ?>">
218 <span class="vbo-currency"><?php echo $currencysymb; ?></span>
219 <span class="vbo-price"><?php echo $finance->numberFormatShort($stats['revenue']); ?></span>
220 </span>
221 </div>
222 </div>
223 </div>
224
225 <div class="vbo-widget-finance-data-block" data-typestat="ibe_revenue">
226 <div class="vbo-widget-finance-stat">
227 <div class="vbo-widget-finance-stat-info">
228 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBOREPORTREVENUEREVWEB'); ?></span>
229 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
230 </div>
231 <div class="vbo-widget-finance-stat-amount">
232 <span class="vbo-widget-finance-stat-amount-value vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo $currencysymb . ' ' . VikBooking::numberFormat($stats['ibe_revenue']); ?>">
233 <span class="vbo-currency"><?php echo $currencysymb; ?></span>
234 <span class="vbo-price"><?php echo $finance->numberFormatShort($stats['ibe_revenue']); ?></span>
235 </span>
236 </div>
237 </div>
238 </div>
239
240 <div class="vbo-widget-finance-data-block" data-typestat="ota_revenue">
241 <div class="vbo-widget-finance-stat">
242 <div class="vbo-widget-finance-stat-info">
243 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBOREPORTREVENUEREVOTA'); ?></span>
244 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
245 </div>
246 <div class="vbo-widget-finance-stat-amount">
247 <span class="vbo-widget-finance-stat-amount-value vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo $currencysymb . ' ' . VikBooking::numberFormat($stats['ota_revenue']); ?>">
248 <span class="vbo-currency"><?php echo $currencysymb; ?></span>
249 <span class="vbo-price"><?php echo $finance->numberFormatShort($stats['ota_revenue']); ?></span>
250 </span>
251 </div>
252 </div>
253 </div>
254
255 <?php
256 if ($stats['cmm_savings'] > 0) {
257 ?>
258 <div class="vbo-widget-finance-data-block" data-typestat="ota_avg_cmms">
259 <div class="vbo-widget-finance-stat">
260 <div class="vbo-widget-finance-stat-info">
261 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBO_AVG_COMMISSIONS'); ?></span>
262 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
263 </div>
264 <div class="vbo-widget-finance-stat-amount">
265 <span class="vbo-widget-finance-stat-amount-value">
266 <span><?php echo $stats['ota_avg_cmms']; ?>%</span>
267 </span>
268 </div>
269 </div>
270 </div>
271
272 <div class="vbo-widget-finance-data-block" data-typestat="cmm_savings">
273 <div class="vbo-widget-finance-stat">
274 <div class="vbo-widget-finance-stat-info">
275 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBO_COMMISSION_SAVINGS'); ?></span>
276 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
277 </div>
278 <div class="vbo-widget-finance-stat-amount">
279 <span class="vbo-widget-finance-stat-amount-value vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo $currencysymb . ' ' . VikBooking::numberFormat($stats['cmm_savings']); ?>">
280 <span class="vbo-currency"><?php echo $currencysymb; ?></span>
281 <span class="vbo-price"><?php echo $finance->numberFormatShort($stats['cmm_savings']); ?></span>
282 </span>
283 </div>
284 </div>
285 </div>
286 <?php
287 }
288 ?>
289
290 <div class="vbo-widget-finance-data-block" data-typestat="tot_bookings">
291 <div class="vbo-widget-finance-stat">
292 <div class="vbo-widget-finance-stat-info">
293 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBCUSTOMERTOTBOOKINGS'); ?></span>
294 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
295 </div>
296 <div class="vbo-widget-finance-stat-amount">
297 <span class="vbo-widget-finance-stat-amount-value">
298 <span><?php echo $stats['tot_bookings']; ?></span>
299 </span>
300 </div>
301 </div>
302 </div>
303
304 <div class="vbo-widget-finance-data-block" data-typestat="nights_booked">
305 <div class="vbo-widget-finance-stat">
306 <div class="vbo-widget-finance-stat-info">
307 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBOGRAPHTOTNIGHTSLBL'); ?></span>
308 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
309 </div>
310 <div class="vbo-widget-finance-stat-amount">
311 <span class="vbo-widget-finance-stat-amount-value vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo htmlspecialchars(trim(str_replace('%', '', JText::translate('VBOREPORTREVENUEPOCC'))) . ' ' . $stats['occupancy'] . '%'); ?>">
312 <span><?php echo $stats['nights_booked'] . '/' . $stats['tot_inventory']; ?></span>
313 </span>
314 </div>
315 </div>
316 </div>
317
318 <div class="vbo-widget-finance-data-block" data-typestat="avg_los">
319 <div class="vbo-widget-finance-stat">
320 <div class="vbo-widget-finance-stat-info">
321 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBTRKAVGLOS'); ?></span>
322 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
323 </div>
324 <div class="vbo-widget-finance-stat-amount">
325 <span class="vbo-widget-finance-stat-amount-value">
326 <span><?php echo $stats['avg_los']; ?></span>
327 </span>
328 </div>
329 </div>
330 </div>
331
332 <div class="vbo-widget-finance-data-block" data-typestat="abw">
333 <div class="vbo-widget-finance-stat">
334 <div class="vbo-widget-finance-stat-info">
335 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBO_AVG_BOOK_WINDOW'); ?></span>
336 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
337 </div>
338 <div class="vbo-widget-finance-stat-amount">
339 <span class="vbo-widget-finance-stat-amount-value vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo htmlspecialchars(JText::translate('VBOCRONSMSREMPARAMBEFD')); ?>">
340 <span><?php echo round($stats['abw'], 1); ?></span>
341 </span>
342 </div>
343 </div>
344 </div>
345
346 <div class="vbo-widget-finance-data-block" data-typestat="rooms_booked">
347 <div class="vbo-widget-finance-stat">
348 <div class="vbo-widget-finance-stat-info">
349 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBLIBTEN'); ?></span>
350 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
351 </div>
352 <div class="vbo-widget-finance-stat-amount">
353 <span class="vbo-widget-finance-stat-amount-value vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo htmlspecialchars(JText::translate('VBOGRAPHTOTUNITSLBL') . ' ' . $stats['room_units']); ?>">
354 <span><?php echo $stats['rooms_booked']; ?></span>
355 </span>
356 </div>
357 </div>
358 </div>
359
360 <div class="vbo-widget-finance-data-block" data-typestat="cancellations_amt">
361 <div class="vbo-widget-finance-stat">
362 <div class="vbo-widget-finance-stat-info">
363 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBO_CANCELLATIONS'); ?></span>
364 <span class="vbo-widget-finance-stat-cmd"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
365 </div>
366 <div class="vbo-widget-finance-stat-amount">
367 <span class="vbo-widget-finance-stat-amount-value vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo $currencysymb . ' ' . VikBooking::numberFormat($stats['cancellations_amt']) . ($stats['tot_cancellations'] ? ' (' . $stats['tot_cancellations'] . ')' : ''); ?>">
368 <span class="vbo-currency"><?php echo $currencysymb; ?></span>
369 <span class="vbo-price"><?php echo $finance->numberFormatShort($stats['cancellations_amt']); ?></span>
370 </span>
371 </div>
372 </div>
373 </div>
374
375 </div>
376
377 <div class="vbo-widget-finance-data-block-rankings">
378
379 <div class="vbo-widget-finance-data-block-rank" data-typestat="country_ranks">
380 <div class="vbo-widget-finance-stat">
381 <div class="vbo-widget-finance-stat-info">
382 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBOSTATSTOPCOUNTRIES'); ?></span>
383 </div>
384 <div class="vbo-widget-finance-stat-ranks">
385 <?php
386 foreach ($stats['country_ranks'] as $country_rank) {
387 ?>
388 <div class="vbo-widget-finance-stat-rank">
389 <div class="vbo-widget-finance-stat-rank-logo">
390 <?php
391 if (is_file(VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'countries' . DIRECTORY_SEPARATOR . $country_rank['code'] . '.png')) {
392 ?>
393 <img src="<?php echo VBO_ADMIN_URI . 'resources/countries/' . $country_rank['code'] . '.png'; ?>" />
394 <?php
395 } else {
396 VikBookingIcons::e('globe');
397 }
398 ?>
399 </div>
400 <div class="vbo-widget-finance-stat-rank-score">
401 <div class="vbo-widget-finance-stat-rank-name">
402 <span><?php echo $country_rank['name']; ?></span>
403 </div>
404 <div class="vbo-widget-finance-stat-rank-amount">
405 <span class="vbo-currency"><?php echo $currencysymb; ?></span>
406 <span class="vbo-price vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo $currencysymb . ' ' . VikBooking::numberFormat($country_rank['revenue']); ?>"><?php echo $finance->numberFormatShort($country_rank['revenue']); ?></span>
407 </div>
408 <div class="vbo-widget-finance-stat-rank-pcent">
409 <progress class="vbo-widget-finance-progress" value="<?php echo $country_rank['pcent']; ?>" max="100"><?php echo $country_rank['pcent']; ?>%</progress>
410 </div>
411 </div>
412 </div>
413 <?php
414 }
415 ?>
416 </div>
417 </div>
418 </div>
419
420 <div class="vbo-widget-finance-data-block-rank" data-typestat="pos_revenue">
421 <div class="vbo-widget-finance-stat">
422 <div class="vbo-widget-finance-stat-info">
423 <span class="vbo-widget-finance-stat-name"><?php echo JText::translate('VBOCHANNELS'); ?></span>
424 </div>
425 <div class="vbo-widget-finance-stat-ranks">
426 <?php
427 foreach ($stats['pos_revenue'] as $pos_revenue) {
428 $say_pos_name = ucfirst($pos_revenue['name']);
429 ?>
430 <div class="vbo-widget-finance-stat-rank">
431 <div class="vbo-widget-finance-stat-rank-logo">
432 <?php
433 if (!empty($pos_revenue['logo'])) {
434 ?>
435 <img src="<?php echo $pos_revenue['logo']; ?>" />
436 <?php
437 // adjust channel name
438 $say_pos_name = strtolower($pos_revenue['name']) == 'airbnbapi' ? 'Airbnb' : $say_pos_name;
439 $say_pos_name = strtolower($pos_revenue['name']) == 'googlehotel' ? 'Google Hotel' : $say_pos_name;
440 } elseif (!strcasecmp($pos_revenue['name'], 'website')) {
441 VikBookingIcons::e('hotel');
442 // adjust channel name
443 $say_pos_name = JText::translate('VBORDFROMSITE');
444 } else {
445 VikBookingIcons::e('globe');
446 }
447 ?>
448 </div>
449 <div class="vbo-widget-finance-stat-rank-score">
450 <div class="vbo-widget-finance-stat-rank-name">
451 <span><?php echo $say_pos_name; ?></span>
452 </div>
453 <div class="vbo-widget-finance-stat-rank-amount">
454 <span class="vbo-currency"><?php echo $currencysymb; ?></span>
455 <span class="vbo-price vbo-tooltip vbo-tooltip-top" data-tooltiptext="<?php echo $currencysymb . ' ' . VikBooking::numberFormat($pos_revenue['revenue']); ?>"><?php echo $finance->numberFormatShort($pos_revenue['revenue']); ?></span>
456 </div>
457 <div class="vbo-widget-finance-stat-rank-pcent">
458 <progress class="vbo-widget-finance-progress" value="<?php echo $pos_revenue['pcent']; ?>" max="100"><?php echo $pos_revenue['pcent']; ?>%</progress>
459 </div>
460 </div>
461 </div>
462 <?php
463 }
464 ?>
465 </div>
466 </div>
467 </div>
468
469 </div>
470
471 <input type="hidden" class="vbo-widget-finance-json-stats" value="" />
472 <input type="hidden" class="vbo-widget-finance-stats-from" value="" />
473 <input type="hidden" class="vbo-widget-finance-stats-to" value="" />
474
475 <script type="text/javascript">
476 jQuery(function() {
477
478 let vbo_wfinance_compare_auto = jQuery('#<?php echo $wrapper; ?>').find('.vbo-finance-compare-auto').val();
479
480 let vbo_wfinance_ctx_btns = [
481 {
482 icon: '<?php echo VikBookingIcons::i('balance-scale-left'); ?>',
483 text: Joomla.JText._('<?php echo $js_step_comp_str; ?>'),
484 separator: false,
485 action: (root, config) => {
486 vboWidgetFinanceCompare('<?php echo $wrapper; ?>', '<?php echo $step; ?>');
487 },
488 },
489 {
490 icon: '<?php echo VikBookingIcons::i('balance-scale-right'); ?>',
491 text: Joomla.JText._('VBO_COMPARE_WITH_LAST_Y'),
492 separator: false,
493 action: (root, config) => {
494 vboWidgetFinanceCompare('<?php echo $wrapper; ?>', 'year');
495 },
496 },
497 ];
498
499 if (vbo_wfinance_compare_auto.length) {
500 vbo_wfinance_ctx_btns[1].separator = true;
501 vbo_wfinance_ctx_btns.push({
502 icon: '<?php echo VikBookingIcons::i('ban'); ?>',
503 text: Joomla.JText._((vbo_wfinance_compare_auto == 'year' ? 'VBO_COMPARE_WITH_LAST_Y' : '<?php echo $js_step_comp_str; ?>')),
504 class: 'vbo-context-menu-entry-danger',
505 separator: false,
506 action: (root, config) => {
507 // turn off the flag to automatically compare data
508 jQuery('#<?php echo $wrapper; ?>').find('.vbo-finance-compare-auto').val('');
509 // reload data
510 vboWidgetFinanceLoad('<?php echo $wrapper; ?>');
511 },
512 });
513 }
514
515 jQuery('#<?php echo $wrapper; ?>').find('.vbo-widget-finance-stat-cmd').vboContextMenu({
516 placement: 'bottom-right',
517 buttons: vbo_wfinance_ctx_btns,
518 });
519 });
520 </script>
521
522 <?php
523
524 // get the HTML buffer
525 $html_content = ob_get_contents();
526 ob_end_clean();
527
528 // take care of the readable dates interval
529 list($format_from_date, $format_to_date) = $this->parseReadableDateInterval($from_ts, $to_ts, $this->df, $step);
530
531 // return an associative array of values
532 return [
533 'html' => $html_content,
534 'fromdate' => $fromdate,
535 'f_fromdate' => $format_from_date,
536 'todate' => $todate,
537 'f_todate' => $format_to_date,
538 'step' => $step,
539 'step_name' => $step_name,
540 'stats' => $stats,
541 ];
542 }
543
544 /**
545 * Custom method for this widget only to load the comparison values.
546 * The method is called by the admin controller through an AJAX request.
547 * The visibility should be public, it should not exit the process, and
548 * any content sent to output will be returned to the AJAX response.
549 * In this case we return an array because this method requires "return":1.
550 *
551 * Returns the necessary HTML to display comparison values with past dates.
552 */
553 public function loadComparisonStats()
554 {
555 $app = JFactory::getApplication();
556
557 $wrapper = $app->input->getString('wrapper', '');
558
559 $fromdate = $app->input->getString('fromdate', '');
560 $todate = $app->input->getString('todate', '');
561 $step = $app->input->getString('step', 'quarter');
562 $prev_stats = $app->input->get('stats', [], 'array');
563 $date_dir = -1;
564
565 if (empty($fromdate) || empty($todate) || empty($prev_stats)) {
566 // make the AJAX request fail nicely
567 VBOHttpDocument::getInstance()->close(500, 'Missing data to calculate comparison values');
568 }
569
570 // convert dates to timestamps to support custom date formats
571 $from_ts = VikBooking::getDateTimestamp($fromdate);
572 $to_ts = VikBooking::getDateTimestamp($todate, 23, 59, 59);
573
574 // stats calculation type
575 $calc_type = 'stay_dates';
576 if (!strcasecmp($step, 'booking_dates')) {
577 $calc_type = 'booking_dates';
578 }
579
580 // calculate backward dates for comparison
581 list($from_ts, $to_ts, $step_name) = $this->calcDatesNavigation($from_ts, $to_ts, $step, $date_dir);
582
583 // convert final dates to Y-m-d
584 $fromdate = date('Y-m-d', $from_ts);
585 $todate = date('Y-m-d', $to_ts);
586
587 // access the finance helper object
588 $finance = VBOTaxonomyFinance::getInstance();
589
590 // currency symbol
591 $currencysymb = VikBooking::getCurrencySymb();
592
593 // get the financial stats for the requested dates and use them for comparison
594 try {
595 $compare_stats = $finance->getStats($fromdate, $todate, [], $calc_type);
596 } catch (Exception $e) {
597 // make the AJAX request fail nicely
598 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
599 }
600
601 // define the "compare vs" string
602 switch ($step) {
603 case 'year':
604 $compare_vs_str = JText::translate('VBO_VS_LAST_Y');
605 break;
606 case 'quarter':
607 $compare_vs_str = JText::translate('VBO_VS_PREV_Q');
608 break;
609 case 'month':
610 $compare_vs_str = JText::translate('VBO_VS_PREV_M');
611 break;
612 case 'week':
613 $compare_vs_str = JText::translate('VBO_VS_PREV_W');
614 break;
615 case 'booking_dates':
616 $compare_vs_str = JText::translate('VBO_VS_PREV_D');
617 break;
618 default:
619 $compare_vs_str = '';
620 break;
621 }
622
623 // build comparison values
624 $comparison = [];
625
626 // stats that require percent values for comparison
627 $pcent_stats = [
628 'gross_revenue' => [],
629 'taxes' => [],
630 'cmms' => [
631 'reverse' => 1,
632 ],
633 'revenue' => [],
634 'ibe_revenue' => [],
635 'ota_revenue' => [],
636 'ota_avg_cmms' => [
637 'reverse' => 1,
638 'no_pcent' => 1,
639 'fixednum' => 1,
640 ],
641 'cmm_savings' => [],
642 'tot_bookings' => [
643 'fixednum' => 1,
644 ],
645 'nights_booked' => [
646 'fixednum' => 1,
647 ],
648 'avg_los' => [
649 'fixednum' => 1,
650 ],
651 'abw' => [
652 'fixednum' => 1,
653 ],
654 'rooms_booked' => [
655 'fixednum' => 1,
656 ],
657 'cancellations_amt' => [
658 'reverse' => 1,
659 ],
660 ];
661
662 foreach ($pcent_stats as $stat_name => $stat_opt) {
663 if (!isset($compare_stats[$stat_name])) {
664 continue;
665 }
666
667 // make sure the previous value is set
668 $prev_stats[$stat_name] = !isset($prev_stats[$stat_name]) ? 0 : $prev_stats[$stat_name];
669
670 // difference
671 $diff = isset($stat_opt['reverse']) ? ($compare_stats[$stat_name] - $prev_stats[$stat_name]) : ($prev_stats[$stat_name] - $compare_stats[$stat_name]);
672
673 // calculate values
674 $comparison[$stat_name] = [
675 'amount' => $compare_stats[$stat_name],
676 'amount_f' => VikBooking::numberFormat($compare_stats[$stat_name]),
677 'amount_s' => $finance->numberFormatShort($compare_stats[$stat_name]),
678 'diff' => $diff,
679 'diff_f' => VikBooking::numberFormat($diff),
680 'diff_s' => $finance->numberFormatShort($diff),
681 'pcent' => isset($stat_opt['no_pcent']) ? null : $finance->calcAbsPercent($prev_stats[$stat_name], $compare_stats[$stat_name]),
682 'reverse' => $stat_opt['reverse'] ?? false,
683 ];
684
685 // inject property for fixed number with no currency
686 if (isset($stat_opt['fixednum']) && $stat_opt['fixednum']) {
687 $comparison[$stat_name]['fixednum'] = $stat_opt['fixednum'];
688 }
689 }
690
691 // return an associative array of values
692 return [
693 'no_data' => ($compare_stats['tot_bookings'] < 1 ? 1 : 0),
694 'compare' => $comparison,
695 'vs_str' => $compare_vs_str,
696 'currency' => $currencysymb,
697 'stats' => $compare_stats,
698 ];
699 }
700
701 /**
702 * Custom method for this widget only to retrieve the daily
703 * welcome statistics about the bookings of the day before.
704 *
705 * @return array the associative list of statistics.
706 */
707 public function getWelcomeStats()
708 {
709 // get yesterday's date
710 $yesterday = date('Y-m-d', strtotime('yesterday'));
711
712 // access the finance helper object
713 $finance = VBOTaxonomyFinance::getInstance();
714
715 // get the financial stats for the requested dates
716 try {
717 $stats = $finance->getStats($yesterday, $yesterday, $rooms = [], $type = 'booking_dates');
718 } catch (Exception $e) {
719 // make the AJAX request fail nicely
720 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
721 }
722
723 return $stats;
724 }
725
726
727 /**
728 * Main method to invoke the widget. Contents will be loaded
729 * through AJAX requests, not via PHP when the page loads.
730 *
731 * @param VBOMultitaskData $data
732 *
733 * @return void
734 */
735 public function render(VBOMultitaskData $data = null)
736 {
737 // increase widget's instance counter
738 static::$instance_counter++;
739
740 // check whether the widget is being rendered via AJAX when adding it through the customizer
741 $is_ajax = $this->isAjaxRendering();
742
743 // generate a unique ID for the sticky notes wrapper instance
744 $wrapper_instance = !$is_ajax ? static::$instance_counter : rand();
745 $wrapper_id = 'vbo-widget-finance-' . $wrapper_instance;
746
747 // check permissions
748 $vbo_auth_pricing = JFactory::getUser()->authorise('core.vbo.pricing', 'com_vikbooking');
749 $vbo_auth_bookings = JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking');
750 if (!$vbo_auth_pricing || !$vbo_auth_bookings) {
751 // base permissions are not met
752 return;
753 }
754
755 // we make use of the rates flow and occupancy ranking report helper classes
756 $report = VikBooking::getReportInstance('rates_flow');
757 if (!$report) {
758 // display nothing
759 return;
760 }
761
762 // get minimum and maximum nights updated for dates filters
763 list($mindate, $maxdate) = $this->getMinDatesFinance();
764 $mindate = empty($mindate) ? time() : $mindate;
765 $maxdate = empty($maxdate) ? $mindate : $maxdate;
766
767 // dates navigation preference
768 $cookie = JFactory::getApplication()->input->cookie;
769 $cookie_step = $cookie->get('vbo_widget_finance_step', 'quarter', 'string');
770
771 // determine default period name and dates
772 $df = $report->getDateFormat();
773 $dtpicker_df = $report->getDateFormat('jui');
774 $now_info = getdate();
775 if ($cookie_step == 'week') {
776 // next week
777 $from_ts = mktime(0, 0, 0, $now_info['mon'], $now_info['mday'], $now_info['year']);
778 $to_ts = mktime(0, 0, 0, $now_info['mon'], ($now_info['mday'] + 7), $now_info['year']);
779 $fromdate = date($df, $from_ts);
780 $todate = date($df, $to_ts);
781 $period_name = JText::translate('VBOWEEK');
782 } elseif ($cookie_step == 'month') {
783 // current full month
784 $from_ts = mktime(0, 0, 0, $now_info['mon'], 1, $now_info['year']);
785 $fromdate = date($df, $from_ts);
786 $to_ts = mktime(23, 59, 59, $now_info['mon'], date('t', $now_info[0]), $now_info['year']);
787 $todate = date($df, $to_ts);
788 $period_name = JText::translate('VBPVIEWRESTRICTIONSTWO');
789 } else {
790 // default to next 3 (total) months from today's month
791 $from_ts = mktime(0, 0, 0, $now_info['mon'], 1, $now_info['year']);
792 $fromdate = date($df, $from_ts);
793 $end_info = getdate(mktime(0, 0, 0, ($now_info['mon'] + 2), 1, $now_info['year']));
794 $to_ts = mktime(23, 59, 59, $end_info['mon'], date('t', $end_info[0]), $end_info['year']);
795 $todate = date($df, $to_ts);
796 $period_name = JText::translate('VBO_QUARTER');
797 }
798
799 // check multitask data
800 $rooms_filtered = [];
801 if ($data) {
802 $inj_fromdate = $data->get('fromdate', $this->options()->get('fromdate', ''));
803 $inj_todate = $data->get('todate', $this->options()->get('todate', ''));
804 $inj_type = $data->get('type', $this->options()->get('type', ''));
805
806 // check if some room IDs were filtered
807 $rooms_filtered = (array) $data->get('room_ids', $this->options()->get('room_ids', []));
808
809 if (preg_match("/^\d{4}-\d{2}-\d{2}$/", $inj_fromdate) && preg_match("/^\d{4}-\d{2}-\d{2}$/", $inj_todate)) {
810 // valid dates injected in Y-m-d format
811 $fromdate = $inj_fromdate;
812 $todate = $inj_todate;
813 $to_info = getdate(strtotime($todate));
814 $from_ts = strtotime($fromdate);
815 $to_ts = mktime(23, 59, 59, $to_info['mon'], $to_info['mday'], $to_info['year']);
816 if (!empty($inj_type)) {
817 $cookie_step = $inj_type;
818 if (!strcasecmp($inj_type, 'booking_dates')) {
819 $period_name = JText::translate('VBOSTATSMODETS');
820 }
821 }
822 }
823 }
824
825 // take care of the readable dates interval
826 list($format_from_date, $format_to_date) = $this->parseReadableDateInterval($from_ts, $to_ts, $df, $cookie_step);
827 $interval_name = $format_from_date . ' - ' . $format_to_date;
828 if ($format_from_date == $format_to_date) {
829 $interval_name = $format_from_date;
830 }
831
832 ?>
833 <div id="<?php echo $wrapper_id; ?>" class="vbo-admin-widget-wrapper" data-instance="<?php echo $wrapper_instance; ?>">
834 <div class="vbo-admin-widget-head">
835 <div class="vbo-admin-widget-head-inline">
836 <h4><?php echo $this->widgetIcon; ?> <span><?php echo $this->widgetName; ?></span></h4>
837 <div class="vbo-admin-widget-head-commands">
838
839 <div class="vbo-reportwidget-commands">
840 <div class="vbo-reportwidget-commands-main">
841 <div class="vbo-reportwidget-command-dates">
842 <div class="vbo-reportwidget-period-name"><?php echo $period_name; ?></div>
843 <div class="vbo-reportwidget-period-date"><?php echo $interval_name; ?></div>
844 </div>
845 <div class="vbo-reportwidget-command-chevron vbo-reportwidget-command-prev">
846 <span class="vbo-widget-finance-dt-prev" onclick="vboWidgetFinanceDatesNav('<?php echo $wrapper_id; ?>', -1);"><?php VikBookingIcons::e('chevron-left'); ?></span>
847 </div>
848 <div class="vbo-reportwidget-command-chevron vbo-reportwidget-command-next">
849 <span class="vbo-widget-finance-dt-next" onclick="vboWidgetFinanceDatesNav('<?php echo $wrapper_id; ?>', 1);"><?php VikBookingIcons::e('chevron-right'); ?></span>
850 </div>
851 </div>
852 <div class="vbo-reportwidget-command-dots">
853 <span class="vbo-widget-command-togglefilters vbo-widget-finance-togglefilters" onclick="vboWidgetFinanceToggleFilters('<?php echo $wrapper_id; ?>');"><?php VikBookingIcons::e('ellipsis-v'); ?></span>
854 </div>
855 </div>
856 <div class="vbo-reportwidget-filters">
857 <div class="vbo-reportwidget-filter">
858 <span class="vbo-reportwidget-datepicker">
859 <?php VikBookingIcons::e('calendar', 'vbo-widget-finance-caltrigger'); ?>
860 <input type="text" class="vbo-finance-dtpicker-from" value="<?php echo $fromdate; ?>" />
861 </span>
862 </div>
863 <div class="vbo-reportwidget-filter">
864 <span class="vbo-reportwidget-datepicker">
865 <?php VikBookingIcons::e('calendar', 'vbo-widget-finance-caltrigger'); ?>
866 <input type="text" class="vbo-finance-dtpicker-to" value="<?php echo $todate; ?>" />
867 </span>
868 </div>
869 <div class="vbo-reportwidget-filter">
870 <select class="vbo-finance-period-nav" onchange="vboWidgetFinanceChangePeriod(this.value);">
871 <option value="week"<?php echo $cookie_step == 'week' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBOWEEK'); ?></option>
872 <option value="month"<?php echo $cookie_step == 'month' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBPVIEWRESTRICTIONSTWO'); ?></option>
873 <option value="quarter"<?php echo $cookie_step == 'quarter' ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBO_QUARTER'); ?></option>
874 <option value="booking_dates"<?php echo !strcasecmp($cookie_step, 'booking_dates') ? ' selected="selected"' : ''; ?>><?php echo JText::translate('VBPCHOOSEBUSYORDATE'); ?></option>
875 </select>
876 </div>
877 <div class="vbo-reportwidget-filter vbo-widget-finance-rooms-filter-wrap">
878 <select class="vbo-widget-finance-rooms-filter" multiple="multiple">
879 <?php
880 foreach ($rooms_filtered as $room_filtered) {
881 if ($room_info = VikBooking::getRoomInfo($room_filtered, ['id', 'name'], true)) {
882 ?>
883 <option value="<?php echo $room_info['id']; ?>" selected="selected"><?php echo $room_info['name']; ?></option>
884 <?php
885 }
886 }
887 ?>
888 </select>
889 </div>
890 <div class="vbo-reportwidget-filter vbo-reportwidget-filter-confirm">
891 <input type="hidden" class="vbo-finance-compare-auto" value="" />
892 <button type="button" class="btn vbo-config-btn" onclick="vboWidgetFinanceChangeDates('<?php echo $wrapper_id; ?>');"><?php echo JText::translate('VBADMINNOTESUPD'); ?></button>
893 </div>
894 </div>
895
896 </div>
897 </div>
898 </div>
899 <div class="vbo-widget-finance-wrap">
900 <div class="vbo-widget-finance-inner">
901 <div class="vbo-widget-finance-list">
902 <div class="vbo-widget-finance-skeleton-blocks">
903 <?php
904 // display a few loading skeletons
905 for ($i = 0; $i < 10; $i++) {
906 ?>
907 <div class="vbo-widget-finance-skeleton-block">
908 <div class="vbo-widget-finance-skeleton-top">
909 <div class="vbo-skeleton-loading vbo-skeleton-loading-finance-top"></div>
910 </div>
911 <div class="vbo-widget-finance-skeleton-bottom">
912 <div class="vbo-skeleton-loading vbo-skeleton-loading-finance-bottom"></div>
913 </div>
914 </div>
915 <?php
916 }
917 ?>
918 </div>
919 </div>
920 </div>
921 </div>
922 </div>
923 <?php
924
925 if (static::$instance_counter === 0 || $is_ajax) {
926 /**
927 * Print the JS code only once for all instances of this widget.
928 * The real rendering is made through AJAX, not when the page loads.
929 */
930 ?>
931 <script type="text/javascript">
932
933 /**
934 * Display the loading skeletons.
935 */
936 function vboWidgetFinanceSkeletons(wrapper) {
937 var widget_instance = jQuery('#' + wrapper);
938 if (!widget_instance.length) {
939 return false;
940 }
941 widget_instance.find('.vbo-widget-finance-list').html('');
942 var skeleton = '';
943 skeleton += '<div class="vbo-widget-finance-skeleton-blocks">' + "\n";
944 for (var i = 0; i < 10; i++) {
945 skeleton += '<div class="vbo-widget-finance-skeleton-block">';
946 skeleton += ' <div class="vbo-widget-finance-skeleton-top">';
947 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-finance-top"></div>';
948 skeleton += ' </div>';
949 skeleton += ' <div class="vbo-widget-finance-skeleton-bottom">';
950 skeleton += ' <div class="vbo-skeleton-loading vbo-skeleton-loading-finance-bottom"></div>';
951 skeleton += ' </div>';
952 skeleton += '</div>' + "\n";
953 }
954 skeleton += '</div>' + "\n";
955 // append skeletons
956 jQuery(skeleton).appendTo(widget_instance.find('.vbo-widget-finance-list'));
957 }
958
959 /**
960 * Perform the request to load the financial stats.
961 */
962 function vboWidgetFinanceLoad(wrapper, dates_direction) {
963 var widget_instance = jQuery('#' + wrapper);
964 if (!widget_instance.length) {
965 return false;
966 }
967
968 // check if a navigation of dates was requested (0 = no dates nav)
969 if (typeof dates_direction === 'undefined') {
970 dates_direction = 0;
971 }
972
973 // get vars for making the request
974 var from_date = widget_instance.find('.vbo-finance-dtpicker-from').val();
975 var to_date = widget_instance.find('.vbo-finance-dtpicker-to').val();
976 var dates_step = widget_instance.find('.vbo-finance-period-nav').val();
977 var room_ids = widget_instance.find('select.vbo-widget-finance-rooms-filter').val();
978 var auto_compare = widget_instance.find('.vbo-finance-compare-auto').val();
979
980 // the widget method to call
981 var call_method = 'loadFinancialStats';
982
983 // make a request to load the financial stats
984 VBOCore.doAjax(
985 "<?php echo $this->getExecWidgetAjaxUri(); ?>",
986 {
987 widget_id: "<?php echo $this->getIdentifier(); ?>",
988 call: call_method,
989 return: 1,
990 fromdate: from_date,
991 todate: to_date,
992 step: dates_step,
993 room_ids: room_ids,
994 date_dir: dates_direction,
995 wrapper: wrapper,
996 tmpl: "component"
997 },
998 (response) => {
999 try {
1000 var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
1001 if (!obj_res.hasOwnProperty(call_method)) {
1002 console.error('Unexpected JSON response', obj_res);
1003 return false;
1004 }
1005
1006 // replace HTML with new stats
1007 widget_instance.find('.vbo-widget-finance-list').html(obj_res[call_method]['html']);
1008
1009 // populate JSON data for comparison
1010 widget_instance.find('input.vbo-widget-finance-json-stats').val(JSON.stringify(obj_res[call_method]['stats']));
1011 widget_instance.find('input.vbo-widget-finance-stats-from').val(obj_res[call_method]['fromdate']);
1012 widget_instance.find('input.vbo-widget-finance-stats-to').val(obj_res[call_method]['todate']);
1013
1014 if (dates_direction > 0 || dates_direction < 0) {
1015 // set new dates calculated - note: if maxDate or minDate is exceeded, the datepicker calendars won't update!
1016 try {
1017 // construct a solid date object without using the raw Y-m-d string as only argument
1018 var dfrom_parts = obj_res[call_method]['fromdate'].split('-');
1019 var dfrom_obj = new Date(parseInt(dfrom_parts[0]), (parseInt(dfrom_parts[1]) - 1), parseInt(dfrom_parts[2]), 0, 0, 0);
1020 widget_instance.find('.vbo-finance-dtpicker-from').datepicker('setDate', dfrom_obj);
1021
1022 // restore the original minimum and maximum dates for the "to date" calendar to avoid issues setting a date
1023 widget_instance.find('.vbo-finance-dtpicker-to').datepicker('option', {
1024 minDate: "<?php echo date($df, $mindate); ?>",
1025 maxDate: "<?php echo date($df, $maxdate); ?>",
1026 });
1027
1028 // do the same for the to date by using a precise date object instance
1029 var dto_parts = obj_res[call_method]['todate'].split('-');
1030 var dto_obj = new Date(parseInt(dto_parts[0]), (parseInt(dto_parts[1]) - 1), parseInt(dto_parts[2]), 0, 0, 0);
1031 widget_instance.find('.vbo-finance-dtpicker-to').datepicker('setDate', dto_obj);
1032 } catch(e) {
1033 // just log the error
1034 console.error(e);
1035 }
1036 }
1037
1038 // update step name
1039 if (obj_res[call_method]['step_name'] && obj_res[call_method]['step_name'].length) {
1040 widget_instance.find('.vbo-reportwidget-period-name').text(obj_res[call_method]['step_name']);
1041 }
1042
1043 // update readable dates interval
1044 var readable_period = obj_res[call_method]['f_fromdate'] + ' - ' + obj_res[call_method]['f_todate'];
1045 if (obj_res[call_method]['f_fromdate'] == obj_res[call_method]['f_todate']) {
1046 // a full month does not need an interval
1047 readable_period = obj_res[call_method]['f_fromdate'];
1048 }
1049 widget_instance.find('.vbo-reportwidget-period-date').text(readable_period);
1050
1051 if (auto_compare.length) {
1052 // trigger the stats comparison
1053 setTimeout(() => {
1054 vboWidgetFinanceCompare(wrapper, (auto_compare == 'year' ? 'year' : dates_step));
1055 }, 400);
1056 }
1057 } catch(err) {
1058 console.error('could not parse JSON response', err, response);
1059 }
1060 },
1061 (error) => {
1062 // remove the skeleton loading
1063 widget_instance.find('.vbo-widget-finance-list').find('.vbo-widget-finance-skeleton-blocks').remove();
1064 console.error(error);
1065 alert(error.responseText);
1066 }
1067 );
1068 }
1069
1070 /**
1071 * Load stats comparison values.
1072 */
1073 function vboWidgetFinanceCompare(wrapper, period) {
1074 var widget_instance = jQuery('#' + wrapper);
1075 if (!widget_instance.length) {
1076 return false;
1077 }
1078
1079 // append loading elements
1080 widget_instance.find('.vbo-widget-finance-data-blocks').find('.vbo-widget-finance-stat').each(function(k, v) {
1081 if (jQuery(this).find('.vbo-widget-finance-stat-compare').length) {
1082 jQuery(this).find('.vbo-widget-finance-stat-compare').remove();
1083 }
1084 var comparison_el = jQuery('<div></div>').addClass('vbo-widget-finance-stat-compare');
1085 var loading_el = jQuery('<div></div>').addClass('vbo-widget-finance-compare-amount');
1086 loading_el.append('<i class="<?php echo VikBookingIcons::i('circle-notch', 'fa-spin fa-fw'); ?>"></i>');
1087 comparison_el.append(loading_el);
1088 jQuery(this).append(comparison_el);
1089 });
1090
1091 // get vars for making the request
1092 var prev_stats = JSON.parse(widget_instance.find('.vbo-widget-finance-json-stats').val());
1093 var from_date = widget_instance.find('.vbo-widget-finance-stats-from').val();
1094 var to_date = widget_instance.find('.vbo-widget-finance-stats-to').val();
1095
1096 // the widget method to call
1097 var call_method = 'loadComparisonStats';
1098
1099 // make a request to load the comparison values
1100 VBOCore.doAjax(
1101 "<?php echo $this->getExecWidgetAjaxUri(); ?>",
1102 {
1103 widget_id: "<?php echo $this->getIdentifier(); ?>",
1104 call: call_method,
1105 return: 1,
1106 fromdate: from_date,
1107 todate: to_date,
1108 step: period,
1109 stats: prev_stats,
1110 wrapper: wrapper,
1111 tmpl: "component"
1112 },
1113 (response) => {
1114 try {
1115 var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
1116 if (!obj_res.hasOwnProperty(call_method)) {
1117 console.error('Unexpected JSON response', obj_res);
1118 return false;
1119 }
1120
1121 // turn on the flag to automatically compare data
1122 widget_instance.find('.vbo-finance-compare-auto').val(period);
1123
1124 if (obj_res[call_method]['no_data']) {
1125 // no data available for comparison
1126 widget_instance.find('.vbo-widget-finance-data-blocks').find('.vbo-widget-finance-stat').each(function(k, v) {
1127 if (jQuery(this).find('.vbo-widget-finance-stat-compare').length) {
1128 jQuery(this).find('.vbo-widget-finance-stat-compare').remove();
1129 }
1130 let comparison_el = jQuery('<div></div>').addClass('vbo-widget-finance-stat-compare');
1131 let amount_el = jQuery('<div></div>').addClass('vbo-widget-finance-compare-amount');
1132 comparison_el.append(amount_el);
1133 let pcent_el = jQuery('<div></div>').addClass('vbo-widget-finance-compare-pcent');
1134 pcent_el.append('<span></span>').addClass('vbo-widget-finance-compare-txt').text(Joomla.JText._('VBNOTRACKINGS'));
1135 comparison_el.append(pcent_el);
1136 jQuery(this).append(comparison_el);
1137 });
1138
1139 // do not proceed
1140 return;
1141 }
1142
1143 // parse all comparison values
1144 for (let stat_name in obj_res[call_method]['compare']) {
1145 if (!obj_res[call_method]['compare'].hasOwnProperty(stat_name)) {
1146 continue;
1147 }
1148
1149 let stat_elem = widget_instance.find('.vbo-widget-finance-data-block[data-typestat="' + stat_name + '"]');
1150 if (!stat_elem || !stat_elem.length) {
1151 continue;
1152 }
1153
1154 // clean up loading element
1155 if (stat_elem.find('.vbo-widget-finance-stat-compare').length) {
1156 stat_elem.find('.vbo-widget-finance-stat-compare').remove();
1157 }
1158
1159 // check if currency is needed
1160 let use_currency = (!obj_res[call_method]['compare'][stat_name].hasOwnProperty('fixednum') || !obj_res[call_method]['compare'][stat_name]['fixednum']);
1161
1162 // the new comparison element
1163 let comparison_el = jQuery('<div></div>').addClass('vbo-widget-finance-stat-compare');
1164
1165 // amount block
1166 let amount_block = jQuery('<div></div>').addClass('vbo-widget-finance-compare-amount');
1167 let amount_value = jQuery('<span></span>').addClass('vbo-widget-finance-compare-amount-value');
1168 if (use_currency) {
1169 amount_value.addClass('vbo-tooltip vbo-tooltip-top').attr('data-tooltiptext', obj_res[call_method]['currency'] + ' ' + obj_res[call_method]['compare'][stat_name]['amount_f']);
1170 amount_value.append('<span class="vbo-currency">' + obj_res[call_method]['currency'] + '</span>');
1171 }
1172 if (obj_res[call_method]['compare'][stat_name]['pcent'] != null) {
1173 amount_value.append('<span class="vbo-price">' + obj_res[call_method]['compare'][stat_name]['amount_s'] + '</span>');
1174 } else {
1175 amount_value.append('<span class="vbo-price">' + obj_res[call_method]['compare'][stat_name]['amount'] + '%</span>');
1176 }
1177 amount_block.append(amount_value);
1178
1179 // percent block
1180 let pcent_block = jQuery('<div></div>').addClass('vbo-widget-finance-compare-pcent');
1181 let pcent_icon = '';
1182 if (obj_res[call_method]['compare'][stat_name]['diff'] == 0) {
1183 pcent_block.addClass('vbo-widget-finance-compare-pcent-equal');
1184 pcent_icon = '<?php VikBookingIcons::e('equals') ?>';
1185 } else if (obj_res[call_method]['compare'][stat_name]['diff'] > 0) {
1186 pcent_block.addClass('vbo-widget-finance-compare-pcent-up');
1187 pcent_icon = obj_res[call_method]['compare'][stat_name]['reverse'] ? '<?php VikBookingIcons::e('arrow-down') ?>' : '<?php VikBookingIcons::e('arrow-up') ?>';
1188 } else if (obj_res[call_method]['compare'][stat_name]['diff'] < 0) {
1189 pcent_block.addClass('vbo-widget-finance-compare-pcent-down');
1190 pcent_icon = obj_res[call_method]['compare'][stat_name]['reverse'] ? '<?php VikBookingIcons::e('arrow-up') ?>' : '<?php VikBookingIcons::e('arrow-down') ?>';
1191 }
1192 if (obj_res[call_method]['compare'][stat_name]['pcent'] != null) {
1193 pcent_block.append('<span class="vbo-widget-finance-compare-val">' + pcent_icon + ' ' + obj_res[call_method]['compare'][stat_name]['pcent'] + '%</span>');
1194 }
1195 pcent_block.append('<span class="vbo-widget-finance-compare-txt">' + obj_res[call_method]['vs_str'] + '</span>');
1196
1197 // append amount block
1198 comparison_el.append(amount_block);
1199 // append percent block
1200 comparison_el.append(pcent_block);
1201
1202 // append comparison element
1203 stat_elem.find('.vbo-widget-finance-stat').append(comparison_el);
1204 }
1205 } catch(err) {
1206 console.error('could not parse JSON response', err, response);
1207 }
1208 },
1209 (error) => {
1210 // remove loading icons
1211 widget_instance.find('.vbo-widget-finance-data-blocks').find('.vbo-widget-finance-stat-compare').remove();
1212 console.error(error);
1213 alert(error.responseText);
1214 }
1215 );
1216
1217 }
1218
1219 /**
1220 * Navigate between the date steps and load the stats.
1221 */
1222 function vboWidgetFinanceDatesNav(wrapper, direction) {
1223 var widget_instance = jQuery('#' + wrapper);
1224 if (!widget_instance.length) {
1225 return false;
1226 }
1227
1228 // show loading skeletons
1229 vboWidgetFinanceSkeletons(wrapper);
1230
1231 // launch dates navigation and load records
1232 vboWidgetFinanceLoad(wrapper, direction);
1233 }
1234
1235 /**
1236 * Load stats for the selected dates.
1237 */
1238 function vboWidgetFinanceChangeDates(wrapper) {
1239 var widget_instance = jQuery('#' + wrapper);
1240 if (!widget_instance.length) {
1241 return false;
1242 }
1243
1244 // hide filters when making a new request
1245 widget_instance.find('.vbo-reportwidget-filters').hide();
1246
1247 // show loading skeletons
1248 vboWidgetFinanceSkeletons(wrapper);
1249
1250 // load data
1251 vboWidgetFinanceLoad(wrapper);
1252 }
1253
1254 /**
1255 * Toggle dates and step filters.
1256 */
1257 function vboWidgetFinanceToggleFilters(wrapper) {
1258 var widget_instance = jQuery('#' + wrapper);
1259 if (!widget_instance.length) {
1260 return false;
1261 }
1262
1263 widget_instance.find('.vbo-reportwidget-filters').toggle();
1264 }
1265
1266 /**
1267 * Datepicker dates selection.
1268 */
1269 function vboWidgetFinanceCheckDates(selectedDate, inst) {
1270 if (selectedDate === null || inst === null) {
1271 return;
1272 }
1273 var cur_from_date = jQuery(this).val();
1274 if (jQuery(this).hasClass("vbo-finance-dtpicker-from") && cur_from_date.length) {
1275 var nowstart = jQuery(this).datepicker("getDate");
1276 var nowstartdate = new Date(nowstart.getTime());
1277 jQuery(".vbo-finance-dtpicker-to").datepicker("option", {minDate: nowstartdate});
1278 }
1279 }
1280
1281 /**
1282 * Navigation period cookie onchange.
1283 */
1284 function vboWidgetFinanceChangePeriod(period) {
1285 // always exclude "booking_dates"
1286 if (period && period === 'booking_dates') {
1287 return;
1288 }
1289 // update cookie for the step selected
1290 var nd = new Date();
1291 nd.setTime(nd.getTime() + (365*24*60*60*1000));
1292 document.cookie = "vbo_widget_finance_step=" + period + "; expires=" + nd.toUTCString() + "; path=/; SameSite=Lax";
1293 }
1294
1295 </script>
1296 <?php
1297 }
1298 ?>
1299
1300 <script type="text/javascript">
1301
1302 jQuery(function() {
1303
1304 // render datepicker calendars for dates navigation
1305 jQuery('#<?php echo $wrapper_id; ?>').find('.vbo-finance-dtpicker-from, .vbo-finance-dtpicker-to').datepicker({
1306 minDate: "<?php echo date($df, $mindate); ?>",
1307 maxDate: "<?php echo date($df, $maxdate); ?>",
1308 yearRange: "<?php echo date('Y', $mindate); ?>:<?php echo date('Y', $maxdate); ?>",
1309 changeMonth: true,
1310 changeYear: true,
1311 dateFormat: "<?php echo $dtpicker_df; ?>",
1312 onSelect: vboWidgetFinanceCheckDates
1313 });
1314
1315 // triggering for datepicker calendar icons
1316 jQuery('i.vbo-widget-finance-caltrigger').click(function() {
1317 var jdp = jQuery(this).parent().find('input.hasDatepicker');
1318 if (jdp.length) {
1319 jdp.focus();
1320 }
1321 });
1322
1323 // register room filters search via AJAX
1324 jQuery('#<?php echo $wrapper_id; ?>').find('select.vbo-widget-finance-rooms-filter').select2({
1325 placeholder: Joomla.JText._('VBROOMFILTER'),
1326 language: {
1327 errorLoading: () => {
1328 return Joomla.JText._('VBO_ERR_LOAD_RESULTS');
1329 },
1330 noResults: () => {
1331 return Joomla.JText._('VBNOROOMSFOUND');
1332 },
1333 searching: () => {
1334 return Joomla.JText._('VBO_SEARCHING');
1335 },
1336 },
1337 ajax: {
1338 delay: 350,
1339 url: "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=bookings.rooms_search'); ?>",
1340 dataType: 'json',
1341 }
1342 });
1343
1344 // when document is ready, load stats for this widget's instance
1345 vboWidgetFinanceLoad('<?php echo $wrapper_id; ?>');
1346
1347 });
1348
1349 </script>
1350
1351 <?php
1352 }
1353
1354 /**
1355 * Returns an array with the minimum and maximum dates booked.
1356 *
1357 * @return array to be used with list() to get the min/max stay date timestamps.
1358 */
1359 protected function getMinDatesFinance()
1360 {
1361 $dbo = JFactory::getDbo();
1362
1363 $mindate = null;
1364 $maxdate = null;
1365
1366 $q = "SELECT MIN(`checkin`) AS `mindate`, MAX(`checkout`) AS `maxdate` FROM `#__vikbooking_orders`
1367 WHERE `status` = 'confirmed' AND `closure` = 0";
1368 $dbo->setQuery($q);
1369 $data = $dbo->loadAssoc();
1370 if ($data) {
1371 if (!empty($data['mindate']) && !empty($data['maxdate'])) {
1372 $mindate = $data['mindate'];
1373 $maxdate = $data['maxdate'];
1374 }
1375 }
1376
1377 return [$mindate, $maxdate];
1378 }
1379
1380 /**
1381 * Calculates the new dates interval for navigation according to step.
1382 * Internal method for this widget only.
1383 *
1384 * @param int $from_ts current from date timestamp.
1385 * @param int $to_ts current end date timestamp.
1386 * @param string $step the navigation step to take (week, month, quarter or booking_dates).
1387 * @param int $date_dir less than 0 backward nav, more than 0 forward.
1388 *
1389 * @return array the new from and end date timestamps, plus the step name.
1390 */
1391 protected function calcDatesNavigation($from_ts, $to_ts, $step, $date_dir)
1392 {
1393 if (empty($from_ts) || (!($date_dir > 0) && !($date_dir < 0))) {
1394 return [$from_ts, $to_ts, ''];
1395 }
1396
1397 // start/end date information
1398 $from_info = getdate($from_ts);
1399 $to_info = getdate($to_ts);
1400
1401 // next or prev day (booking_dates)
1402 if (!strcasecmp($step, 'booking_dates')) {
1403 if ($date_dir > 0) {
1404 // forward dates navigation
1405 $from_ts = mktime(0, 0, 0, $to_info['mon'], ($to_info['mday'] + 1), $to_info['year']);
1406 $to_ts = mktime(23, 59, 59, $to_info['mon'], ($to_info['mday'] + 1), $to_info['year']);
1407 } elseif ($date_dir < 0) {
1408 // backward dates navigation
1409 $from_ts = mktime(0, 0, 0, $from_info['mon'], ($from_info['mday'] - 1), $from_info['year']);
1410 $to_ts = mktime(23, 59, 59, $from_info['mon'], ($from_info['mday'] - 1), $from_info['year']);
1411 }
1412
1413 return [$from_ts, $to_ts, JText::translate('VBPCHOOSEBUSYORDATE')];
1414 }
1415
1416 // next or prev week
1417 if ($step == 'week') {
1418 if ($date_dir > 0) {
1419 // forward dates navigation
1420 $from_ts = $to_ts;
1421 $to_ts = strtotime("+1 week", $to_ts);
1422 } elseif ($date_dir < 0) {
1423 // backward dates navigation
1424 $to_ts = $from_ts;
1425 $from_ts = strtotime("-1 week", $from_ts);
1426 }
1427
1428 return [$from_ts, $to_ts, JText::translate('VBOWEEK')];
1429 }
1430
1431 // next or prev month
1432 if ($step == 'month') {
1433 if ($date_dir > 0) {
1434 // forward dates navigation
1435 $from_ts = mktime(0, 0, 0, ($from_info['mon'] + 1), 1, $from_info['year']);
1436 $to_ts = mktime(0, 0, 0, ($from_info['mon'] + 1), date('t', $from_ts), $from_info['year']);
1437 } elseif ($date_dir < 0) {
1438 // backward dates navigation
1439 $from_ts = mktime(0, 0, 0, ($from_info['mon'] - 1), 1, $from_info['year']);
1440 $to_ts = mktime(0, 0, 0, ($from_info['mon'] - 1), date('t', $from_ts), $from_info['year']);
1441 }
1442
1443 return [$from_ts, $to_ts, JText::translate('VBPVIEWRESTRICTIONSTWO')];
1444 }
1445
1446 // next or prev year (keep the same exact dates)
1447 if ($step == 'year') {
1448 if ($date_dir > 0) {
1449 // forward dates navigation
1450 $from_ts = mktime(0, 0, 0, $from_info['mon'], $from_info['mday'], ($from_info['year'] + 1));
1451 $to_ts = mktime(0, 0, 0, $to_info['mon'], $to_info['mday'], ($to_info['year'] + 1));
1452 } elseif ($date_dir < 0) {
1453 // backward dates navigation
1454 $from_ts = mktime(0, 0, 0, $from_info['mon'], $from_info['mday'], ($from_info['year'] - 1));
1455 $to_ts = mktime(0, 0, 0, $to_info['mon'], $to_info['mday'], ($to_info['year'] - 1));
1456 }
1457
1458 return [$from_ts, $to_ts, JText::translate('VBCONFIGSEARCHPMAXDATEYEARS')];
1459 }
1460
1461 // next or prev quarter by default
1462 if ($date_dir > 0) {
1463 // forward dates navigation
1464 $from_ts = mktime(0, 0, 0, ($from_info['mon'] + 3), 1, $from_info['year']);
1465 $end_ts = mktime(0, 0, 0, ($from_info['mon'] + 5), 1, $from_info['year']);
1466 $to_ts = mktime(0, 0, 0, ($from_info['mon'] + 5), date('t', $end_ts), $from_info['year']);
1467 } elseif ($date_dir < 0) {
1468 // backward dates navigation
1469 $from_ts = mktime(0, 0, 0, ($from_info['mon'] - 3), 1, $from_info['year']);
1470 $end_ts = mktime(0, 0, 0, ($from_info['mon'] - 1), 1, $from_info['year']);
1471 $to_ts = mktime(0, 0, 0, ($from_info['mon'] - 1), date('t', $end_ts), $from_info['year']);
1472 }
1473
1474 return [$from_ts, $to_ts, JText::translate('VBO_QUARTER')];
1475 }
1476
1477 /**
1478 * Parse an interval of date timestamps into a readable interval of dates according to step.
1479 * Internal method for this widget only.
1480 *
1481 * @param int $from_ts current from date timestamp.
1482 * @param int $to_ts current end date timestamp.
1483 * @param string $vbo_df the date format in VBO.
1484 * @param string $step the navigation step to take (week, month or quarter).
1485 *
1486 * @return array the formatted from and to date interval strings.
1487 */
1488 protected function parseReadableDateInterval($from_ts, $to_ts, $vbo_df, $step)
1489 {
1490 $from_info = getdate($from_ts);
1491 $to_info = getdate($to_ts);
1492 $format_from_date = date($vbo_df, $from_ts);
1493 $format_to_date = date($vbo_df, $to_ts);
1494
1495 if ($step == 'week') {
1496 // include day of week
1497 $format_from_date = VikBooking::sayWeekDay($from_info['wday']) . ', ' . $format_from_date;
1498 $format_to_date = VikBooking::sayWeekDay($to_info['wday']) . ', ' . $format_to_date;
1499 } elseif ($step == 'month') {
1500 // check if it's a full month to say its name
1501 if ($from_info['mon'] == $to_info['mon'] && $from_info['year'] == $to_info['year']) {
1502 if ($from_info['mday'] == 1 && $to_info['mday'] == date('t', $to_ts)) {
1503 $format_from_date = VikBooking::sayMonth($from_info['mon']) . ' ' . $from_info['year'];
1504 $format_to_date = VikBooking::sayMonth($to_info['mon']) . ' ' . $to_info['year'];
1505 }
1506 }
1507 } elseif ($step == 'quarter') {
1508 // check if it's a full quarter
1509 if ($from_info['mday'] == 1 && $to_info['mday'] == date('t', $to_ts)) {
1510 $format_from_date = VikBooking::sayMonth($from_info['mon']) . ' ' . $from_info['year'];
1511 $format_to_date = VikBooking::sayMonth($to_info['mon']) . ' ' . $to_info['year'];
1512 }
1513 }
1514
1515 return [$format_from_date, $format_to_date];
1516 }
1517
1518 /**
1519 * Sets up the necessary script to load the welcome message for the user.
1520 * On WordPress this method may run outside Vik Booking, and so Javascript,
1521 * may not be able to access language definitions from the DOM.
1522 *
1523 * @return bool true if welcome was set up for the user, or false.
1524 */
1525 protected function setupWelcomeStats()
1526 {
1527 // avoid welcome stats on certain pages
1528 if (VBOPlatformDetection::isWordPress()) {
1529 global $pagenow;
1530 $skip_pages = ['update.php', 'plugins.php', 'plugin-install.php'];
1531 if (isset($pagenow) && in_array($pagenow, $skip_pages)) {
1532 return false;
1533 }
1534 }
1535
1536 $admin_user = JFactory::getUser();
1537 $admin_user_name = $admin_user->name;
1538
1539 if (!$admin_user_name || !$admin_user->authorise('core.vbo.bookings', 'com_vikbooking')) {
1540 // user not authorized
1541 return false;
1542 }
1543
1544 // get yesterday's date
1545 $yesterday_info = getdate(strtotime('yesterday'));
1546 $yesterday_read = implode(' ', [VikBooking::sayWeekDay($yesterday_info['wday'], true), $yesterday_info['mday'], VikBooking::sayMonth($yesterday_info['mon'], true)]);
1547 $yesterday_read = htmlspecialchars($yesterday_read);
1548 $yesterday = date('Y-m-d', $yesterday_info[0]);
1549
1550 // the payload for getting the welcome stats for yesterday
1551 $payload = [
1552 'fromdate' => $yesterday,
1553 'todate' => $yesterday,
1554 'type' => 'booking_dates',
1555 ];
1556
1557 // check if we are inside Vik Booking
1558 $in_vbo = (int)(JFactory::getApplication()->input->get('option', '') === 'com_vikbooking');
1559 $vbo_uri = VBOPlatformDetection::isWordPress() ? 'admin.php?page=vikbooking' : 'index.php?option=com_vikbooking';
1560 if (!$in_vbo && VBOPlatformDetection::isWordPress()) {
1561 // append query string value to render the admin widget
1562 $q_cmds = [
1563 'load_widget' => $this->widgetId,
1564 'multitask_data' => $payload,
1565 ];
1566 $vbo_uri .= '&' . http_build_query($q_cmds);
1567 }
1568
1569 // build data (the JS code may not be able to access script language definitions if running outside Vik Booking)
1570 $aj_endpoint = $this->getExecWidgetAjaxUri();
1571 $widget_id = $this->getIdentifier();
1572 $website_logo = $this->getIconUrl();
1573 $website_logo = $website_logo ? $website_logo : '';
1574 $greetings = VikBooking::strTrimLiteral(htmlspecialchars(JText::sprintf('VBO_WELCOME_ADMIN_USER', $admin_user_name)));
1575 $greetings_ms = VikBooking::strTrimLiteral(htmlspecialchars(JText::translate('VBO_BOOKINGS_YESTERDAY')));
1576 $modal_title = VikBooking::strTrimLiteral(htmlspecialchars(JText::translate('VBOYESTERDAY') . ', ' . $yesterday_read));
1577 $payload_json = json_encode($payload);
1578 $sugg_notifs_btn = '<button class="vbo-suggest-notifications-btn" type="button"><i class="' . VikBookingIcons::i('bell', 'can-shake') . '"></i></button>';
1579
1580 JFactory::getDocument()->addScriptDeclaration(
1581 <<<JS
1582 (function($) {
1583 'use strict';
1584
1585 $(function() {
1586 try {
1587 if (!VBOCore.storageSupported() || VBOCore.storageGetItem('vbo_finance_last_dt') == '$yesterday') {
1588 return false;
1589 }
1590 } catch(e) {
1591 return false;
1592 }
1593
1594 let call_method = 'getWelcomeStats';
1595 VBOCore.doAjax(
1596 "$aj_endpoint",
1597 {
1598 widget_id: "$widget_id",
1599 call: call_method,
1600 return: 1,
1601 tmpl: "component"
1602 },
1603 (response) => {
1604 try {
1605 var obj_res = typeof response === 'string' ? JSON.parse(response) : response;
1606 if (!obj_res.hasOwnProperty(call_method)) {
1607 throw new Error('Unexpected JSON response');
1608 }
1609
1610 VBOCore.storageSetItem('vbo_finance_last_dt', '$yesterday');
1611
1612 if (!obj_res[call_method].hasOwnProperty('tot_bookings') || !obj_res[call_method]['tot_bookings']) {
1613 return;
1614 }
1615
1616 let vbo_notif_click_handler = 'VBOCore.handleGoto';
1617 if ($in_vbo) {
1618 vbo_notif_click_handler = () => {
1619 try {
1620 let yesterday_modal = VBOCore.displayModal({
1621 suffix: 'finance-welcome',
1622 extra_class: 'vbo-modal-rounded vbo-modal-tall vbo-modal-nofooter',
1623 title: '<span class="vbo-suggest-notifications-wrap"><span>$modal_title</span>' + (VBOCore.notificationsEnabled() === false ? '$sugg_notifs_btn' : '') + '</span>',
1624 dismiss_event: 'vbo-dismiss-modal-finance-welcome',
1625 loading_event: 'vbo-loading-modal-finance-welcome',
1626 });
1627
1628 VBOCore.suggestNotifications('.vbo-suggest-notifications-btn');
1629 VBOCore.emitEvent('vbo-loading-modal-finance-welcome');
1630
1631 VBOCore.renderAdminWidget('$this->widgetId', $payload_json).then((content) => {
1632 VBOCore.emitEvent('vbo-loading-modal-finance-welcome');
1633 yesterday_modal.append(content);
1634 }).catch((error) => {
1635 VBOCore.emitEvent('vbo-dismiss-modal-finance-welcome');
1636 alert(error);
1637 });
1638 } catch(e) {
1639 document.location.href = '$vbo_uri';
1640 }
1641 };
1642 }
1643
1644 setTimeout(() => {
1645 VBOCore.dispatchNotification({
1646 title: '$greetings',
1647 message: ('$greetings_ms').replace('%d', obj_res[call_method]['tot_bookings']),
1648 icon: '$website_logo',
1649 onclick: vbo_notif_click_handler,
1650 gotourl: '$vbo_uri',
1651 });
1652 }, 500);
1653 } catch(e) {
1654 console.error(e);
1655 }
1656 },
1657 (error) => {
1658 console.error(error.responseText);
1659 }
1660 );
1661 });
1662 })(jQuery);
1663 JS
1664 );
1665
1666 return true;
1667 }
1668
1669 /**
1670 * Returns the URL to the default site logo.
1671 *
1672 * @return string|null
1673 */
1674 protected function getIconUrl()
1675 {
1676 $config = VBOFactory::getConfig();
1677
1678 // back-end custom logo
1679 $use_logo = $config->get('backlogo');
1680 if (empty($use_logo) || !strcasecmp($use_logo, 'vikbooking.png')) {
1681 // fallback to company (site) logo
1682 $use_logo = $config->get('sitelogo');
1683 }
1684
1685 if (!empty($use_logo) && strcasecmp($use_logo, 'vikbooking.png')) {
1686 // uploaded logo found
1687 $use_logo = VBO_ADMIN_URI . 'resources/' . $use_logo;
1688 } else {
1689 $use_logo = null;
1690 }
1691
1692 return $use_logo;
1693 }
1694 }
1695