PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.15
VikBooking Hotel Booking Engine & PMS v1.8.15
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 / einvoicing / einvoicing.php

einvoicing.php in VikBooking Hotel Booking Engine & PMS 1.8.15, at admin/helpers/einvoicing/einvoicing.php

1,137 lines 25.9 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 * Electronic Invoicing abstract class.
15 */
16 abstract class VikBookingEInvoicing
17 {
18 /**
19 * @var string
20 */
21 protected $driverFile = '';
22
23 /**
24 * @var string
25 */
26 protected $driverName = '';
27
28 /**
29 * @var int
30 */
31 protected $driverId = null;
32
33 /**
34 * @var array
35 */
36 protected $driverFilters = [];
37
38 /**
39 * @var array
40 */
41 protected $driverButtons = [];
42
43 /**
44 * @var string
45 */
46 protected $driverScript = '';
47
48 /**
49 * @var bool
50 */
51 protected $hasSettings = false;
52
53 /**
54 * @var array
55 */
56 protected $warning = [];
57
58 /**
59 * @var array
60 */
61 protected $error = [];
62
63 /**
64 * @var array
65 */
66 protected $info = [];
67
68 /**
69 * @var object
70 */
71 protected $dbo;
72
73 /**
74 * @var object
75 */
76 protected $session;
77
78 /**
79 * @var array
80 */
81 protected $cols = [];
82
83 /**
84 * @var array
85 */
86 protected $rows = [];
87
88 /**
89 * @var array
90 */
91 protected $footerRow = [];
92
93 /**
94 * @var mixed flag modifiable by external invokes (cron, analogic invoices)
95 */
96 public $externalCall = null;
97
98 /**
99 * @var array data injectable by external callers
100 */
101 public $externalData = [];
102
103 /**
104 * @var array
105 *
106 * @since 1.16.7 (J) - 1.6.7 (WP)
107 */
108 public $driverSettings = [];
109
110 /**
111 * Class constructor should define some vars of the driver in use.
112 */
113 public function __construct()
114 {
115 $this->dbo = JFactory::getDbo();
116 $this->session = JFactory::getSession();
117
118 /**
119 * Turn on the flag that the eInvocing class is running
120 * so that other parts of the framework can avoid to
121 * invoke again this class and to generate double e-invoices.
122 */
123 defined('VBO_EINVOICING_RUN') OR define('VBO_EINVOICING_RUN', 1);
124 }
125
126 /**
127 * Extending Classes should define this method
128 * to get the name of class file.
129 */
130 abstract public function getFileName();
131
132 /**
133 * Extending Classes should define this method
134 * to get the name of the driver.
135 */
136 abstract public function getName();
137
138 /**
139 * Extending Classes should define this method
140 * to get the filters of the driver.
141 */
142 abstract public function getFilters();
143
144 /**
145 * Extending Classes should define this method
146 * to get the driver action buttons.
147 */
148 abstract public function getButtons();
149
150 /**
151 * Extending Classes should define this method
152 * to generate the bookings data (cols and rows).
153 */
154 abstract public function getBookingsData();
155
156 /**
157 * Extending Classes should define this method
158 * to prepare the settings of the driver before saving.
159 * Views should not call this method.
160 */
161 abstract protected function prepareSavingSettings();
162
163 /**
164 * Extending Classes should define this method
165 * to generate the electronic invoices.
166 */
167 abstract public function generateEInvoices();
168
169 /**
170 * Extending Classes should define this method
171 * to generate the electronic invoice for
172 * the given booking ID or booking array.
173 */
174 abstract public function generateEInvoice($data);
175
176 /**
177 * Extending Classes should define this method
178 * to generate the electronic invoice from a custom
179 * invoice not related to any booking ID.
180 * Existing (analogic) invoice record and customer
181 * shall be passed as arguments.
182 */
183 abstract public function prepareCustomInvoiceData($invoice, $customer);
184
185 /**
186 * Extending Classes should define this method
187 * to check if an electronic invoice exists for
188 * a given booking ID or Number. This is useful
189 * for external scripts to request later the
190 * obliteration of the invoice to create a new one.
191 * For example, during the update of a custom invoice.
192 */
193 abstract public function eInvoiceExists($data);
194
195 /**
196 * Extending Classes should define this method
197 * to obliterate an existingelectronic invoice.
198 * This is useful for external scripts to request the
199 * obliteration of the invoice to create a new one.
200 * For example, during the update of a custom invoice.
201 */
202 abstract public function obliterateEInvoice($data);
203
204 /**
205 * Drivers can override this method to manipulate the reservation details
206 * at runtime, in case some values should be altered for certain reasons
207 * (i.e. environmental fee detucted and invoiced separately).
208 *
209 * @param array &$booking the reservation record to elaborate (may be nested).
210 * @param array &$rooms the room reservation records to elaborate.
211 *
212 * @return void
213 *
214 * @since 1.16.7 (J) - 1.6.7 (WP)
215 */
216 public function elaborateBookingDetails(array &$booking, array &$rooms = [])
217 {
218 return;
219 }
220
221 /**
222 * Drivers can override this method to register additional PDF invoice
223 * details for a specific reservation ID (i.e. a correlated invoice).
224 *
225 * @param int $bid the reservation record ID.
226 *
227 * @return array
228 *
229 * @since 1.16.7 (J) - 1.6.7 (WP)
230 */
231 public function getBookingExtraInvoices($bid)
232 {
233 return [];
234 }
235
236 /**
237 * Loads the settings for the driver, which parameters
238 * must be saved as a JSON encoded string. Never cache
239 * the settings fetched as they may be updated by other methods.
240 *
241 * @return mixed array if settings exist, false otherwise
242 */
243 protected function loadSettings()
244 {
245 if ($this->driverSettings) {
246 return $this->driverSettings;
247 }
248
249 $this->dbo->setQuery(
250 $this->dbo->getQuery(true)
251 ->select('*')
252 ->from($this->dbo->qn('#__vikbooking_einvoicing_config'))
253 ->where($this->dbo->qn('driver') . ' = ' . $this->dbo->q($this->getFileName()))
254 ->order($this->dbo->qn('id') . ' DESC')
255 , 0, 1);
256 $settings = $this->dbo->loadAssoc();
257
258 if (!$settings) {
259 // no settings defined for this driver
260 return false;
261 }
262
263 $settings['params'] = !empty($settings['params']) ? json_decode($settings['params'], true) : array();
264 $settings['params'] = is_array($settings['params']) ? $settings['params'] : array();
265
266 // update driverId
267 $this->driverId = $settings['id'];
268
269 // cache settings
270 $this->driverSettings = $settings;
271
272 // return array of settings with decoded parameters
273 return $this->driverSettings;
274 }
275
276 /**
277 * Resets and reloads the driver settings to avoid cache.
278 *
279 * @return array
280 *
281 * @since 1.16.7 (J) - 1.6.7 (WP)
282 */
283 protected function reloadSettings()
284 {
285 // reset
286 $this->driverSettings = [];
287
288 // reload
289 $this->loadSettings();
290
291 return $this->driverSettings;
292 }
293
294 /**
295 * Saves the settings for the driver.
296 * The View may call this method to save the driver settings.
297 *
298 * @return boolean true if settings were stored, false otherwise
299 */
300 public function saveSettings()
301 {
302 $data = $this->prepareSavingSettings();
303 if (!$data instanceof stdClass || !get_object_vars($data)) {
304 return false;
305 }
306
307 $q = "SELECT * FROM `#__vikbooking_einvoicing_config` WHERE `driver`=".$this->dbo->quote($this->getFileName())." ORDER BY `id` DESC LIMIT 1;";
308 $this->dbo->setQuery($q);
309 $current = $this->dbo->loadAssoc();
310 if (!$current) {
311 // create new driver record as it's the first time we're saving the settings
312 if (!$this->dbo->insertObject('#__vikbooking_einvoicing_config', $data, 'id')) {
313 return false;
314 }
315 } else {
316 // update the driver record with the new settings
317 $data->id = $current['id'];
318 if (!$this->dbo->updateObject('#__vikbooking_einvoicing_config', $data, 'id')) {
319 return false;
320 }
321 }
322
323 $this->setInfo(JText::translate('VBODRIVERSETTSUPD'));
324
325 return true;
326 }
327
328 /**
329 * Updates one setting in the current driver configuration.
330 *
331 * @param string $name the setting name to set.
332 * @param mixed $value the setting value to set.
333 *
334 * @return bool
335 *
336 * @since 1.16.7 (J) - 1.6.7 (WP)
337 */
338 public function updateDriverSetting($name, $value = null)
339 {
340 $settings = $this->loadSettings();
341 $settings['params'][$name] = $value;
342
343 $record = new stdClass;
344 $record->id = $this->getDriverId();
345 $record->params = json_encode($settings['params']);
346
347 $result = (bool)$this->dbo->updateObject('#__vikbooking_einvoicing_config', $record, 'id');
348
349 // reload driver settings
350 $this->reloadSettings();
351
352 return $result;
353 }
354
355 /**
356 * Returns whether the driver has settings to be defined.
357 *
358 * @return boolean
359 */
360 public function hasSettings()
361 {
362 return $this->hasSettings;
363 }
364
365 /**
366 * Echoes the HTML required for the driver settings form.
367 * This method should be extended by the driver if settings are needed.
368 *
369 * @return void
370 */
371 public function printSettings()
372 {
373 return;
374 }
375
376 /**
377 * Echoes the HTML required for the driver overlay form.
378 * This method should be extended by the driver if needed.
379 * For example, to show contents within a modal.
380 *
381 * @return void
382 */
383 public function printOverlayContent()
384 {
385 return;
386 }
387
388 /**
389 * Returns the ID of the current driver by taking it from the settings.
390 * By calling loadSettings(), if some settings are defined, the ID is set in driverId.
391 *
392 * @return mixed the ID of the current driver if some settings were stored or null.
393 *
394 * @uses loadSettings
395 */
396 protected function getDriverId()
397 {
398 if (is_null($this->driverId)) {
399 // this method will set a value for the property driverId if some settings were saved
400 $this->loadSettings();
401 }
402
403 return $this->driverId;
404 }
405
406 /**
407 * Stores an electronic invoice. If the booking ID is passed through the data
408 * the system will set to obliterated all e-invoices previously made for that booking.
409 * Custom (manual) invoices should be obliterated by the external caller script instead.
410 *
411 * @param object $data stdClass object with the data to store
412 *
413 * @return mixed the ID of the invoice stored, false otherwise
414 */
415 protected function storeEInvoice($data)
416 {
417 if (!$data instanceof stdClass || !get_object_vars($data)) {
418 return false;
419 }
420
421 if (isset($data->idorder) && (int)$data->idorder > 0) {
422 // obliterate any possible previous e-invoice for this booking
423 $q = "UPDATE `#__vikbooking_einvoicing_data` SET `obliterated`=1 WHERE `driverid`=".(int)$this->driverId." AND `idorder`=".(int)$data->idorder.";";
424 $this->dbo->setQuery($q);
425 $this->dbo->execute();
426 }
427
428 if ($this->dbo->insertObject('#__vikbooking_einvoicing_data', $data, 'id')) {
429 return $data->id;
430 }
431
432 return false;
433 }
434
435 /**
436 * Updates the information for an electronic invoice.
437 *
438 * @param object $data stdClass object with the data to store
439 *
440 * @return boolean
441 */
442 protected function updateEInvoice($data)
443 {
444 if (!$data instanceof stdClass || !get_object_vars($data)) {
445 return false;
446 }
447
448 if ($this->dbo->updateObject('#__vikbooking_einvoicing_data', $data, 'id')) {
449 return true;
450 }
451
452 return false;
453 }
454
455 /**
456 * Checks whether an analogic invoice in PDF was already issued
457 * for the given booking ID. This is to avoid double PDF invoices.
458 *
459 * @param int $idorder the ID of the booking
460 *
461 * @return bool
462 */
463 protected function hasAnalogicInvoice($idorder)
464 {
465 if (empty($idorder)) {
466 return false;
467 }
468
469 $this->dbo->setQuery(
470 $this->dbo->getQuery(true)
471 ->select($this->dbo->qn('id'))
472 ->from($this->dbo->qn('#__vikbooking_invoices'))
473 ->where($this->dbo->qn('idorder') . ' = ' . (int)$idorder)
474 );
475
476 if ($this->dbo->loadResult()) {
477 return true;
478 }
479
480 return false;
481 }
482
483 /**
484 * Generates an analogic invoice in PDF format for the given booking ID.
485 *
486 * @param int $idorder the ID of the booking.
487 * @param int $invnum the number of the invoice.
488 * @param string $invdate the date for the invoice in Y-m-d format.
489 *
490 * @return boolean
491 */
492 protected function generateAnalogicInvoice($idorder, $invnum, $invdate)
493 {
494 if (empty($idorder)) {
495 return false;
496 }
497
498 // get booking record
499 $this->dbo->setQuery(
500 $this->dbo->getQuery(true)
501 ->select($this->dbo->qn('o') . '.*')
502 ->select($this->dbo->qn('co.idcustomer'))
503 ->select('CONCAT_WS(\' \', ' . $this->dbo->qn('c.first_name') . ', ' . $this->dbo->qn('c.last_name') . ') AS ' . $this->dbo->qn('customer_name'))
504 ->select([
505 $this->dbo->qn('c.pin', 'customer_pin'),
506 $this->dbo->qn('nat.country_name'),
507 ])
508 ->from($this->dbo->qn('#__vikbooking_orders', 'o'))
509 ->leftJoin($this->dbo->qn('#__vikbooking_customers_orders', 'co') . ' ON ' . $this->dbo->qn('co.idorder') . ' = ' . $this->dbo->qn('o.id'))
510 ->leftJoin($this->dbo->qn('#__vikbooking_customers', 'c') . ' ON ' . $this->dbo->qn('c.id') . ' = ' . $this->dbo->qn('co.idcustomer'))
511 ->leftJoin($this->dbo->qn('#__vikbooking_countries', 'nat') . ' ON ' . $this->dbo->qn('nat.country_3_code') . ' = ' . $this->dbo->qn('o.country'))
512 ->where($this->dbo->qn('o.id') . ' = ' . (int)$idorder)
513 ->where($this->dbo->qn('o.status') . ' = ' . $this->dbo->q('confirmed'))
514 ->where($this->dbo->qn('o.total') . ' > 0')
515 );
516
517 $booking = $this->dbo->loadAssoc();
518
519 if (!$booking) {
520 return false;
521 }
522
523 try {
524 $res = VikBooking::generateBookingInvoice($booking, $invnum, '', date($this->getDateFormat(), strtotime($invdate)));
525 } catch (Exception $e) {
526 return false;
527 }
528
529 return $res;
530 }
531
532 /**
533 * Loads an helper file to obtain HTML content within a buffer.
534 *
535 * @param string the path to the layout/helper file to include
536 * @param array the vars needed by the layout/helper file
537 *
538 * @return string the HTML content to print, or an empty string
539 */
540 protected function loadHelperFile($fpath, $data = array())
541 {
542 if (!is_file($fpath)) {
543 return '';
544 }
545
546 // capture the content of the layout/helper file within a buffer
547 ob_start();
548 include $fpath;
549 $content = ob_get_contents();
550 ob_end_clean();
551
552 // return the content to be displayed
553 return $content;
554 }
555
556 /**
557 * Requires an helper file.
558 *
559 * @return void
560 */
561 protected function importHelper($fpath)
562 {
563 if (!is_file($fpath)) {
564 return;
565 }
566
567 require_once $fpath;
568 }
569
570 /**
571 * Returns whether the driver has some session filters to
572 * immediately call getBookingsData() when the page loads.
573 * Child classes could override this method depending on the needs.
574 *
575 * @return boolean
576 */
577 public function hasFiltersSet()
578 {
579 return false;
580 }
581
582 /**
583 * Loads the jQuery UI Datepicker.
584 * Method used only by sub-classes.
585 *
586 * @return self
587 */
588 protected function loadDatePicker()
589 {
590 $vbo_app = VikBooking::getVboApplication();
591 $vbo_app->loadDatePicker();
592
593 return $this;
594 }
595
596 /**
597 * Loads all the rooms in VBO and returns the array.
598 *
599 * @return array associative array with key=ID value=data
600 */
601 protected function getRooms()
602 {
603 $rooms = [];
604
605 $q = "SELECT * FROM `#__vikbooking_rooms` ORDER BY `name` ASC;";
606 $this->dbo->setQuery($q);
607 $all = $this->dbo->loadAssocList();
608
609 foreach ($all as $r) {
610 $rooms[$r['id']] = $r;
611 }
612
613 return $rooms;
614 }
615
616 /**
617 * Concatenates the JavaScript rules.
618 * Method used only by sub-classes.
619 *
620 * @param string $str
621 *
622 * @return self
623 */
624 protected function setScript($str)
625 {
626 $this->driverScript .= $str."\n";
627
628 return $this;
629 }
630
631 /**
632 * Returns the aliquote number from the given record ID.
633 *
634 * @param int $idvat the ID of the IVA record
635 *
636 * @return float the aliquot found or 0
637 */
638 protected function getAliquoteById($idvat)
639 {
640 $aliq = 0;
641
642 if (!empty($idvat)) {
643 $q = "SELECT `aliq` FROM `#__vikbooking_iva` WHERE `id`=" . (int)$idvat . ";";
644 $this->dbo->setQuery($q);
645 $aliq = $this->dbo->loadResult();
646
647 if (!$aliq) {
648 return 0;
649 }
650 }
651
652 return floatval($aliq);
653 }
654
655 /**
656 * Returns the aliquote number from the given price ID.
657 *
658 * @param int $idvat the ID of the IVA record
659 *
660 * @return float the aliquot found or 0
661 */
662 protected function getAliquoteFromPriceId($idprice)
663 {
664 $aliq = 0;
665
666 if (!empty($idprice)) {
667 $q = "SELECT `p`.`idiva`,`i`.`aliq` FROM `#__vikbooking_prices` AS `p` LEFT JOIN `#__vikbooking_iva` `i` ON `i`.`id`=`p`.`idiva` WHERE `p`.`id`=".(int)$idprice.";";
668 $this->dbo->setQuery($q);
669 $data = $this->dbo->loadAssoc();
670 if ($data) {
671 $aliq = $data['aliq'];
672 }
673 }
674
675 return floatval($aliq);
676 }
677
678 /**
679 * Updates the current invoice number for later use.
680 *
681 * @param int $invnum the invoice number to set
682 *
683 * @return void
684 */
685 protected function updateInvoiceNumber($invnum)
686 {
687 VBOFactory::getConfig()->set('invoiceinum', (int)$invnum);
688 }
689
690 /**
691 * Updates the progrssive number for the data transmission for this driver.
692 *
693 * @param int $num the prograssive number to set (should be already increased)
694 *
695 * @return void
696 */
697 protected function updateProgressiveNumber($num)
698 {
699 $q = "UPDATE `#__vikbooking_einvoicing_config` SET `progcount`=".(int)$num." WHERE `driver`=".$this->dbo->quote($this->getFileName()).";";
700 $this->dbo->setQuery($q);
701 $this->dbo->execute();
702
703 // reload driver settings
704 $this->reloadSettings();
705 }
706
707 /**
708 * Attempts to format the given XML string through DOMDocument.
709 * This method takes the XML string by reference.
710 *
711 * @param string $xml the XML string to be formatted
712 *
713 * @return string the formatted string or the original string
714 */
715 protected function formatXmlString(&$xml)
716 {
717 if (!class_exists('DOMDocument')) {
718 // we cannot format the XML because DOMDocument is missing
719 return $xml;
720 }
721
722 $dom = new DOMDocument;
723 $dom->preserveWhiteSpace = false;
724 $dom->loadXML($xml);
725 $dom->formatOutput = true;
726 $xml = $dom->saveXML();
727
728 return $xml;
729 }
730
731 /**
732 * Returns whether the debug request has been set.
733 *
734 * @return boolean
735 */
736 protected function debugging()
737 {
738 $debug = VikRequest::getInt('e4j_debug', 0, 'request');
739
740 return $debug;
741 }
742
743 /**
744 * Gets the current script string.
745 *
746 * @return string
747 */
748 public function getScript()
749 {
750 return rtrim($this->driverScript, "\n");
751 }
752
753 /**
754 * Returns the date format in VBO for date, jQuery UI, Joomla.
755 * Method used only by sub-classes.
756 *
757 * @param string $type
758 *
759 * @return string
760 */
761 protected function getDateFormat($type = 'date')
762 {
763 $nowdf = VikBooking::getDateFormat();
764 if ($nowdf == "%d/%m/%Y") {
765 $df = 'd/m/Y';
766 $juidf = 'dd/mm/yy';
767 } elseif ($nowdf == "%m/%d/%Y") {
768 $df = 'm/d/Y';
769 $juidf = 'mm/dd/yy';
770 } else {
771 $df = 'Y/m/d';
772 $juidf = 'yy/mm/dd';
773 }
774
775 switch ($type) {
776 case 'jui':
777 return $juidf;
778 case 'joomla':
779 return $nowdf;
780 default:
781 return $df;
782 }
783 }
784
785 /**
786 * Returns the translated weekday.
787 * Uses the back-end language definitions.
788 *
789 * @param int $wday
790 * @param string $type use 'long' for the full name of the week, short for the 3-char version
791 *
792 * @return string
793 */
794 protected function getWdayString($wday, $type = 'long')
795 {
796 $wdays_map_long = array(
797 JText::translate('VBWEEKDAYZERO'),
798 JText::translate('VBWEEKDAYONE'),
799 JText::translate('VBWEEKDAYTWO'),
800 JText::translate('VBWEEKDAYTHREE'),
801 JText::translate('VBWEEKDAYFOUR'),
802 JText::translate('VBWEEKDAYFIVE'),
803 JText::translate('VBWEEKDAYSIX')
804 );
805
806 $wdays_map_short = array(
807 JText::translate('VBSUN'),
808 JText::translate('VBMON'),
809 JText::translate('VBTUE'),
810 JText::translate('VBWED'),
811 JText::translate('VBTHU'),
812 JText::translate('VBFRI'),
813 JText::translate('VBSAT')
814 );
815
816 if ($type != 'long') {
817 return isset($wdays_map_short[(int)$wday]) ? $wdays_map_short[(int)$wday] : '';
818 }
819
820 return isset($wdays_map_long[(int)$wday]) ? $wdays_map_long[(int)$wday] : '';
821 }
822
823 /**
824 * Returns the translated month.
825 * Uses the back-end language definitions.
826 *
827 * @param int $month the month to convert (from 1 to 12)
828 *
829 * @return string
830 */
831 protected function getMonthString($mon)
832 {
833 $mon--;
834
835 $months_map_long = array(
836 JText::translate('VBMONTHONE'),
837 JText::translate('VBMONTHTWO'),
838 JText::translate('VBMONTHTHREE'),
839 JText::translate('VBMONTHFOUR'),
840 JText::translate('VBMONTHFIVE'),
841 JText::translate('VBMONTHSIX'),
842 JText::translate('VBMONTHSEVEN'),
843 JText::translate('VBMONTHEIGHT'),
844 JText::translate('VBMONTHNINE'),
845 JText::translate('VBMONTHTEN'),
846 JText::translate('VBMONTHELEVEN'),
847 JText::translate('VBMONTHTWELVE')
848 );
849
850 return isset($months_map_long[(int)$mon]) ? $months_map_long[(int)$mon] : '';
851 }
852
853 /**
854 * Replaces accents and special characters to their non-special version.
855 * Takes also rid of the ampersand symbol that would break the XML.
856 *
857 * @param string $string the string to parse
858 *
859 * @return string
860 */
861 protected function convertSpecials($string)
862 {
863 // map of special characters
864 $table = array(
865 'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', '�
866 '=>'A', 'Ă'=>'A', 'Ā'=>'A', 'Ą'=>'A', 'Æ'=>'A', 'Ǽ'=>'A',
867 'à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'ă'=>'a', 'ā'=>'a', '�
868 '=>'a', 'æ'=>'a', 'ǽ'=>'a',
869
870 'Þ'=>'B', 'þ'=>'b', 'ß'=>'Ss',
871
872 'Ç'=>'C', 'Č'=>'C', 'Ć'=>'C', 'Ĉ'=>'C', 'Ċ'=>'C',
873 'ç'=>'c', 'č'=>'c', 'ć'=>'c', 'ĉ'=>'c', 'ċ'=>'c',
874
875 'Đ'=>'Dj', 'Ď'=>'D',
876 'đ'=>'dj', 'ď'=>'d',
877
878 'È'=>'E', 'É'=>'E', 'Ê'=>'E', 'Ë'=>'E', 'Ĕ'=>'E', 'Ē'=>'E', 'Ę'=>'E', 'Ė'=>'E',
879 'è'=>'e', 'é'=>'e', 'ê'=>'e', 'ë'=>'e', 'ĕ'=>'e', 'ē'=>'e', 'ę'=>'e', 'ė'=>'e',
880
881 'Ĝ'=>'G', 'Ğ'=>'G', 'Ġ'=>'G', 'Ģ'=>'G',
882 'ĝ'=>'g', 'ğ'=>'g', 'ġ'=>'g', 'ģ'=>'g',
883
884 'Ĥ'=>'H', 'Ħ'=>'H',
885 'ĥ'=>'h', 'ħ'=>'h',
886
887 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'İ'=>'I', 'Ĩ'=>'I', 'Ī'=>'I', 'Ĭ'=>'I', 'Į'=>'I',
888 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'į'=>'i', 'ĩ'=>'i', 'ī'=>'i', 'ĭ'=>'i', 'ı'=>'i',
889
890 'Ĵ'=>'J',
891 'ĵ'=>'j',
892
893 'Ķ'=>'K',
894 'ķ'=>'k', 'ĸ'=>'k',
895
896 'Ĺ'=>'L', 'Ļ'=>'L', 'Ľ'=>'L', 'Ŀ'=>'L', 'Ł'=>'L',
897 'ĺ'=>'l', 'ļ'=>'l', 'ľ'=>'l', 'ŀ'=>'l', 'ł'=>'l',
898
899 'Ñ'=>'N', 'Ń'=>'N', 'Ň'=>'N', '�
900 '=>'N', 'Ŋ'=>'N',
901 'ñ'=>'n', 'ń'=>'n', 'ň'=>'n', 'ņ'=>'n', 'ŋ'=>'n', 'ʼn'=>'n',
902
903 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O', 'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O', 'Ō'=>'O', 'Ŏ'=>'O', 'Ő'=>'O', 'Œ'=>'O',
904 'ò'=>'o', 'ó'=>'o', 'ô'=>'o', 'õ'=>'o', 'ö'=>'o', 'ø'=>'o', 'ō'=>'o', 'ŏ'=>'o', 'ő'=>'o', 'œ'=>'o', 'ð'=>'o',
905
906 'Ŕ'=>'R', 'Ř'=>'R',
907 'ŕ'=>'r', 'ř'=>'r', 'ŗ'=>'r',
908
909 'Š'=>'S', 'Ŝ'=>'S', 'Ś'=>'S', 'Ş'=>'S',
910 'š'=>'s', 'ŝ'=>'s', 'ś'=>'s', 'ş'=>'s',
911
912 'Ŧ'=>'T', 'Ţ'=>'T', 'Ť'=>'T',
913 'ŧ'=>'t', 'ţ'=>'t', 'ť'=>'t',
914
915 'Ù'=>'U', 'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ũ'=>'U', 'Ū'=>'U', 'Ŭ'=>'U', 'Ů'=>'U', 'Ű'=>'U', 'Ų'=>'U',
916 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ü'=>'u', 'ũ'=>'u', 'ū'=>'u', 'ŭ'=>'u', 'ů'=>'u', 'ű'=>'u', 'ų'=>'u',
917
918 'Ŵ'=>'W', 'Ẁ'=>'W', 'Ẃ'=>'W', 'Ẅ'=>'W',
919 'ŵ'=>'w', 'ẁ'=>'w', 'ẃ'=>'w', '�
920 '=>'w',
921
922 'Ý'=>'Y', 'Ÿ'=>'Y', 'Ŷ'=>'Y',
923 'ý'=>'y', 'ÿ'=>'y', 'ŷ'=>'y',
924
925 'Ž'=>'Z', 'Ź'=>'Z', 'Ż'=>'Z',
926 'ž'=>'z', 'ź'=>'z', 'ż'=>'z',
927 '+'=>'',
928 );
929
930 $string = strtr($string, $table);
931
932 $string = preg_replace("/[^\x9\xA\xD\x20-\x7F]/u", "", $string);
933
934 // replace ampersand
935 $string = str_replace('&', 'and', $string);
936 // convert to HTML entities for a safe XML content
937 $string = htmlentities($string);
938 // remove any ampersand added by htmlentities()
939 $string = str_replace('&', '', $string);
940
941 return $string;
942 }
943
944 /**
945 * Sets the columns for this driver.
946 *
947 * @param array $arr
948 *
949 * @return self
950 */
951 protected function setDriverCols($arr)
952 {
953 $this->cols = $arr;
954
955 return $this;
956 }
957
958 /**
959 * Returns the columns for this driver.
960 * Should be called after getBookingsData()
961 * or the returned array will be empty.
962 *
963 * @return array
964 */
965 public function getDriverCols()
966 {
967 return $this->cols;
968 }
969
970 /**
971 * Sorts the rows of the driver by key.
972 *
973 * @param string $krsort the key attribute of the array pairs
974 * @param string $krorder ascending (ASC) or descending (DESC)
975 *
976 * @return void
977 */
978 protected function sortRows($krsort, $krorder)
979 {
980 if (empty($krsort) || !(count($this->rows))) {
981 return;
982 }
983
984 $map = array();
985 foreach ($this->rows as $k => $row) {
986 foreach ($row as $kk => $v) {
987 if (isset($v['key']) && $v['key'] == $krsort) {
988 $map[$k] = $v['value'];
989 }
990 }
991 }
992 if (!(count($map))) {
993 return;
994 }
995
996 if ($krorder == 'ASC') {
997 asort($map);
998 } else {
999 arsort($map);
1000 }
1001
1002 $sorted = array();
1003 foreach ($map as $k => $v) {
1004 $sorted[$k] = $this->rows[$k];
1005 }
1006
1007 $this->rows = $sorted;
1008 }
1009
1010 /**
1011 * Sets the rows for this driver.
1012 *
1013 * @param array $arr
1014 *
1015 * @return self
1016 */
1017 protected function setDriverRows($arr)
1018 {
1019 $this->rows = $arr;
1020
1021 return $this;
1022 }
1023
1024 /**
1025 * Returns the rows for this driver.
1026 * Should be called after getBookingsData()
1027 * or the returned array will be empty.
1028 *
1029 * @return array
1030 */
1031 public function getDriverRows()
1032 {
1033 return $this->rows;
1034 }
1035
1036 /**
1037 * Sets the footer row (the totals) for this driver.
1038 *
1039 * @param array $arr
1040 *
1041 * @return self
1042 */
1043 protected function setDriverFooterRow($arr)
1044 {
1045 $this->footerRow = $arr;
1046
1047 return $this;
1048 }
1049
1050 /**
1051 * Returns the footer row for this driver.
1052 * Should be called after getBookingsData()
1053 * or the returned array will be empty.
1054 *
1055 * @return array
1056 */
1057 public function getDriverFooterRow()
1058 {
1059 return $this->footerRow;
1060 }
1061
1062 /**
1063 * Sets warning messages by concatenating the existing ones.
1064 * Method used only by sub-classes.
1065 *
1066 * @param string $str
1067 *
1068 * @return self
1069 */
1070 protected function setWarning($str)
1071 {
1072 $this->warning[] = $str;
1073
1074 return $this;
1075 }
1076
1077 /**
1078 * Gets the current warning string.
1079 *
1080 * @return string
1081 */
1082 public function getWarning()
1083 {
1084 return implode('<br/>', $this->warning);
1085 }
1086
1087 /**
1088 * Sets errors by concatenating the existing ones.
1089 * Method used only by sub-classes.
1090 *
1091 * @param string $str
1092 *
1093 * @return self
1094 */
1095 protected function setError($str)
1096 {
1097 $this->error[] = $str;
1098
1099 return $this;
1100 }
1101
1102 /**
1103 * Gets the current error string.
1104 *
1105 * @return string
1106 */
1107 public function getError()
1108 {
1109 return implode('<br/>', $this->error);
1110 }
1111
1112 /**
1113 * Sets info messages by concatenating the existing ones.
1114 * Method used only by sub-classes.
1115 *
1116 * @param string $str
1117 *
1118 * @return self
1119 */
1120 protected function setInfo($str)
1121 {
1122 $this->info[] = $str;
1123
1124 return $this;
1125 }
1126
1127 /**
1128 * Gets the current info string.
1129 *
1130 * @return string
1131 */
1132 public function getInfo()
1133 {
1134 return implode('<br/>', $this->info);
1135 }
1136 }
1137