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 / daily_room_report.php

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

802 lines 26.1 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 * Daily Room Report child Class of VikBookingReport
15 */
16 class VikBookingReportDailyRoomReport extends VikBookingReport
17 {
18 /**
19 * Property 'defaultKeySort' is used by the View that renders the report.
20 */
21 public $defaultKeySort = 'type';
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('VBOREPORTREVENUEDAY').'</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="" placeholder="'.addslashes(JText::translate('VBOFILTEISROPTIONAL')).'" class="vbo-report-datepicker vbo-report-datepicker-to" />',
110 'type' => 'calendar',
111 'name' => 'todate'
112 );
113 array_push($this->reportFilters, $filter_opt);
114
115 // type filter
116 $types = [
117 'arrivals' => JText::translate('VBOARRIVING'),
118 'departures' => JText::translate('VBODEPARTING'),
119 'stayover' => JText::translate('VBOTYPESTAYOVER'),
120 ];
121 $ptype = VikRequest::getString('type', '', 'request');
122 $types_sel_html = $vbo_app->getNiceSelect($types, $ptype, 'type', JText::translate('VBANYTHING'), JText::translate('VBANYTHING'), '', '', 'type');
123 $filter_opt = array(
124 'label' => '<label for="type">' . JText::translate('VBPSHOWSEASONSTHREE') . '</label>',
125 'html' => $types_sel_html,
126 'type' => 'select',
127 'name' => 'type'
128 );
129 array_push($this->reportFilters, $filter_opt);
130
131 //Room ID filter
132 $pidroom = VikRequest::getInt('idroom', '', 'request');
133 $all_rooms = $this->getRooms();
134 $rooms = array();
135 foreach ($all_rooms as $room) {
136 $rooms[$room['id']] = $room['name'];
137 }
138 if (count($rooms)) {
139 $rooms_sel_html = $vbo_app->getNiceSelect($rooms, $pidroom, 'idroom', JText::translate('VBOSTATSALLROOMS'), JText::translate('VBOSTATSALLROOMS'), '', '', 'idroom');
140 $filter_opt = array(
141 'label' => '<label for="idroom">'.JText::translate('VBOREPORTSROOMFILT').'</label>',
142 'html' => $rooms_sel_html,
143 'type' => 'select',
144 'name' => 'idroom'
145 );
146 array_push($this->reportFilters, $filter_opt);
147 }
148
149 // get minimum check-in and maximum check-out for dates filters
150 $df = $this->getDateFormat();
151 $mincheckin = 0;
152 $maxcheckout = 0;
153 $q = "SELECT MIN(`checkin`) AS `mincheckin`, MAX(`checkout`) AS `maxcheckout` FROM `#__vikbooking_orders` WHERE `status`='confirmed' AND `closure`=0;";
154 $this->dbo->setQuery($q);
155 $this->dbo->execute();
156 if ($this->dbo->getNumRows()) {
157 $data = $this->dbo->loadAssoc();
158 if (!empty($data['mincheckin']) && !empty($data['maxcheckout'])) {
159 $mincheckin = $data['mincheckin'];
160 $maxcheckout = $data['maxcheckout'];
161 }
162 }
163 //
164
165 //jQuery code for the datepicker calendars and select2
166 $pfromdate = VikRequest::getString('fromdate', date($df), 'request');
167 $ptodate = VikRequest::getString('todate', '', 'request');
168 $js = 'jQuery(document).ready(function() {
169 jQuery(".vbo-report-datepicker:input").datepicker({
170 '.(!empty($mincheckin) ? 'minDate: "'.date($df, $mincheckin).'", ' : '').'
171 '.(!empty($maxcheckout) ? 'maxDate: "'.date($df, $maxcheckout).'", ' : '').'
172 dateFormat: "'.$this->getDateFormat('jui').'"
173 });
174 '.(!empty($pfromdate) ? 'jQuery(".vbo-report-datepicker-from").datepicker("setDate", "'.$pfromdate.'");' : '').'
175 '.(!empty($ptodate) ? 'jQuery(".vbo-report-datepicker-to").datepicker("setDate", "'.$ptodate.'");' : '').'
176 });';
177 $this->setScript($js);
178
179 return $this->reportFilters;
180 }
181
182 /**
183 * Loads the report data from the DB.
184 * Returns true in case of success, false otherwise.
185 * Sets the columns and rows for the report to be displayed.
186 *
187 * @return boolean
188 */
189 public function getReportData()
190 {
191 if (strlen($this->getError())) {
192 // export functions may set errors rather than exiting the process, and the View may continue the execution to attempt to render the report.
193 return false;
194 }
195
196 // input fields and other vars
197 $pfromdate = VikRequest::getString('fromdate', '', 'request');
198 $ptodate = VikRequest::getString('todate', $pfromdate, 'request');
199 $ptodate = empty($ptodate) ? $pfromdate : $ptodate;
200 $ptype = VikRequest::getString('type', '', 'request');
201 $pidroom = VikRequest::getInt('idroom', '', 'request');
202 $pkrsort = VikRequest::getString('krsort', $this->defaultKeySort, 'request');
203 $pkrsort = empty($pkrsort) ? $this->defaultKeySort : $pkrsort;
204 $pkrorder = VikRequest::getString('krorder', $this->defaultKeyOrder, 'request');
205 $pkrorder = empty($pkrorder) ? $this->defaultKeyOrder : $pkrorder;
206 $pkrorder = $pkrorder == 'DESC' ? 'DESC' : 'ASC';
207
208 $df = $this->getDateFormat();
209 $datesep = VikBooking::getDateSeparator();
210 // get date timestamps
211 $from_ts = VikBooking::getDateTimestamp($pfromdate, 0, 0);
212 $to_ts = VikBooking::getDateTimestamp($ptodate, 23, 59, 59);
213 if (empty($pfromdate) || empty($from_ts)) {
214 $this->setError(JText::translate('VBOREPORTSERRNODATES'));
215 return false;
216 }
217
218 // arrival and departure dates
219 $arrivedt = date('Y-m-d', $from_ts);
220 $departdt = date('Y-m-d', $to_ts);
221
222 // date type filter
223 $dtype_clause = "`o`.`checkout` >= {$from_ts} AND `o`.`checkin` <= {$to_ts}";
224 if ($ptype == 'arrivals') {
225 $dtype_clause = "`o`.`checkin` >= {$from_ts} AND `o`.`checkin` <= {$to_ts}";
226 } elseif ($ptype == 'departures') {
227 $dtype_clause = "`o`.`checkout` >= {$from_ts} AND `o`.`checkout` <= {$to_ts}";
228 } elseif ($ptype == 'stayover') {
229 $dtype_clause = "`o`.`checkin` < {$from_ts} AND `o`.`checkout` > {$to_ts}";
230 }
231
232 // query to obtain the records
233 $q = "SELECT `o`.`id`,`o`.`custdata`,`o`.`ts`,`o`.`days`,`o`.`checkin`,`o`.`checkout`,`o`.`roomsnum`,`o`.`idorderota`,`o`.`channel`,`o`.`country`,`o`.`adminnotes`,".
234 "`or`.`idorder`,`or`.`idroom`,`or`.`adults`,`or`.`children`,`or`.`pets`,`or`.`idtar`,`or`.`t_first_name`,`or`.`t_last_name`,`or`.`roomindex`,`or`.`pkg_name`,`or`.`otarplan`,".
235 "`or`.`meals`,`r`.`name`,`r`.`params`,`co`.`idcustomer`,`c`.`first_name`,`c`.`last_name`,`c`.`country` AS `customer_country` ".
236 "FROM `#__vikbooking_orders` AS `o` LEFT JOIN `#__vikbooking_ordersrooms` AS `or` ON `or`.`idorder`=`o`.`id` LEFT JOIN `#__vikbooking_rooms` AS `r` ON `or`.`idroom`=`r`.`id` ".
237 "LEFT JOIN `#__vikbooking_customers_orders` AS `co` ON `co`.`idorder`=`o`.`id` LEFT JOIN `#__vikbooking_customers` AS `c` ON `c`.`id`=`co`.`idcustomer` ".
238 "WHERE `o`.`status`='confirmed' AND `o`.`closure`=0 AND {$dtype_clause} ".(!empty($pidroom) ? "AND `or`.`idroom`=".(int)$pidroom." " : "").
239 "ORDER BY `o`.`checkin` DESC, `o`.`id` ASC;";
240 $this->dbo->setQuery($q);
241 $records = $this->dbo->loadAssocList();
242 if (!$records) {
243 $this->setError(JText::translate('VBOREPORTSERRNORESERV'));
244 return false;
245 }
246
247 // nest records with multiple rooms booked inside sub-array
248 $bookings = array();
249 foreach ($records as $v) {
250 if (!isset($bookings[$v['id']])) {
251 $bookings[$v['id']] = array();
252 }
253 //calculate the from_ts and to_ts values for later comparison
254 $in_info = getdate($v['checkin']);
255 $out_info = getdate($v['checkout']);
256 $v['from_ts'] = mktime(0, 0, 0, $in_info['mon'], $in_info['mday'], $in_info['year']);
257 $v['to_ts'] = mktime(23, 59, 59, $out_info['mon'], ($out_info['mday'] - 1), $out_info['year']);
258 //
259 array_push($bookings[$v['id']], $v);
260 }
261
262 //define the columns of the report
263 $this->cols = array(
264 //Type
265 array(
266 'key' => 'type',
267 'sortable' => 1,
268 'label' => JText::translate('VBPSHOWSEASONSTHREE')
269 ),
270 //Room name
271 array(
272 'key' => 'room_name',
273 'attr' => array(
274 'class="left"'
275 ),
276 'sortable' => 1,
277 'label' => JText::translate('VBEDITORDERTHREE')
278 ),
279 //customer
280 array(
281 'key' => 'customer',
282 'attr' => array(
283 'class="left"'
284 ),
285 'sortable' => 1,
286 'label' => JText::translate('VBCUSTOMERNOMINATIVE')
287 ),
288 //guests
289 array(
290 'key' => 'guests',
291 'attr' => array(
292 'class="center"'
293 ),
294 'sortable' => 1,
295 'label' => JText::translate('VBPVIEWORDERSPEOPLE')
296 ),
297 //rate plan
298 array(
299 'key' => 'rplan',
300 'attr' => array(
301 'class="center"'
302 ),
303 'sortable' => 1,
304 'label' => JText::translate('VBOROVWSELRPLAN')
305 ),
306 // meal plan
307 array(
308 'key' => 'mplan',
309 'attr' => array(
310 'class="center"'
311 ),
312 'label' => JText::translate('VBO_MEAL_PLAN')
313 ),
314 //Channel
315 array(
316 'key' => 'channel',
317 'attr' => array(
318 'class="center"'
319 ),
320 'sortable' => 1,
321 'label' => JText::translate('VBOCHANNEL')
322 ),
323 //checkin
324 array(
325 'key' => 'checkin',
326 'attr' => array(
327 'class="left"'
328 ),
329 'sortable' => 1,
330 'label' => JText::translate('VBPICKUPAT')
331 ),
332 //checkout
333 array(
334 'key' => 'checkout',
335 'attr' => array(
336 'class="left"'
337 ),
338 'sortable' => 1,
339 'label' => JText::translate('VBRELEASEAT')
340 ),
341 //nights
342 array(
343 'key' => 'nights',
344 'attr' => array(
345 'class="center"'
346 ),
347 'sortable' => 1,
348 'label' => JText::translate('VBDAYS')
349 ),
350 //admin notes
351 array(
352 'key' => 'admin_notes',
353 'attr' => array(
354 'class="left"'
355 ),
356 'sortable' => 1,
357 'label' => JText::translate('VBPSHOWPAYMENTSTHREE')
358 ),
359 //ID
360 array(
361 'key' => 'id',
362 'attr' => array(
363 'class="center"'
364 ),
365 'sortable' => 1,
366 'label' => JText::translate('VBDASHBOOKINGID')
367 ),
368 );
369
370 //loop over the bookings to build the list of countries
371 $countries = [];
372 foreach ($bookings as $k => $gbook) {
373 $country = 'unknown';
374 if (!empty($gbook[0]['country'])) {
375 $country = $gbook[0]['country'];
376 } elseif (!empty($gbook[0]['customer_country'])) {
377 $country = $gbook[0]['customer_country'];
378 }
379 if (!in_array($country, $countries)) {
380 array_push($countries, $country);
381 }
382 $bookings[$k][0]['country'] = $country;
383 }
384
385 // countries map
386 $countries_map = $this->getCountriesMap($countries);
387
388 // default channel provenience
389 $website_provenience = JText::translate('VBORDFROMSITE');
390
391 // channel logos helper
392 $vcm_logos = VikBooking::getVcmChannelsLogo('', true);
393
394 // guests stay counter
395 $guests_stay_counter = [
396 'arriving' => 0,
397 'departing' => 0,
398 'stayover' => 0,
399 ];
400
401 // meals included
402 $default_meal_plans = VBOMealplanManager::getInstance()->getPlans();
403 $guest_meals_included = [];
404 foreach ($default_meal_plans as $meal_enum => $meal_name) {
405 $guest_meals_included[$meal_enum] = 0;
406 }
407
408 // loop over the bookings to build the rows
409 foreach ($bookings as $gbook) {
410 // prepare vars
411 $customer_name = '-----';
412 if (!empty($gbook[0]['t_first_name']) && !empty($gbook[0]['t_last_name'])) {
413 $customer_name = ltrim($gbook[0]['t_first_name'].' '.$gbook[0]['t_last_name'], ' ');
414 } elseif (!empty($gbook[0]['first_name']) && !empty($gbook[0]['last_name'])) {
415 $customer_name = ltrim($gbook[0]['first_name'].' '.$gbook[0]['last_name'], ' ');
416 } elseif (!empty($gbook[0]['custdata'])) {
417 $parts = explode("\n", $gbook[0]['custdata']);
418 if (count($parts) >= 2 && strpos($parts[0], ':') !== false && strpos($parts[1], ':') !== false) {
419 $first_parts = explode(':', $parts[0]);
420 $second_parts = explode(':', $parts[1]);
421 $customer_name = ltrim(trim($first_parts[1]).' '.trim($second_parts[1]), ' ');
422 }
423 }
424 $country = $gbook[0]['country'];
425 $in_info = getdate($gbook[0]['checkin']);
426 $curwday_in = $this->getWdayString($in_info['wday'], 'short');
427 $out_info = getdate($gbook[0]['checkout']);
428 $curwday_out = $this->getWdayString($out_info['wday'], 'short');
429 $type = JText::translate('VBOTYPESTAYOVER');
430 $raw_type = 'stayover';
431 if (date('Y-m-d', $gbook[0]['checkin']) == $arrivedt) {
432 $type = JText::translate('VBOTYPEARRIVAL');
433 $raw_type = 'arriving';
434 } elseif (date('Y-m-d', $gbook[0]['checkout']) == $departdt) {
435 $type = JText::translate('VBOTYPEDEPARTURE');
436 $raw_type = 'departing';
437 }
438
439 // channel logo and provenience
440 $provenience = $website_provenience;
441 $channel_html = $website_provenience;
442 if (!empty($gbook[0]['channel'])) {
443 // build channel name
444 $otachannel = '';
445 $channelparts = explode('_', $gbook[0]['channel']);
446 $otachannel = array_key_exists(1, $channelparts) && strlen($channelparts[1]) ? $channelparts[1] : ucwords($channelparts[0]);
447 $provenience = $otachannel;
448
449 // attempt to fetch the channel logo
450 $ota_logo_img = is_object($vcm_logos) ? $vcm_logos->setProvenience($otachannel, $gbook[0]['channel'])->getLogoURL() : false;
451 if ($ota_logo_img !== false) {
452 // display the image in the View and hide it during printing
453 $channel_html = '<img src="' . $ota_logo_img . '" title="' . htmlspecialchars($otachannel) . '" class="vbo-hidein-print vbo-channelimg-medium" />';
454 // get rid of the ending "...api" in the channel name
455 $channel_html .= '<span class="vbo-showin-print">' . preg_replace("/([a-z0-9 ]+)(api)$/i", '$1', $otachannel) . '</span>';
456 } else {
457 // get rid of the ending "...api" in the channel name
458 $channel_html = '<span class="vbo-provenience">' . preg_replace("/([a-z0-9 ]+)(api)$/i", '$1', $otachannel) . '</span>';
459 }
460 }
461
462 foreach ($gbook as $roomres) {
463 // rate plan name and ID, if any
464 $active_rplan_id = 0;
465 if (!empty($roomres['otarplan'])) {
466 $rplan = $roomres['otarplan'];
467 } else {
468 list($rplan, $active_rplan_id) = $this->getPriceName($roomres['idtar']);
469 }
470
471 // room name and index
472 $unit_index = '';
473 if (strlen($roomres['roomindex']) && !empty($roomres['params'])) {
474 $room_params = json_decode($roomres['params'], true);
475 if (is_array($room_params) && array_key_exists('features', $room_params) && @count($room_params['features']) > 0) {
476 foreach ($room_params['features'] as $rind => $rfeatures) {
477 if ($rind == $roomres['roomindex']) {
478 foreach ($rfeatures as $fname => $fval) {
479 if (strlen($fval)) {
480 $unit_index = ' #'.$fval;
481 break;
482 }
483 }
484 break;
485 }
486 }
487 }
488 }
489 $roomname = $roomres['name'] . $unit_index;
490
491 // room guests number
492 $room_guests_numb = ($roomres['adults'] + $roomres['children']);
493 $pets_str = '';
494 if ($roomres['pets'] > 0) {
495 $pets_str = $roomres['pets'] > 1 ? "{$roomres['pets']} " . JText::translate('VBO_PETS') : '1 ' . JText::translate('VBO_PET');
496 }
497
498 // update guests stay counter
499 $guests_stay_counter[$raw_type] += $room_guests_numb;
500
501 // meals included in the room rate
502 $short_meal_enums = VBOMealplanManager::getInstance()->getShortMealPlans();
503 $included_meals = [];
504 $room_meals = [];
505 $room_meals_not_today = [];
506 if (!empty($roomres['meals'])) {
507 // display included meals defined at room-reservation record
508 $included_meals = VBOMealplanManager::getInstance()->roomRateIncludedMeals($roomres);
509 } else {
510 // fetch default included meals in the selected rate plan
511 $included_meals = $active_rplan_id ? VBOMealplanManager::getInstance()->ratePlanIncludedMeals($active_rplan_id) : [];
512 }
513 if (!$included_meals && empty($roomres['meals']) && !empty($roomres['idorderota']) && !empty($roomres['channel']) && !empty($roomres['custdata'])) {
514 // attempt to fetch the included meal plans from the raw customer data or OTA reservation and room
515 $included_meals = VBOMealplanManager::getInstance()->otaDataIncludedMeals($roomres, $roomres);
516 }
517 foreach ($included_meals as $meal_enum => $meal_name) {
518 if ($raw_type == 'arriving') {
519 // no breakfast for those who arrive
520 if ($meal_enum == 'breakfast') {
521 // set meal short name
522 $room_meals_not_today[] = $short_meal_enums[$meal_enum];
523 continue;
524 }
525 // increase pax for this meal
526 $guest_meals_included[$meal_enum] += $room_guests_numb;
527 // set meal short name for this room
528 $room_meals[] = $short_meal_enums[$meal_enum];
529 } elseif ($raw_type == 'departing') {
530 // only breakfast for those who depart
531 if ($meal_enum == 'breakfast') {
532 // increase pax for this meal
533 $guest_meals_included[$meal_enum] += $room_guests_numb;
534 // set meal short name for this room
535 $room_meals[] = $short_meal_enums[$meal_enum];
536 } else {
537 // set meal short name
538 $room_meals_not_today[] = $short_meal_enums[$meal_enum];
539 }
540 } else {
541 // increase pax for this meal (any kind of meal for those who are staying)
542 $guest_meals_included[$meal_enum] += $room_guests_numb;
543 // set meal first letter for this room
544 $room_meals[] = $short_meal_enums[$meal_enum];
545 }
546 }
547
548 //push fields in the rows array as a new row
549 array_push($this->rows, array(
550 array(
551 'key' => 'type',
552 'value' => $type
553 ),
554 array(
555 'key' => 'room_name',
556 'attr' => array(
557 'class="left"'
558 ),
559 'value' => $roomname
560 ),
561 array(
562 'key' => 'customer',
563 'attr' => array(
564 'class="vbo-report-touristtaxes-countryname"'
565 ),
566 'callback' => function ($val) use ($country) {
567 if (is_file(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$country.'.png')) {
568 return $val.'<img src="'.VBO_ADMIN_URI.'resources/countries/'.$country.'.png" title="'.$country.'" class="vbo-country-flag vbo-country-flag-left" />';
569 }
570 return $val;
571 },
572 'export_callback' => function ($val) use ($country) {
573 return $val . (!empty($country) ? ' ('.$country.')' : '');
574 },
575 'value' => $customer_name
576 ),
577 array(
578 'key' => 'guests',
579 'attr' => array(
580 'class="center"'
581 ),
582 'value' => $room_guests_numb . ($roomres['pets'] > 0 ? " + {$pets_str}" : '')
583 ),
584 array(
585 'key' => 'rplan',
586 'attr' => array(
587 'class="center"'
588 ),
589 'value' => ucwords($rplan)
590 ),
591 array(
592 'key' => 'mplan',
593 'attr' => array(
594 'class="center"'
595 ),
596 'callback' => function ($val) use ($room_meals_not_today, $room_meals) {
597 $meals_str = '';
598 if ($room_meals_not_today || $room_meals) {
599 $meals_str .= '<span class="vbo-wider-badges-wrap vbo-hidein-print">';
600 if (count($room_meals_not_today) === 1) {
601 // we assume this is no breakfast today because of an arrival
602 $meals_str .= '<span class="badge badge-primary">' . implode(', ', $room_meals_not_today) . '</span>';
603 $meals_str .= $room_meals ? ' ' : '';
604 }
605 if ($room_meals) {
606 $meals_str .= '<span class="badge badge-info">' . implode(', ', $room_meals) . '</span>';
607 }
608 if ($room_meals_not_today && count($room_meals_not_today) !== 1) {
609 // we assume this is no lunch & dinner today because of a departure
610 $meals_str .= $room_meals ? ' ' : '';
611 $meals_str .= '<span class="badge badge-primary">' . implode(', ', $room_meals_not_today) . '</span>';
612 }
613 $meals_str .= '</span>';
614 $meals_str .= '<span class="vbo-showin-print">(' . implode(', ', array_merge($room_meals_not_today, $room_meals)) . ')</span>';
615 }
616 return $meals_str;
617 },
618 'export_callback' => function ($val) {
619 return $val;
620 },
621 'value' => ($room_meals ? '(' . implode(', ', $room_meals) . ')' : '')
622 ),
623 array(
624 'key' => 'channel',
625 'attr' => array(
626 'class="center"'
627 ),
628 'callback' => function ($val) use ($channel_html) {
629 return $channel_html;
630 },
631 'export_callback' => function ($val) {
632 return $val;
633 },
634 'value' => $provenience
635 ),
636 array(
637 'key' => 'checkin',
638 'callback' => function ($val) use ($df, $datesep, $curwday_in) {
639 return $curwday_in.', '.date(str_replace("/", $datesep, $df), $val);
640 },
641 'export_callback' => function ($val) use ($df, $datesep, $curwday_in) {
642 return $curwday_in.', '.date(str_replace("/", $datesep, $df), $val);
643 },
644 'value' => $roomres['checkin']
645 ),
646 array(
647 'key' => 'checkout',
648 'callback' => function ($val) use ($df, $datesep, $curwday_out) {
649 return $curwday_out.', '.date(str_replace("/", $datesep, $df), $val);
650 },
651 'export_callback' => function ($val) use ($df, $datesep, $curwday_out) {
652 return $curwday_out.', '.date(str_replace("/", $datesep, $df), $val);
653 },
654 'value' => $roomres['checkout']
655 ),
656 array(
657 'key' => 'nights',
658 'attr' => array(
659 'class="center"'
660 ),
661 'value' => $roomres['days']
662 ),
663 array(
664 'key' => 'admin_notes',
665 'attr' => array(
666 'class="left"'
667 ),
668 'value' => $roomres['adminnotes']
669 ),
670 array(
671 'key' => 'id',
672 'attr' => array(
673 'class="center"'
674 ),
675 'callback' => function ($val) {
676 return '<a href="index.php?option=com_vikbooking&task=editorder&cid[]='.$val.'" target="_blank"><i class="'.VikBookingIcons::i('external-link').'"></i> '.$val.'</a>';
677 },
678 'export_callback' => function ($val) {
679 return $val;
680 },
681 'value' => $roomres['id']
682 ),
683 ));
684 }
685 }
686
687 // sort rows
688 $this->sortRows($pkrsort, $pkrorder);
689
690 // build the string for tot arrivals, departures, stayovers
691 $stay_type_tots = [
692 JText::translate('VBOARRIVING') . ': ' . $guests_stay_counter['arriving'],
693 JText::translate('VBODEPARTING') . ': ' . $guests_stay_counter['departing'],
694 JText::translate('VBOTYPESTAYOVER') . ': ' . $guests_stay_counter['stayover'],
695 ];
696
697 // build the string for total meal plans included
698 $meal_plan_tots = [];
699 foreach ($guest_meals_included as $meal_enum => $meal_pax) {
700 $meal_plan_tots[] = $default_meal_plans[$meal_enum] . ': ' . $meal_pax;
701 }
702
703 // push footer rows
704 $this->footerRow[] = [
705 [
706 'attr' => [
707 'class="vbo-report-total"',
708 'colspan="12"',
709 ],
710 'value' => '<h3><span class="vbo-report-footer-inlinedata">' . implode(' - ', $stay_type_tots) . '</span> <span class="vbo-report-footer-inlinedata">' . implode(' - ', $meal_plan_tots) . '</span></h3>',
711 ],
712 ];
713
714 // Debug
715 if ($this->debug) {
716 $this->setWarning('path to report file = '.urlencode(dirname(__FILE__)).'<br/>');
717 $this->setWarning('$bookings:<pre>'.print_r($bookings, true).'</pre><br/>');
718 }
719
720 return true;
721 }
722
723 /**
724 * Registers the name to give to the CSV file being exported.
725 *
726 * @return void
727 *
728 * @since 1.16.1 (J) - 1.6.1 (WP)
729 */
730 private function registerExportCSVFileName()
731 {
732 $pfromdate = VikRequest::getString('fromdate', '', 'request');
733 $ptodate = VikRequest::getString('todate', '', 'request');
734
735 $this->setExportCSVFileName($this->reportName . '-' . str_replace('/', '_', $pfromdate) . (!empty($ptodate) && $ptodate != $pfromdate ? '-' . str_replace('/', '_', $ptodate) : '') . '.csv');
736 }
737
738 /**
739 * Maps the 3-char country codes to their full names.
740 * Translates also the 'unknown' country.
741 *
742 * @param array $countries
743 *
744 * @return array
745 */
746 private function getCountriesMap($countries)
747 {
748 $map = array();
749
750 if (in_array('unknown', $countries)) {
751 $map['unknown'] = JText::translate('VBOREPORTTOPCUNKNC');
752 foreach ($countries as $k => $v) {
753 if ($v == 'unknown') {
754 unset($countries[$k]);
755 }
756 }
757 }
758
759 if (count($countries)) {
760 $clauses = array();
761 foreach ($countries as $country) {
762 array_push($clauses, $this->dbo->quote($country));
763 }
764 $q = "SELECT `country_name`,`country_3_code` FROM `#__vikbooking_countries` WHERE `country_3_code` IN (".implode(', ', $clauses).");";
765 $this->dbo->setQuery($q);
766 $records = $this->dbo->loadAssocList();
767 if ($records) {
768 foreach ($records as $v) {
769 $map[$v['country_3_code']] = $v['country_name'];
770 }
771 }
772 }
773
774 return $map;
775 }
776
777 /**
778 * Finds the ID and name of the rate plan from the given tariff ID.
779 *
780 * @param int $idtar the ID of the tariff.
781 *
782 * @return array list of rate plan name (or an empty string) and ID.
783 */
784 private function getPriceName($idtar)
785 {
786 if (empty($idtar)) {
787 return [JText::translate('VBOROOMCUSTRATEPLAN'), 0];
788 }
789
790 $q = "SELECT `p`.`id`, `p`.`name` FROM `#__vikbooking_prices` AS `p`
791 LEFT JOIN `#__vikbooking_dispcost` AS `t` ON `p`.`id`=`t`.`idprice` WHERE `t`.`id`=".(int)$idtar.";";
792 $this->dbo->setQuery($q);
793 $price_record = $this->dbo->loadAssoc();
794
795 if ($price_record) {
796 return [$price_record['name'], $price_record['id']];
797 }
798
799 return ['', 0];
800 }
801 }
802