File "transactions_report.php"

Full Path: /home/romayxjt/public_html/wp-content/plugins/vikbooking/admin/helpers/report/transactions_report.php
File size: 13.19 KB
MIME-type: text/x-php
Charset: utf-8

<?php
/**
 * @package     VikBooking
 * @subpackage  com_vikbooking
 * @author      Alessio Gaggii - e4j - Extensionsforjoomla.com
 * @copyright   Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 * @link        https://vikwp.com
 */

defined('ABSPATH') or die('No script kiddies please!');

/**
 * Transactions child Class of VikBookingReport
 */
class VikBookingReportTransactionsReport extends VikBookingReport
{
	/**
	 * Property 'defaultKeySort' is used by the View that renders the report.
	 */
	public $defaultKeySort = 'tot';

	/**
	 * Property 'defaultKeyOrder' is used by the View that renders the report.
	 */
	public $defaultKeyOrder = 'DESC';

	/**
	 * Property 'exportAllowed' is used by the View to display the export button.
	 */
	public $exportAllowed = 1;

	/**
	 * Debug mode is activated by passing the value 'e4j_debug' > 0
	 */
	private $debug;

	/**
	 * Class constructor should define the name of the report and
	 * other vars. Call the parent constructor to define the DB object.
	 */
	public function __construct()
	{
		$this->reportFile = basename(__FILE__, '.php');
		$this->reportName = JText::translate('VBOREPORT'.strtoupper(str_replace('_', '', $this->reportFile)));
		$this->reportFilters = array();

		$this->cols = array();
		$this->rows = array();
		$this->footerRow = array();

		$this->debug = (VikRequest::getInt('e4j_debug', 0, 'request') > 0);

		$this->registerExportCSVFileName();

		parent::__construct();
	}

	/**
	 * Returns the name of this report.
	 *
	 * @return 	string
	 */
	public function getName()
	{
		return $this->reportName;
	}

	/**
	 * Returns the name of this file without .php.
	 *
	 * @return 	string
	 */
	public function getFileName()
	{
		return $this->reportFile;
	}

