PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
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 1.7.3 All 36 releases
vikbooking / admin / helpers / report / options_extras.php

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

489 lines 16.8 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(function() {
133 jQuery(".vbo-report-datepicker:input").datepicker({
134 maxDate: "+2 years",
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
350 /**
351 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
352 *
353 * @since 1.17.7 (J) - 1.7.7 (WP)
354 */
355 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$actopt, $gbook[0], $or]);
356 if ($custom_calculation) {
357 $realcost = (float) $custom_calculation[0];
358 }
359
360 $with_tax = VikBooking::sayOptionalsPlusIva($realcost, $actopt['idiva']);
361 $with_tax_daily = $with_tax / (int)$gbook[0]['days'];
362 $without_tax = VikBooking::sayOptionalsMinusIva($realcost, $actopt['idiva']);
363 $without_tax_daily = $without_tax / (int)$gbook[0]['days'];
364 //update values for this option, for this day
365 $option_day_row[(int)$stept[0]]['row_value_net'] += $without_tax_daily;
366 $option_day_row[(int)$stept[0]]['row_value_tax'] += $with_tax_daily - $without_tax_daily;
367
368 }
369 }
370 }
371 }
372
373 //push fields in the rows array as a new row
374 array_push($this->rows, array(
375 array(
376 'key' => 'day',
377 'callback' => function ($val) use ($df, $datesep, $curwday) {
378 return $curwday.', '.date(str_replace("/", $datesep, $df), $val);
379 },
380 'value' => $day_ts
381 )
382 ));
383 $row_key = count($this->rows) - 1;
384 foreach ($option_day_row as $idopt => $v) {
385 array_push($this->rows[$row_key], array(
386 'key' => 'optid'.$idopt,
387 'attr' => array(
388 'class="center"'
389 ),
390 'callback' => function ($val) use ($currency_symb) {
391 return $currency_symb.' '.VikBooking::numberFormat($val);
392 },
393 'value' => $v['row_value_net']
394 ));
395 }
396
397 //next day iteration
398 $from_info = getdate(mktime(0, 0, 0, $from_info['mon'], ($from_info['mday'] + 1), $from_info['year']));
399 }
400
401 //sort rows
402 $this->sortRows($pkrsort, $pkrorder);
403
404 //loop over the rows to build the footer row with the totals
405 $foot_tots = array();
406 foreach ($this->rows as $row) {
407 foreach ($row as $k => $v) {
408 if ($k < 1) {
409 continue;
410 }
411 if (!isset($foot_tots[$k])) {
412 $foot_tots[$k] = 0;
413 }
414 $foot_tots[$k] += $v['value'];
415 }
416 }
417 array_push($this->footerRow, array(
418 array(
419 'attr' => array(
420 'class="vbo-report-total"'
421 ),
422 'value' => '<h3>'.JText::translate('VBOREPORTSTOTALROW').'</h3>'
423 )
424 ));
425 foreach ($foot_tots as $v) {
426 array_push($this->footerRow[0], array(
427 'attr' => array(
428 'class="center"'
429 ),
430 'callback' => function ($val) use ($currency_symb) {
431 return $currency_symb.' '.VikBooking::numberFormat($val);
432 },
433 'value' => $v
434 ));
435 }
436
437 //Debug
438 if ($this->debug) {
439 $this->setWarning('path to report file = '.urlencode(dirname(__FILE__)).'<br/>');
440 $this->setWarning('$total_rooms_units = '.$total_rooms_units.'<br/>');
441 $this->setWarning('$bookings:<pre>'.print_r($bookings, true).'</pre><br/>');
442 }
443 //
444
445 return true;
446 }
447
448 /**
449 * Registers the name to give to the CSV file being exported.
450 *
451 * @return void
452 *
453 * @since 1.16.1 (J) - 1.6.1 (WP)
454 */
455 private function registerExportCSVFileName()
456 {
457 $pfromdate = VikRequest::getString('fromdate', '', 'request');
458 $ptodate = VikRequest::getString('todate', '', 'request');
459
460 $this->setExportCSVFileName($this->reportName . '-' . str_replace('/', '_', $pfromdate) . '-' . str_replace('/', '_', $ptodate) . '.csv');
461 }
462
463 /**
464 * Reads from the DB all the Options that are not
465 * tourist taxes, and returns an associative array
466 * with the ID of the Option as key.
467 *
468 * @param boolean $return_all return the whole array record
469 *
470 * @return array
471 */
472 private function getAllOptions($return_all = false)
473 {
474 $all_options = array();
475 $q = "SELECT * FROM `#__vikbooking_optionals` WHERE `is_citytax`=0 ORDER BY `name` ASC;";
476 $this->dbo->setQuery($q);
477 $this->dbo->execute();
478 if ($this->dbo->getNumRows()) {
479 $all_options = $this->dbo->loadAssocList();
480 }
481 $options = array();
482 foreach ($all_options as $opt) {
483 $options[$opt['id']] = $return_all ? $opt : $opt['name'];
484 }
485
486 return $options;
487 }
488 }
489