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 / report / options_extras.php

options_extras.php in VikBooking Hotel Booking Engine & PMS 1.7.1, at admin/helpers/report/options_extras.php

478 lines 16.4 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 * Options/Extras child Class of VikBookingReport
15 */
16 class VikBookingReportOptionsExtras extends VikBookingReport
17 {
18 /**
19 * Property 'defaultKeySort' is used by the View that renders the report.
20 */
21 public $defaultKeySort = 'day';
22
23 /**
24 * Property 'defaultKeyOrder' is used by the View that renders the report.
25 */
26 public $defaultKeyOrder = 'ASC';
27
28 /**
29 * Property 'exportAllowed' is used by the View to display the export button.
30 */
31 public $exportAllowed = 1;
32
33 /**
34 * Debug mode is activated by passing the value 'e4j_debug' > 0
35 */
36 private $debug;
37
38 /**
39 * Class constructor should define the name of the report and
40 * other vars. Call the parent constructor to define the DB object.
41 */
42 public function __construct()
43 {
44 $this->reportFile = basename(__FILE__, '.php');
45 $this->reportName = JText::translate('VBOREPORT'.strtoupper(str_replace('_', '', $this->reportFile)));
46 $this->reportFilters = array();
47
48 $this->cols = array();
49 $this->rows = array();
50 $this->footerRow = array();
51
52 $this->debug = (VikRequest::getInt('e4j_debug', 0, 'request') > 0);
53
54 $this->registerExportCSVFileName();
55
56 parent::__construct();
57 }
58
59 /**
60 * Returns the name of this report.
61 *
62 * @return string
63 */
64 public function getName()
65 {
66 return $this->reportName;
67 }
68
69 /**
70 * Returns the name of this file without .php.
71 *
72 * @return string
73 */
74 public function getFileName()
75 {
76 return $this->reportFile;
77 }
78
79 /**
80 * Returns the filters of this report.
81 *
82 * @return array
83 */
84 public function getFilters()
85 {
86 if (count($this->reportFilters)) {
87 //do not run this method twice, as it could load JS and CSS files.
88 return $this->reportFilters;
89 }
90
91 //get VBO Application Object
92 $vbo_app = VikBooking::getVboApplication();
93
94 //load the jQuery UI Datepicker
95 $this->loadDatePicker();
96
97 //From Date Filter
98 $filter_opt = array(
99 'label' => '<label for="fromdate">'.JText::translate('VBOREPORTSDATEFROM').'</label>',
100 'html' => '<input type="text" id="fromdate" name="fromdate" value="" class="vbo-report-datepicker vbo-report-datepicker-from" />',
101 'type' => 'calendar',
102 'name' => 'fromdate'
103 );
104 array_push($this->reportFilters, $filter_opt);
105
106 //To Date Filter
107 $filter_opt = array(
108 'label' => '<label for="todate">'.JText::translate('VBOREPORTSDATETO').'</label>',
109 'html' => '<input type="text" id="todate" name="todate" value="" class="vbo-report-datepicker vbo-report-datepicker-to" />',
110 'type' => 'calendar',
111 'name' => 'todate'
112 );
113 array_push($this->reportFilters, $filter_opt);
114
115 //Option ID filter
116 $pidoptional = VikRequest::getInt('idoptional', '', 'request');
117 $options = $this->getAllOptions();
118 if (count($options)) {
119 $opts_sel_html = $vbo_app->getNiceSelect($options, $pidoptional, 'idoptional', JText::translate('VBOREPORTFILTALLOPTS'), JText::translate('VBOREPORTFILTALLOPTS'), '', '', 'idoptional');
120 $filter_opt = array(
121 'label' => '<label for="idoptional">'.JText::translate('VBOREPORTOPTIONSEXTRAS').'</label>',
122 'html' => $opts_sel_html,
123 'type' => 'select',
124 'name' => 'idoptional'
125 );
126 array_push($this->reportFilters, $filter_opt);
127 }
128
129 //jQuery code for the datepicker calendars and select2
130 $pfromdate = VikRequest::getString('fromdate', '', 'request');
131 $ptodate = VikRequest::getString('todate', '', 'request');
132 $js = 'jQuery(document).ready(function() {
133 jQuery(".vbo-report-datepicker:input").datepicker({
134 maxDate: 0,
135 dateFormat: "'.$this->getDateFormat('jui').'",
136 onSelect: vboReportCheckDates
137 });
138 '.(!empty($pfromdate) ? 'jQuery(".vbo-report-datepicker-from").datepicker("setDate", "'.$pfromdate.'");' : '').'
139 '.(!empty($ptodate) ? 'jQuery(".vbo-report-datepicker-to").datepicker("setDate", "'.$ptodate.'");' : '').'
140 });
141 function vboReportCheckDates(selectedDate, inst) {
142 if (selectedDate === null || inst === null) {
143 return;
144 }
145 var cur_from_date = jQuery(this).val();
146 if (jQuery(this).hasClass("vbo-report-datepicker-from") && cur_from_date.length) {
147 var nowstart = jQuery(this).datepicker("getDate");
148 var nowstartdate = new Date(nowstart.getTime());
149 jQuery(".vbo-report-datepicker-to").datepicker("option", {minDate: nowstartdate});
150 }
151 }';
152 $this->setScript($js);
153
154 return $this->reportFilters;
155 }
156
157 /**
158 * Loads the report data from the DB.
159 * Returns true in case of success, false otherwise.
160 * Sets the columns and rows for the report to be displayed.
161 *
162 * @return boolean
163 */
164 public function getReportData()
165 {
166 if (strlen($this->getError())) {
167 //Export functions may set errors rather than exiting the process, and the View may continue the execution to attempt to render the report.
168 return false;
169 }
170 $options = $this->getAllOptions(true);
171 if (!count($options)) {
172 $this->setError('No Options configured in the system.');
173 return false;
174 }
175 //Input fields and other vars
176 $pfromdate = VikRequest::getString('fromdate', '', 'request');
177 $ptodate = VikRequest::getString('todate', '', 'request');
178 $pidoptional = VikRequest::getInt('idoptional', '', 'request');
179 $pkrsort = VikRequest::getString('krsort', $this->defaultKeySort, 'request');
180 $pkrsort = empty($pkrsort) ? $this->defaultKeySort : $pkrsort;
181 $pkrorder = VikRequest::getString('krorder', $this->defaultKeyOrder, 'request');
182 $pkrorder = empty($pkrorder) ? $this->defaultKeyOrder : $pkrorder;
183 $pkrorder = $pkrorder == 'DESC' ? 'DESC' : 'ASC';
184 $currency_symb = VikBooking::getCurrencySymb();
185 $df = $this->getDateFormat();
186 $datesep = VikBooking::getDateSeparator();
187 if (empty($ptodate)) {
188 $ptodate = $pfromdate;
189 }
190 //Get dates timestamps
191 $from_ts = VikBooking::getDateTimestamp($pfromdate, 0, 0);
192 $to_ts = VikBooking::getDateTimestamp($ptodate, 23, 59, 59);
193 if (empty($pfromdate) || empty($from_ts) || empty($to_ts)) {
194 $this->setError(JText::translate('VBOREPORTSERRNODATES'));
195 return false;
196 }
197
198 //Query to obtain the records
199 $records = array();
200 $q = "SELECT `o`.`id`,`o`.`ts`,`o`.`days`,`o`.`checkin`,`o`.`checkout`,`o`.`totpaid`,`o`.`roomsnum`,`o`.`total`,`o`.`idorderota`,`o`.`channel`,`o`.`country`,`o`.`tot_taxes`,`o`.`tot_city_taxes`,".
201 "`o`.`tot_fees`,`o`.`cmms`,`or`.`idorder`,`or`.`idroom`,`or`.`adults`,`or`.`children`,`or`.`optionals`,`or`.`childrenage`,`or`.`cust_cost`,`or`.`cust_idiva`,`or`.`extracosts`,`or`.`room_cost` ".
202 "FROM `#__vikbooking_orders` AS `o` LEFT JOIN `#__vikbooking_ordersrooms` AS `or` ON `or`.`idorder`=`o`.`id` ".
203 "WHERE `o`.`status`='confirmed' AND `o`.`closure`=0 AND `o`.`checkout`>=".$from_ts." AND `o`.`checkin`<=".$to_ts." ".
204 "ORDER BY `o`.`checkin` ASC, `o`.`id` ASC, `or`.`id` ASC;";
205 $this->dbo->setQuery($q);
206 $records = $this->dbo->loadAssocList();
207
208 if (!$records) {
209 $this->setError(JText::translate('VBOREPORTSERRNORESERV'));
210 return false;
211 }
212
213 $valid_opt_ids = array_keys($options);
214
215 //nest records with multiple rooms booked inside sub-array
216 $bookings = array();
217 foreach ($records as $v) {
218 if (empty($v['optionals'])) {
219 continue;
220 }
221 if (!empty($pidoptional) && strpos($v['optionals'], $pidoptional.':') === false) {
222 //this booking does not contain the filtered option
223 continue;
224 }
225 $has_valid_options = false;
226 foreach ($valid_opt_ids as $optid) {
227 if (strpos($v['optionals'], $optid.':') !== false) {
228 $has_valid_options = true;
229 break;
230 }
231 }
232 if (!$has_valid_options) {
233 //maybe this booking has only city taxes as options, not real ones, so we don't take it
234 continue;
235 }
236 if (!isset($bookings[$v['id']])) {
237 $bookings[$v['id']] = array();
238 }
239 //calculate the from_ts and to_ts values for later comparison
240 $in_info = getdate($v['checkin']);
241 $out_info = getdate($v['checkout']);
242 $v['from_ts'] = mktime(0, 0, 0, $in_info['mon'], $in_info['mday'], $in_info['year']);
243 $v['to_ts'] = mktime(23, 59, 59, $out_info['mon'], ($out_info['mday'] - 1), $out_info['year']);
244 //
245 array_push($bookings[$v['id']], $v);
246 }
247
248 //define the columns of the report and the Option IDs for the rows
249 $option_rows = array();
250 $this->cols = array(
251 //date
252 array(
253 'key' => 'day',
254 'sortable' => 1,
255 'label' => JText::translate('VBOREPORTREVENUEDAY')
256 )
257 );
258 foreach ($options as $opt) {
259 if (!empty($pidoptional) && $pidoptional != $opt['id']) {
260 continue;
261 }
262 $opt['row_value_net'] = 0;
263 $opt['row_value_tax'] = 0;
264 $option_rows[$opt['id']] = $opt;
265 array_push($this->cols, array(
266 'key' => 'optid'.$opt['id'],
267 'attr' => array(
268 'class="center"'
269 ),
270 'sortable' => 1,
271 'label' => $opt['name']
272 ));
273 }
274
275 //loop over the dates of the report to build the rows
276 $from_info = getdate($from_ts);
277 $to_info = getdate($to_ts);
278 while ($from_info[0] <= $to_info[0]) {
279 //prepare default fields for this row
280 $day_ts = $from_info[0];
281 $curwday = $this->getWdayString($from_info['wday'], 'short');
282 $option_day_row = $option_rows;
283 //calculate the report details for this day
284 foreach ($bookings as $gbook) {
285 if ($from_info[0] >= $gbook[0]['from_ts'] && $from_info[0] <= $gbook[0]['to_ts']) {
286 //this booking affects the current day
287 foreach ($gbook as $or) {
288 $stepo = explode(";", $or['optionals']);
289 foreach ($stepo as $roptkey => $one) {
290 if (empty($one)) {
291 continue;
292 }
293 $stept = explode(":", $one);
294 if (!isset($option_rows[(int)$stept[0]])) {
295 //we don't need this option
296 continue;
297 }
298 $actopt = $option_rows[(int)$stept[0]];
299 //calculate the cost of this option
300 if (!empty($actopt['ageintervals']) && $or['children'] > 0 && strstr($stept[1], '-') != false) {
301 $optagenames = VikBooking::getOptionIntervalsAges($actopt['ageintervals']);
302 $optagepcent = VikBooking::getOptionIntervalsPercentage($actopt['ageintervals']);
303 $optageovrct = VikBooking::getOptionIntervalChildOverrides($actopt, $or['adults'], $or['children']);
304 $child_num = VikBooking::getRoomOptionChildNumber($or['optionals'], $actopt['id'], $roptkey, $or['children']);
305 $optagecosts = VikBooking::getOptionIntervalsCosts(isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $actopt['ageintervals']);
306 $agestept = explode('-', $stept[1]);
307 $stept[1] = $agestept[0];
308 $chvar = $agestept[1];
309 if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 1) {
310 //percentage value of the adults tariff
311 if (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00) {
312 $optagecosts[($chvar - 1)] = $or['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
313 } else {
314 //we ignore the room base cost (tars) as it's not available here
315 $display_rate = !empty($or['room_cost']) ? $or['room_cost'] : 0;
316 $optagecosts[($chvar - 1)] = $display_rate * $optagecosts[($chvar - 1)] / 100;
317 }
318 } elseif (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 2) {
319 //VBO 1.10 - percentage value of room base cost
320 if (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00) {
321 $optagecosts[($chvar - 1)] = $or['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
322 } else {
323 //we ignore the room base cost (tars) as it's not available here
324 $display_rate = !empty($or['room_cost']) ? $or['room_cost'] : 0;
325 $optagecosts[($chvar - 1)] = $display_rate * $optagecosts[($chvar - 1)] / 100;
326 }
327 }
328 $realcost = (intval($actopt['perday']) == 1 ? (floatval($optagecosts[($chvar - 1)]) * $gbook[0]['days'] * $stept[1]) : (floatval($optagecosts[($chvar - 1)]) * $stept[1]));
329 } else {
330 // VBO 1.11 - options percentage cost of the room total fee
331 if (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00) {
332 $deftar_basecosts = $or['cust_cost'];
333 } else {
334 $deftar_basecosts = !empty($or['room_cost']) ? $or['room_cost'] : 0;
335 }
336 $actopt['cost'] = (int)$actopt['pcentroom'] ? ($deftar_basecosts * $actopt['cost'] / 100) : $actopt['cost'];
337 //
338 $realcost = (intval($actopt['perday']) == 1 ? ($actopt['cost'] * $gbook[0]['days'] * $stept[1]) : ($actopt['cost'] * $stept[1]));
339 }
340 if (!empty ($actopt['maxprice']) && $actopt['maxprice'] > 0 && $realcost > $actopt['maxprice']) {
341 $realcost = $actopt['maxprice'];
342 if (intval($actopt['hmany']) == 1 && intval($stept[1]) > 1) {
343 $realcost = $actopt['maxprice'] * $stept[1];
344 }
345 }
346 if ($actopt['perperson'] == 1) {
347 $realcost = $realcost * $or['adults'];
348 }
349 $with_tax = VikBooking::sayOptionalsPlusIva($realcost, $actopt['idiva']);
350 $with_tax_daily = $with_tax / (int)$gbook[0]['days'];
351 $without_tax = VikBooking::sayOptionalsMinusIva($realcost, $actopt['idiva']);
352 $without_tax_daily = $without_tax / (int)$gbook[0]['days'];
353 //update values for this option, for this day
354 $option_day_row[(int)$stept[0]]['row_value_net'] += $without_tax_daily;
355 $option_day_row[(int)$stept[0]]['row_value_tax'] += $with_tax_daily - $without_tax_daily;
356
357 }
358 }
359 }
360 }
361
362 //push fields in the rows array as a new row
363 array_push($this->rows, array(
364 array(
365 'key' => 'day',
366 'callback' => function ($val) use ($df, $datesep, $curwday) {
367 return $curwday.', '.date(str_replace("/", $datesep, $df), $val);
368 },
369 'value' => $day_ts
370 )
371 ));
372 $row_key = count($this->rows) - 1;
373 foreach ($option_day_row as $idopt => $v) {
374 array_push($this->rows[$row_key], array(
375 'key' => 'optid'.$idopt,
376 'attr' => array(
377 'class="center"'
378 ),
379 'callback' => function ($val) use ($currency_symb) {
380 return $currency_symb.' '.VikBooking::numberFormat($val);
381 },
382 'value' => $v['row_value_net']
383 ));
384 }
385
386 //next day iteration
387 $from_info = getdate(mktime(0, 0, 0, $from_info['mon'], ($from_info['mday'] + 1), $from_info['year']));
388 }
389
390 //sort rows
391 $this->sortRows($pkrsort, $pkrorder);
392
393 //loop over the rows to build the footer row with the totals
394 $foot_tots = array();
395 foreach ($this->rows as $row) {
396 foreach ($row as $k => $v) {
397 if ($k < 1) {
398 continue;
399 }
400 if (!isset($foot_tots[$k])) {
401 $foot_tots[$k] = 0;
402 }
403 $foot_tots[$k] += $v['value'];
404 }
405 }
406 array_push($this->footerRow, array(
407 array(
408 'attr' => array(
409 'class="vbo-report-total"'
410 ),
411 'value' => '<h3>'.JText::translate('VBOREPORTSTOTALROW').'</h3>'
412 )
413 ));
414 foreach ($foot_tots as $v) {
415 array_push($this->footerRow[0], array(
416 'attr' => array(
417 'class="center"'
418 ),
419 'callback' => function ($val) use ($currency_symb) {
420 return $currency_symb.' '.VikBooking::numberFormat($val);
421 },
422 'value' => $v
423 ));
424 }
425
426 //Debug
427 if ($this->debug) {
428 $this->setWarning('path to report file = '.urlencode(dirname(__FILE__)).'<br/>');
429 $this->setWarning('$total_rooms_units = '.$total_rooms_units.'<br/>');
430 $this->setWarning('$bookings:<pre>'.print_r($bookings, true).'</pre><br/>');
431 }
432 //
433
434 return true;
435 }
436
437 /**
438 * Registers the name to give to the CSV file being exported.
439 *
440 * @return void
441 *
442 * @since 1.16.1 (J) - 1.6.1 (WP)
443 */
444 private function registerExportCSVFileName()
445 {
446 $pfromdate = VikRequest::getString('fromdate', '', 'request');
447 $ptodate = VikRequest::getString('todate', '', 'request');
448
449 $this->setExportCSVFileName($this->reportName . '-' . str_replace('/', '_', $pfromdate) . '-' . str_replace('/', '_', $ptodate) . '.csv');
450 }
451
452 /**
453 * Reads from the DB all the Options that are not
454 * tourist taxes, and returns an associative array
455 * with the ID of the Option as key.
456 *
457 * @param boolean $return_all return the whole array record
458 *
459 * @return array
460 */
461 private function getAllOptions($return_all = false)
462 {
463 $all_options = array();
464 $q = "SELECT * FROM `#__vikbooking_optionals` WHERE `is_citytax`=0 ORDER BY `name` ASC;";
465 $this->dbo->setQuery($q);
466 $this->dbo->execute();
467 if ($this->dbo->getNumRows()) {
468 $all_options = $this->dbo->loadAssocList();
469 }
470 $options = array();
471 foreach ($all_options as $opt) {
472 $options[$opt['id']] = $return_all ? $opt : $opt['name'];
473 }
474
475 return $options;
476 }
477 }
478