	/**
	 * Returns the filters of this report.
	 *
	 * @return 	array
	 */
	public function getFilters()
	{
		if (count($this->reportFilters)) {
			//do not run this method twice, as it could load JS and CSS files.
			return $this->reportFilters;
		}

		//get VBO Application Object
		$vbo_app = VikBooking::getVboApplication();

		//load the jQuery UI Datepicker
		$this->loadDatePicker();

		//From Date Filter
		$filter_opt = array(
			'label' => '<label for="fromdate">'.JText::translate('VBOREPORTSDATEFROM').'</label>',
			'html' => '<input type="text" id="fromdate" name="fromdate" value="" class="vbo-report-datepicker vbo-report-datepicker-from" />',
			'type' => 'calendar',
			'name' => 'fromdate'
		);
		array_push($this->reportFilters, $filter_opt);

		//To Date Filter
		$filter_opt = array(
			'label' => '<label for="todate">'.JText::translate('VBOREPORTSDATETO').'</label>',
			'html' => '<input type="text" id="todate" name="todate" value="" class="vbo-report-datepicker vbo-report-datepicker-to" />',
			'type' => 'calendar',
			'name' => 'todate'
		);
		array_push($this->reportFilters, $filter_opt);

		//Room ID filter
		$pidroom = VikRequest::getInt('idroom', '', 'request');
		$all_rooms = $this->getRooms();
		$rooms = array();
		foreach ($all_rooms as $room) {
			$rooms[$room['id']] = $room['name'];
		}
		if (count($rooms)) {
			$rooms_sel_html = $vbo_app->getNiceSelect($rooms, $pidroom, 'idroom', JText::translate('VBOSTATSALLROOMS'), JText::translate('VBOSTATSALLROOMS'), '', '', 'idroom');
			$filter_opt = array(
				'label' => '<label for="idroom">'.JText::translate('VBOREPORTSROOMFILT').'</label>',
				'html' => $rooms_sel_html,
				'type' => 'select',
				'name' => 'idroom'
			);
			array_push($this->reportFilters, $filter_opt);
		}

		// get minimum check-in and maximum check-out for dates filters
		$df = $this->getDateFormat();
		$mincheckin = 0;
		$maxcheckout = 0;
		$q = "SELECT MIN(`checkin`) AS `mincheckin`, MAX(`checkout`) AS `maxcheckout` FROM `#__vikbooking_orders` WHERE `status`='confirmed' AND `closure`=0;";
		$this->dbo->setQuery($q);
		$this->dbo->execute();
		if ($this->dbo->getNumRows()) {
			$data = $this->dbo->loadAssoc();
			if (!empty($data['mincheckin']) && !empty($data['maxcheckout'])) {
				$mincheckin = $data['mincheckin'];
				$maxcheckout = $data['maxcheckout'];
			}
		}
		//

		//jQuery code for the datepicker calendars and select2
		$pfromdate = VikRequest::getString('fromdate', '', 'request');
		$ptodate = VikRequest::getString('todate', '', 'request');
		$js = 'jQuery(function() {
			jQuery(".vbo-report-datepicker:input").datepicker({
				'.(!empty($mincheckin) ? 'minDate: "'.date($df, $mincheckin).'", ' : '').'
				'.(!empty($maxcheckout) ? 'maxDate: "'.date($df, $maxcheckout).'", ' : '').'
				dateFormat: "'.$this->getDateFormat('jui').'",
				onSelect: vboReportCheckDates
			});
			'.(!empty($pfromdate) ? 'jQuery(".vbo-report-datepicker-from").datepicker("setDate", "'.$pfromdate.'");' : '').'
			'.(!empty($ptodate) ? 'jQuery(".vbo-report-datepicker-to").datepicker("setDate", "'.$ptodate.'");' : '').'
		});
		function vboReportCheckDates(selectedDate, inst) {
			if (selectedDate === null || inst === null) {
				return;
			}
			var cur_from_date = jQuery(this).val();
			if (jQuery(this).hasClass("vbo-report-datepicker-from") && cur_from_date.length) {
				var nowstart = jQuery(this).datepicker("getDate");
				var nowstartdate = new Date(nowstart.getTime());
				jQuery(".vbo-report-datepicker-to").datepicker("option", {minDate: nowstartdate});
			}
		}';
		$this->setScript($js);

		return $this->reportFilters;
	}

	/**
	 * Loads the report data from the DB.
	 * Returns true in case of success, false otherwise.
	 * Sets the columns and rows for the report to be displayed.
	 *
	 * @return 	boolean
	 */
	public function getReportData()
	{
		if (strlen($this->getError())) {
			//Export functions may set errors rather than exiting the process, and the View may continue the execution to attempt to render the report.
			return false;
		}
		//Input fields and other vars
		$pfromdate = VikRequest::getString('fromdate', '', 'request');
		$ptodate = VikRequest::getString('todate', '', 'request');
		$pidroom = VikRequest::getInt('idroom', '', 'request');
		$pkrsort = VikRequest::getString('krsort', $this->defaultKeySort, 'request');
		$pkrsort = empty($pkrsort) ? $this->defaultKeySort : $pkrsort;
		$pkrorder = VikRequest::getString('krorder', $this->defaultKeyOrder, 'request');
		$pkrorder = empty($pkrorder) ? $this->defaultKeyOrder : $pkrorder;
		$pkrorder = $pkrorder == 'DESC' ? 'DESC' : 'ASC';
		$currency_symb = VikBooking::getCurrencySymb();
		$df = $this->getDateFormat();
		$datesep = VikBooking::getDateSeparator();
		if (empty($ptodate)) {
			$ptodate = $pfromdate;
		}
		//Get dates timestamps
		$from_ts = VikBooking::getDateTimestamp($pfromdate, 0, 0);
		$to_ts = VikBooking::getDateTimestamp($ptodate, 23, 59, 59);
		if (empty($pfromdate) || empty($from_ts) || empty($to_ts)) {
			$this->setError(JText::translate('VBOREPORTSERRNODATES'));
			return false;
		}

		/**
		 * We do not make a left join onto the orders roooms table
		 * or we may obtain multiple records with the query. Since we cannot
		 * group by booking ID as we need all history logs of type PU,
		 * we try to make a sub-query to concatenate all room IDs.
		 */

		// Query to obtain the records
		$bookings = array();
		$q = "SELECT `o`.`id`,`o`.`ts`,`o`.`days`,`o`.`checkin`,`o`.`checkout`,`o`.`totpaid`,`o`.`idpayment`,`o`.`roomsnum`,`o`.`total`,`o`.`idorderota`,`o`.`channel`,`o`.`country`,`o`.`tot_taxes`,".
			"`o`.`tot_city_taxes`,`o`.`tot_fees`,`o`.`cmms`,`h`.`dt`,`h`.`data`, (SELECT GROUP_CONCAT(`or`.`idroom` SEPARATOR ';') FROM `#__vikbooking_ordersrooms` AS `or` WHERE `or`.`idorder`=`o`.`id`) AS `idrooms` ".
			"FROM `#__vikbooking_orders` AS `o` LEFT JOIN `#__vikbooking_orderhistory` AS `h` ON `h`.`idorder`=`o`.`id` AND `h`.`type`='PU' ".
			"WHERE `o`.`status`='confirmed' AND `o`.`closure`=0 AND `o`.`totpaid` > 0 ".
			"AND ((`o`.`ts`>=".$from_ts." AND `o`.`ts`<=".$to_ts.") OR (`h`.`data` IS NOT NULL AND `h`.`dt`>=".$this->dbo->quote(date('Y-m-d H:i:s', $from_ts))." AND `h`.`dt`<=".$this->dbo->quote(date('Y-m-d H:i:s', $to_ts)).")) ".
			"ORDER BY `o`.`ts` ASC, `h`.`dt` DESC;";
		$this->dbo->setQuery($q);
		$this->dbo->execute();
		if ($this->dbo->getNumRows()) {
			$bookings = $this->dbo->loadAssocList();
			// apply room ID filter that was not used via SQL
			if (!empty($pidroom)) {
				foreach ($bookings as $k => $gbook) {
					$roomids = explode(';', $gbook['idrooms']);
					if (!in_array($pidroom, $roomids)) {
						unset($bookings[$k]);
					}
				}
			}
		}
		if (!count($bookings)) {
			$this->setError(JText::translate('VBOREPORTSERRNORESERV'));
			return false;
		}

		// Debug
		// $this->setWarning('<pre>'.print_r($bookings, true).'</pre><br/>');
		//

		//define the columns of the report
		$this->cols = array(
			//date
			array(
				'key' => 'paymeth',
				'sortable' => 1,
				'label' => JText::translate('VBPAYMENTMETHOD')
			),
			//rooms sold
			array(
				'key' => 'tot',
				'attr' => array(
					'class="center"'
				),
				'sortable' => 1,
				'label' => JText::translate('VBLIBSIX')
			),
			//bookings affected
			array(
				'key' => 'ids',
				'attr' => array(
					'class="center"'
				),
				'sortable' => 0,
				'ignore_export' => 0,
				'label' => JText::translate('VBMENUTHREE')
			),
		);

		$paystats = array();
		$allbids  = array();

		// loop over the bookings to build the payment stats
		foreach ($bookings as &$gbook) {
			if (!in_array($gbook['id'], $allbids)) {
				array_push($allbids, $gbook['id']);
			}
			$payname = null;
			if (!empty($gbook['data'])) {
				$paydata = json_decode($gbook['data']);
				$payname = is_object($paydata) && isset($paydata->payment_method) ? $paydata->payment_method : $payname;
				// update totpaid if history data available
				if (is_object($paydata) && isset($paydata->amount_paid) && $paydata->amount_paid > 0) {
					$gbook['totpaid'] = (float)$paydata->amount_paid;
				}
			} elseif (!empty($gbook['idpayment'])) {
				if (strpos($gbook['idpayment'], '=') !== false) {
					$parts = explode('=', $gbook['idpayment']);
					$payname = $parts[1];
				} else {
					$payname = $gbook['idpayment'];
				}
			} elseif (!empty($gbook['channel']) && !empty($gbook['idorderota'])) {
				$parts = explode('_', $gbook['channel']);
				unset($parts[0]);
				$payname = implode('_', $parts);
			}

			if (is_null($payname)) {
				// unknown
				$payname = JText::translate('VBOREPORTTOPCUNKNC');
			}

			// update reference
			$gbook['payment_method'] = $payname;

			// update stats for this payment method
			if (!isset($paystats[$payname])) {
				$paystats[$payname] = array(
					'totpaid' => 0,
					'bids' 	  => array()
				);
			}

			// increase total paid and push booking ID
			$paystats[$payname]['totpaid'] += $gbook['totpaid'];
			if (!in_array($gbook['id'], $paystats[$payname]['bids'])) {
				array_push($paystats[$payname]['bids'], $gbook['id']);
			}
		}

		// loop over the stats to build the rows
		foreach ($paystats as $payname => $stats) {
			//push fields in the rows array as a new row
			array_push($this->rows, array(
				array(
					'key' => 'paymeth',
					'value' => $payname
				),
				array(
					'key' => 'tot',
					'attr' => array(
						'class="center"'
					),
					'callback' => function ($val) use ($currency_symb) {
						return $currency_symb.' '.VikBooking::numberFormat($val);
					},
					'export_callback' => function ($val) use ($currency_symb) {
						return $currency_symb.' '.VikBooking::numberFormat($val);
					},
					'value' => $stats['totpaid']
				),
				array(
					'key' => 'ids',
					'attr' => array(
						'class="center"'
					),
					'callback' => function ($val) {
						$str = '';
						foreach ($val as $bid) {
							$str .= '<span style="display: inline-block; margin: 0 2px;"><a href="index.php?option=com_vikbooking&task=editorder&cid[]='.$bid.'" target="_blank"><i class="'.VikBookingIcons::i('external-link').'"></i> '.$bid.'</a></span>';
						}
						return $str;
					},
					'ignore_export' => 1,
					'value' => $stats['bids']
				),
			));
		}

		// sort rows
		$this->sortRows($pkrsort, $pkrorder);

		// loop over the rows to build the footer row with the totals
		$foot_tot_collected = 0;
		foreach ($this->rows as $row) {
			$foot_tot_collected += $row[1]['value'];
		}

		// push footer row
		array_push($this->footerRow, array(
			array(
				'attr' => array(
					'class="vbo-report-total"'
				),
				'value' => '<h3>'.JText::translate('VBOREPORTSTOTALROW').'</h3>'
			),
			array(
				'attr' => array(
					'class="center"'
				),
				'callback' => function ($val) use ($currency_symb) {
					return $currency_symb.' '.VikBooking::numberFormat($val);
				},
				'value' => $foot_tot_collected
			),
			array(
				'attr' => array(
					'class="center"'
				),
				'value' => count($allbids)
			),
		));

		//Debug
		if ($this->debug) {
			$this->setWarning('path to report file = '.urlencode(dirname(__FILE__)).'<br/>');
			$this->setWarning('$bookings:<pre>'.print_r($bookings, true).'</pre><br/>');
		}
		//

		return true;
	}

	/**
	 * Registers the name to give to the CSV file being exported.
	 * 
	 * @return 	void
	 * 
	 * @since 	1.16.1 (J) - 1.6.1 (WP)
	 */
	private function registerExportCSVFileName()
	{
		$pfromdate = VikRequest::getString('fromdate', '', 'request');
		$ptodate = VikRequest::getString('todate', '', 'request');

		$this->setExportCSVFileName($this->reportName . '-' . str_replace('/', '_', $pfromdate) . '-' . str_replace('/', '_', $ptodate) . '.csv');
	}
}