English (United Kingdom)
Knowledge Base  >  Vik Booking  >  For Developers  >  Custom PMS Reports  >  PMS Report Driver File

A custom PMS Report driver is a PHP file that declares a class with a proper name according to the file name, that extends the parent abstract class of VikBooking. This means that specific methods must be implemented to define the filters (input values) of the report as well as to obtain the actual report data by querying the database and by performing the necessary operations.

It is strongly recommended to look at the source code of one of the existing PMS Reports pre-installed in VikBooking in order to understand how to perform certain operations depending on your needs, maybe by starting from an existing report that performs similar actions to what you are looking to implementing.

The below example shows how to declare the PHP class and how to implement the mandatory methods in a custom PMS Report file.

<?php
/**
 * @package     VikBooking
 * @subpackage  custom_one_pms_report
 */

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

/**
 * PMS Report "Custom One" child Class of VikBookingReport
 */
class VikBookingReportCustomOne extends VikBookingReport
{
    /**
     * Property 'defaultKeySort' is used by the View that renders the report.
     */
    public $defaultKeySort = 'id';

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

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

    /**
     * Debug mode
     */
    private $debug = false;

    /**
     * Class constructor should define the name of the report and
     * other vars. Call the parent constructor to define the DB object.
     */
    function __construct()
    {
        $this->reportFile = basename(__FILE__, '.php');
        $this->reportName = 'Custom One';
        $this->reportFilters = [];

        $this->cols = [];
        $this->rows = [];
        $this->footerRow = [];

        // tell the framework what will be the file name to be used for exporting this report
        $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 ($this->reportFilters) {
            // do not run this method twice, as it could load JS and CSS files.
            return $this->reportFilters;
        }

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

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

        // From Date Filter
        $filter_opt = array(
            'label' => '<label for="fromdate">' . __('From Date', 'vbocustomadminreports') . '</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">' . __('To Date', 'vbocustomadminreports') . '</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);

        // any other type of required or optional filter could be added here to fetch the desired information from the database

        // JS 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({
                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});
            }
        }';

        // set the script declaration string (if needed)
        $this->setScript($js);

        // return the array of filters supported
        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  bool
     */
    public function getReportData()
    {
        if ($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;
        }

        // gather input fields from filters and other useful variables
        $pfromdate = VikRequest::getString('fromdate', '', 'request');
        $ptodate = VikRequest::getString('todate', '', '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';

        $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(__('Please select the proper dates', 'vbocustomadminreports'));
            return false;
        }

        // query the database to obtain the records
        $q = $this->dbo->getQuery(true);

        // fetch all bookings with a check-in date between the dates selected through the filters
        $q->select('b.*');
        $q->from($this->dbo->qn('#__vikbooking_orders', 'b'));
        $q->where($this->dbo->qn('b.checkin') . '>=' . $from_ts);
        $q->where($this->dbo->qn('b.checkin') . '<=' . $to_ts);
        $q->order($this->dbo->qn('b.ts') . ' ASC');

        // execute the query and fetch a list of objects for each record (booking) found
        $this->dbo->setQuery($q);
        $bookings = $this->dbo->loadObjectList();

        if (!$bookings) {
            $this->setError(__('No bookings found with the specified input parameters', 'vbocustomadminreports'));
            return false;
        }

        // define the columns of the report (what information will be displayed for each booking)
        $this->cols = [
            // the booking ID
            [
                'key'      => 'id',
                'sortable' => 1,
                'label'    => __('Booking ID', 'vbocustomadminreports'),
            ],
            // the booking creation date
            [
                'key'      => 'created',
                'sortable' => 1,
                'label'    => __('Creation date', 'vbocustomadminreports'),
            ],
            // status
            [
                'key'      => 'status',
                'attr'     => [
                    'class="center"'
                ],
                'sortable' => 1,
                'label'    => __('Status', 'vbocustomadminreports'),
            ],
            // number of nights
            [
                'key'      => 'nights',
                'attr'     => [
                    'class="center"'
                ],
                'sortable' => 1,
                'label'    => __('Number of nights', 'vbocustomadminreports'),
            ],
            // booking total amount
            [
                'key'      => 'total',
                'attr'     => [
                    'class="center"'
                ],
                'sortable' => 1,
                'label'    => __('Booking Total', 'vbocustomadminreports'),
            ],
        ];

        // parse all booking records to define the rows of the report
        foreach ($bookings as $booking) {
            // push fields in the rows array as a new row array
            $this->rows[] = [
                [
                    'key'   => 'id',
                    'value' => $booking->id,
                ],
                [
                    'key'      => 'created',
                    'value'    => $booking->ts,
                    'callback' => function($val) use ($df, $datesep)
                    {
                        // use a closure function to format the date during view and export
                        return date(str_replace("/", $datesep, $df), $val);
                    },
                ],
                [
                    'key'   => 'status',
                    'value' => $booking->status,
                    'attr'  => [
                        'class="center"'
                    ],
                    'callback' => function($string)
                    {
                        // use a closure function to manipulate the data to be displayed or exported
                        return strtoupper($string);
                    },
                ],
                [
                    'key'   => 'nights',
                    'value' => $booking->days,
                    'attr'  => [
                        'class="center"'
                    ],
                ],
                [
                    'key'   => 'total',
                    'value' => $booking->total,
                    'attr'  => [
                        'class="center"'
                    ],
                    'callback' => function($value)
                    {
                        // use a closure function to manipulate the data to be displayed or exported
                        return '€ ' . number_format($value, 2, ',', '');
                    },
                ],
            ];
        }

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

        // footer rows would also be supported inside the property "$this->footerRow"

        // Debug
        if ($this->debug) {
            // set some warning messages with the desired information to debug
            $this->setWarning('query for fetching the records from the db:<br/>' . $q);
            $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
     */
    private function registerExportCSVFileName()
    {
        $pfromdate = VikRequest::getString('fromdate', '', 'request');
        $ptodate = VikRequest::getString('todate', '', 'request');

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

This sample PMS Report will generate one row for each booking fetched from the database (if any), and it will display the ID, Creation Date, Status, Number of Nights and Booking Total details. The rows of the report can be sorted by all keys defined as "sortable" with either ascending or descending order. Values can be formatted for display and export, yet the raw value can be kept for a proper sorting.

Any data could be fetched or calculated from the database and included in your own custom report by following the above example, as well as by looking at the code of some more complex reports pre-installed in VikBooking.

Last Update: 2023-06-13
Helpful?
100% of people found this helpful.