Fixed dashboard issues and improved the design
This commit is contained in:
29
admin/themes/default/css/partials/index.php
Normal file
29
admin/themes/default/css/partials/index.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* 2010-2023 Webkul.
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* All right is reserved,
|
||||
* Please go through this link for complete license : https://store.webkul.com/license.html
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade this module to newer
|
||||
* versions in the future. If you wish to customize this module for your
|
||||
* needs please refer to https://store.webkul.com/customisation-guidelines/ for more information.
|
||||
*
|
||||
* @author Webkul IN <support@webkul.com>
|
||||
* @copyright 2010-2023 Webkul IN
|
||||
* @license https://store.webkul.com/license.html
|
||||
*/
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../../../');
|
||||
exit;
|
||||
1
admin/themes/default/css/partials/ndv3.css
Normal file
1
admin/themes/default/css/partials/ndv3.css
Normal file
@@ -0,0 +1 @@
|
||||
.dashboard-chart-tooltip{background-color:#333;padding:0px 10px 10px 10px;color:#fff;min-width:100px}.dashboard-chart-tooltip label{display:flex;align-items:baseline;gap:5px;font-weight:bold;padding-top:10px}.dashboard-chart-tooltip .tooltip-content{margin-top:5px}.dashboard-chart-tooltip .tooltip-content p{display:flex;align-items:baseline;gap:5px;text-align:left;padding-left:5px}.dashboard-chart-tooltip .tooltip-data-color{display:flex;flex-direction:column;align-items:baseline;padding:5px;max-width:2px}.dashboard-chart-tooltip .text-danger{color:#FF655C}.dashboard-chart-tooltip .text-success{color:#56CE56}
|
||||
1396
admin/themes/default/css/vendor/nv.d3.css
vendored
1396
admin/themes/default/css/vendor/nv.d3.css
vendored
File diff suppressed because it is too large
Load Diff
17
admin/themes/default/js/vendor/nv.d3.min.js
vendored
17
admin/themes/default/js/vendor/nv.d3.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -106,6 +106,7 @@
|
||||
@import "partials/ladda"
|
||||
@import "partials/date-picker"
|
||||
@import "partials/select2"
|
||||
@import "partials/ndv3"
|
||||
|
||||
//Backward compatibility
|
||||
@import "partials/backward"
|
||||
|
||||
@@ -132,6 +132,12 @@
|
||||
.data_value
|
||||
display: block
|
||||
text-align: center
|
||||
.nv-axis.nv-x .tick line, .nv-axis.nv-y .tick line, .nv-interactive .nv-interactiveGuideLine line
|
||||
display: none
|
||||
.nvd3.nv-pie path
|
||||
fill-opacity: 1
|
||||
.nv-label text
|
||||
fill: #FFF!important
|
||||
|
||||
#dashgoals
|
||||
svg
|
||||
|
||||
29
admin/themes/default/sass/partials/ndv3.sass
Normal file
29
admin/themes/default/sass/partials/ndv3.sass
Normal file
@@ -0,0 +1,29 @@
|
||||
.dashboard-chart-tooltip
|
||||
background-color: #333
|
||||
padding: 0px 10px 10px 10px
|
||||
color: #fff
|
||||
min-width: 100px
|
||||
label
|
||||
display: flex
|
||||
align-items: baseline
|
||||
gap: 5px
|
||||
font-weight: bold
|
||||
padding-top: 10px
|
||||
.tooltip-content
|
||||
margin-top: 5px
|
||||
.tooltip-content p
|
||||
display: flex
|
||||
align-items: baseline
|
||||
gap: 5px
|
||||
text-align: left
|
||||
padding-left: 5px
|
||||
.tooltip-data-color
|
||||
display: flex
|
||||
flex-direction: column
|
||||
align-items: baseline
|
||||
padding: 5px
|
||||
max-width: 2px
|
||||
.text-danger
|
||||
color: #FF655C
|
||||
.text-success
|
||||
color: #56CE56
|
||||
@@ -135,8 +135,8 @@ class AdminDashboardControllerCore extends AdminController
|
||||
}
|
||||
|
||||
$forms['net_profit']['fields']['CONF_AVERAGE_PRODUCT_MARGIN'] = array(
|
||||
'title' => $this->l('Average gross margin percentage'),
|
||||
'desc' => $this->l('You should calculate this percentage as follows: ((total sales revenue) - (total operating cost)) / (total sales revenue) * 100. This value is only used to calculate Dashboard approximate gross margin, if you do not specify operating cost for each room type.'),
|
||||
'title' => $this->l('Average gross operating cost percentage'),
|
||||
'desc' => $this->l('This value is only used to calculate Dashboard approximate gross operating cost, if you do not specify operating cost for each room type.'),
|
||||
'validation' => 'isPercentage',
|
||||
'cast' => 'intval',
|
||||
'type' => 'text',
|
||||
|
||||
@@ -217,17 +217,12 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
if ($granularity == 'day') {
|
||||
$sales = array();
|
||||
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS(
|
||||
'SELECT LEFT(`invoice_date`, 10) AS date, SUM(total_paid_tax_excl / o.conversion_rate) AS sales,
|
||||
(
|
||||
SELECT hbd.`id_hotel`
|
||||
FROM`'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
WHERE hbd.`id_order` = o.`id_order` LIMIT 1
|
||||
) AS id_hotel
|
||||
'SELECT LEFT(`invoice_date`, 10) AS date, SUM(total_paid_tax_excl / o.`conversion_rate`) AS sales
|
||||
FROM `'._DB_PREFIX_.'orders` o
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state` os ON o.current_state = os.id_order_state
|
||||
WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" AND os.logable = 1
|
||||
GROUP BY LEFT(`invoice_date`, 10)
|
||||
HAVING 1 '.HotelBranchInformation::addHotelRestriction($id_hotel)
|
||||
WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" AND os.logable = 1'.
|
||||
((int) $id_hotel ? ' AND o.`id_order` IN (SELECT id_order FROM `'._DB_PREFIX_.'htl_booking_detail` WHERE id_hotel = '.(int)$id_hotel.')' : '').'
|
||||
GROUP BY LEFT(`invoice_date`, 10)'
|
||||
);
|
||||
|
||||
foreach ($result as $row) {
|
||||
@@ -238,17 +233,11 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
} elseif ($granularity == 'month') {
|
||||
$sales = array();
|
||||
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS(
|
||||
'SELECT LEFT(`invoice_date`, 7) AS date, SUM(total_paid_tax_excl / o.conversion_rate) AS sales,
|
||||
(
|
||||
SELECT hbd.`id_hotel`
|
||||
FROM`'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
WHERE hbd.`id_order` = o.`id_order` LIMIT 1
|
||||
) AS id_hotel
|
||||
'SELECT LEFT(`invoice_date`, 7) AS date, SUM(total_paid_tax_excl / o.`conversion_rate`) AS sales
|
||||
FROM `'._DB_PREFIX_.'orders` o
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state` os ON o.current_state = os.id_order_state
|
||||
WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" AND os.logable = 1
|
||||
GROUP BY LEFT(`invoice_date`, 7)
|
||||
HAVING 1 '.HotelBranchInformation::addHotelRestriction($id_hotel)
|
||||
WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" AND os.logable = 1'.
|
||||
((int) $id_hotel ? ' AND o.`id_order` IN (SELECT id_order FROM `'._DB_PREFIX_.'htl_booking_detail` WHERE id_hotel = '.(int)$id_hotel.')' : '').' GROUP BY LEFT(`invoice_date`, 7)'
|
||||
);
|
||||
|
||||
foreach ($result as $row) {
|
||||
@@ -258,16 +247,11 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
return $sales;
|
||||
} else {
|
||||
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
|
||||
'SELECT SUM(total_paid_tax_excl / o.conversion_rate),
|
||||
(
|
||||
SELECT hbd.`id_hotel`
|
||||
FROM`'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
WHERE hbd.`id_order` = o.`id_order` LIMIT 1
|
||||
) AS id_hotel
|
||||
'SELECT SUM(total_paid_tax_excl / o.`conversion_rate`)
|
||||
FROM `'._DB_PREFIX_.'orders` o
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state` os ON o.current_state = os.id_order_state
|
||||
WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" AND os.logable = 1
|
||||
HAVING 1 '.HotelBranchInformation::addHotelRestriction($id_hotel)
|
||||
WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" AND os.logable = 1'.
|
||||
((int) $id_hotel ? ' AND o.`id_order` IN (SELECT id_order FROM `'._DB_PREFIX_.'htl_booking_detail` WHERE id_hotel = '.(int)$id_hotel.')' : '')
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -369,6 +353,7 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
return array('type' => 'neutral', 'value' => round(100 * $row['neutral'] / $row['total']));
|
||||
}
|
||||
|
||||
// @todo price conversion for admin selected currency is to be corrected
|
||||
public static function getBestCategory($date_from, $date_to)
|
||||
{
|
||||
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
|
||||
@@ -391,7 +376,7 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
) t ON t.`id_product` = capr.`id_product`
|
||||
RIGHT JOIN `'._DB_PREFIX_.'category` c2
|
||||
ON c2.`id_category` = '.(int)Configuration::get('PS_SERVICE_CATEGORY').' AND ca.`nleft` >= c2.`nleft` AND ca.`nright` <= c2.`nright`
|
||||
WHERE ca.`level_depth` > 1
|
||||
WHERE ca.`level_depth` > 2
|
||||
GROUP BY ca.`id_category`
|
||||
ORDER BY SUM(t.`totalPriceSold`) DESC');
|
||||
}
|
||||
@@ -474,7 +459,7 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
return round($messages / $threads, 1);
|
||||
}
|
||||
|
||||
public static function getPurchases($date_from, $date_to, $granularity = false)
|
||||
public static function getPurchases($date_from, $date_to, $granularity = false, $id_hotel = 0)
|
||||
{
|
||||
if ($granularity == 'day') {
|
||||
$purchases = array();
|
||||
@@ -489,12 +474,14 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
FROM `'._DB_PREFIX_.'orders` o
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_detail` od ON o.id_order = od.id_order
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state` os ON o.current_state = os.id_order_state
|
||||
WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" AND os.logable = 1
|
||||
'.Shop::addSqlRestriction(false, 'o').'
|
||||
WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" AND os.logable = 1'.
|
||||
((int) $id_hotel ? ' AND o.`id_order` IN (SELECT id_order FROM `'._DB_PREFIX_.'htl_booking_detail` WHERE id_hotel = '.(int)$id_hotel.')' : '').Shop::addSqlRestriction(false, 'o').'
|
||||
GROUP BY LEFT(`invoice_date`, 10)');
|
||||
|
||||
foreach ($result as $row) {
|
||||
$purchases[strtotime($row['date'])] = $row['total_purchase_price'];
|
||||
}
|
||||
|
||||
return $purchases;
|
||||
} else {
|
||||
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
|
||||
@@ -506,19 +493,19 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
FROM `'._DB_PREFIX_.'orders` o
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_detail` od ON o.id_order = od.id_order
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state` os ON o.current_state = os.id_order_state
|
||||
WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" AND os.logable = 1
|
||||
'.Shop::addSqlRestriction(false, 'o'));
|
||||
WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" AND os.logable = 1'.
|
||||
((int) $id_hotel ? ' AND o.`id_order` IN (SELECT id_order FROM `'._DB_PREFIX_.'htl_booking_detail` WHERE id_hotel = '.(int)$id_hotel.')' : '').Shop::addSqlRestriction(false, 'o'));
|
||||
}
|
||||
}
|
||||
|
||||
public static function getExpenses($date_from, $date_to, $granularity = false)
|
||||
public static function getExpenses($date_from, $date_to, $granularity = false, $id_hotel = 0)
|
||||
{
|
||||
$expenses = ($granularity == 'day' ? array() : 0);
|
||||
|
||||
$orders = Db::getInstance()->ExecuteS('
|
||||
SELECT
|
||||
LEFT(`invoice_date`, 10) as date,
|
||||
total_paid_tax_incl / o.conversion_rate as total_paid_tax_incl,
|
||||
total_paid_tax_incl / o.conversion_rate as total_paid_tax_incl,
|
||||
total_shipping_tax_excl / o.conversion_rate as total_shipping_tax_excl,
|
||||
o.module,
|
||||
hbd.id_country,
|
||||
@@ -528,8 +515,9 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
LEFT JOIN `'._DB_PREFIX_.'address` hbd ON o.id_address_delivery = hbd.id_address
|
||||
LEFT JOIN `'._DB_PREFIX_.'carrier` c ON o.id_carrier = c.id_carrier
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state` os ON o.current_state = os.id_order_state
|
||||
WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" AND os.logable = 1
|
||||
'.Shop::addSqlRestriction(false, 'o'));
|
||||
WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" AND os.logable = 1'.
|
||||
((int) $id_hotel ? ' AND o.`id_order` IN (SELECT id_order FROM `'._DB_PREFIX_.'htl_booking_detail` WHERE id_hotel = '.(int)$id_hotel.')' : '').Shop::addSqlRestriction(false, 'o'));
|
||||
|
||||
foreach ($orders as $order) {
|
||||
// Add flat fees for this order
|
||||
$flat_fees = Configuration::get('CONF_ORDER_FIXED') + (
|
||||
@@ -555,6 +543,7 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
$expenses += $flat_fees + $var_fees;
|
||||
}
|
||||
}
|
||||
|
||||
return $expenses;
|
||||
}
|
||||
|
||||
@@ -771,6 +760,7 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state` os ON os.`id_order_state` = o.`current_state`
|
||||
WHERE o.`invoice_date` BETWEEN "'.pSQL(date('Y-m-d', strtotime('-31 day'))).' 00:00:00" AND "'.pSQL(date('Y-m-d', strtotime('-1 day'))).' 23:59:59" AND os.`logable` = 1
|
||||
'.Shop::addSqlRestriction());
|
||||
|
||||
$value = Tools::displayPrice($row['orders'] ? $row['total_paid_tax_excl'] / $row['orders'] : 0, $currency);
|
||||
break;
|
||||
|
||||
@@ -800,7 +790,7 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
break;
|
||||
|
||||
case 'top_category':
|
||||
if (!($id_category = AdminStatsController::getBestCategory(date('Y-m-d', strtotime('-1 month')), date('Y-m-d')))) {
|
||||
if (!($id_category = AdminStatsController::getBestCategory(date('Y-m-d', strtotime('-1 month')), date('Y-m-d', strtotime('+1 month'))))) {
|
||||
$value = $this->l('No category', null, null, false);
|
||||
} else {
|
||||
$category = new Category($id_category, $this->context->language->id);
|
||||
@@ -956,6 +946,10 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
|
||||
public static function getOccupancyData($dateFrom, $dateTo, $idsHotel = false)
|
||||
{
|
||||
if ($dateFrom == $dateTo) {
|
||||
$dateTo = date('Y-m-d', strtotime('+1 day', strtotime($dateTo)));
|
||||
}
|
||||
|
||||
$occupancyData = array('count_total' => 0, 'count_occupied' => 0, 'count_available' => 0, 'count_unavailable' => 0);
|
||||
|
||||
$countTotal = Db::getInstance()->getValue(
|
||||
@@ -970,48 +964,60 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
);
|
||||
$occupancyData['count_total'] = $countTotal;
|
||||
|
||||
// Occupied rooms are booked rooms that are not refunded in the date range
|
||||
$countOccupied = 0;
|
||||
if ($dateFrom != $dateTo) {
|
||||
$countOccupied = Db::getInstance()->getValue(
|
||||
'SELECT COUNT(DISTINCT hbd.`id_room`)
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
LEFT JOIN `'._DB_PREFIX_.'htl_room_information` hri
|
||||
ON (hri.`id` = hbd.`id_room`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'product` p
|
||||
ON (p.`id_product` = hri.`id_product`)
|
||||
WHERE p.`active` = 1
|
||||
AND hbd.`date_from` < "'.pSQL($dateTo).' 00:00:00" AND hbd.`date_to` > "'.pSQL($dateFrom).' 00:00:00"'.
|
||||
HotelBranchInformation::addHotelRestriction($idsHotel, 'hbd')
|
||||
);
|
||||
} else {
|
||||
$countOccupied = Db::getInstance()->getValue(
|
||||
'SELECT COUNT(DISTINCT hbd.`id_room`)
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
LEFT JOIN `'._DB_PREFIX_.'htl_room_information` hri
|
||||
ON (hri.`id` = hbd.`id_room`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'product` p
|
||||
ON (p.`id_product` = hri.`id_product`)
|
||||
WHERE p.`active` = 1
|
||||
AND hbd.`date_from` <= "'.pSQL($dateFrom).' 00:00:00" AND hbd.`date_to` > "'.pSQL($dateFrom).' 00:00:00"'.
|
||||
HotelBranchInformation::addHotelRestriction($idsHotel, 'hbd')
|
||||
);
|
||||
}
|
||||
$occupancyData['count_occupied'] = $countOccupied;
|
||||
|
||||
$countUnavailable = Db::getInstance()->getValue(
|
||||
'SELECT COUNT(hri.`id`)
|
||||
FROM `'._DB_PREFIX_.'htl_room_information` hri
|
||||
INNER JOIN `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
ON (hbd.`id` = hri.`id_hotel`)
|
||||
$occupiedRooms = Db::getInstance()->executeS(
|
||||
'SELECT DISTINCT hbd.`id_room`
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
LEFT JOIN `'._DB_PREFIX_.'htl_room_information` hri
|
||||
ON (hri.`id` = hbd.`id_room`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'product` p
|
||||
ON (p.`id_product` = hri.`id_product`)
|
||||
WHERE p.`active` = 1
|
||||
AND hri.`id_status` != '.(int) HotelRoomInformation::STATUS_ACTIVE.
|
||||
AND hbd.`is_refunded` = 0
|
||||
AND hbd.`date_from` < "'.pSQL($dateTo).' 00:00:00" AND hbd.`date_to` > "'.pSQL($dateFrom).' 00:00:00"'.
|
||||
HotelBranchInformation::addHotelRestriction($idsHotel, 'hbd')
|
||||
);
|
||||
|
||||
if ($occupiedRooms) {
|
||||
$occupiedRooms = array_column($occupiedRooms, 'id_room');
|
||||
$occupancyData['count_occupied'] = count($occupiedRooms);
|
||||
} else {
|
||||
$occupancyData['count_occupied'] = 0;
|
||||
}
|
||||
|
||||
// Unavailable rooms are rooms that are not booked for the date range and in the inactive status
|
||||
$countUnavailable = Db::getInstance()->getValue(
|
||||
'SELECT COUNT(hri.`id`)
|
||||
FROM `'._DB_PREFIX_.'htl_room_information` hri
|
||||
LEFT JOIN `'._DB_PREFIX_.'htl_branch_info` hbi
|
||||
ON (hbi.`id` = hri.`id_hotel`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'product` p
|
||||
ON (p.`id_product` = hri.`id_product`)
|
||||
WHERE p.`active` = 1 '.
|
||||
($occupiedRooms ? ' AND hri.`id` NOT IN ('.implode(',', $occupiedRooms).')' : '').
|
||||
' AND hri.`id_status` = '.(int) HotelRoomInformation::STATUS_INACTIVE.
|
||||
HotelBranchInformation::addHotelRestriction($idsHotel, 'hbi', 'id')
|
||||
);
|
||||
$occupancyData['count_unavailable'] = $countUnavailable;
|
||||
|
||||
$occupancyData['count_available'] = $countTotal - $countOccupied - $countUnavailable;
|
||||
// Available rooms are rooms that are not booked for the date range and in the temporary inactive for the date range
|
||||
$countDisabled = Db::getInstance()->getValue(
|
||||
'SELECT IFNULL(COUNT(hri.`id`), 0)
|
||||
FROM `'._DB_PREFIX_.'htl_room_information` hri
|
||||
LEFT JOIN `'._DB_PREFIX_.'htl_room_disable_dates` hrdd
|
||||
ON (hrdd.`id_room` = hri.`id`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'product` p
|
||||
ON (p.`id_product` = hri.`id_product`)
|
||||
WHERE hri.`id_status` = '.(int) HotelRoomInformation::STATUS_TEMPORARY_INACTIVE.
|
||||
($occupiedRooms ? ' AND hri.`id` NOT IN ('.implode(',', $occupiedRooms).')' : '').
|
||||
' AND ("'.pSQL($dateTo).'" > hrdd.`date_from` AND "'.pSQL($dateFrom).'" < hrdd.`date_to`)
|
||||
AND p.`active` = 1'.
|
||||
(!is_null($idsHotel) ? HotelBranchInformation::addHotelRestriction($idsHotel, 'hri') : '')
|
||||
);
|
||||
$occupancyData['count_unavailable'] += $countDisabled;
|
||||
|
||||
$occupancyData['count_available'] = $occupancyData['count_total'] - $occupancyData['count_occupied'] - $occupancyData['count_unavailable'];
|
||||
|
||||
return $occupancyData;
|
||||
}
|
||||
@@ -1061,76 +1067,52 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
return $averageDailyRates;
|
||||
}
|
||||
|
||||
// Average Daily Rate is the average income per paid occupied room in a given time period
|
||||
public static function getAverageDailyRate($dateFrom, $dateTo, $idHotel = null)
|
||||
{
|
||||
if ($dateFrom == $dateTo) {
|
||||
$dateTo = date('Y-m-d', strtotime('+1 day', strtotime($dateTo)));
|
||||
}
|
||||
$roomsRevenueByDates = self::getRoomsRevenueForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$occupiedRoomByDates = self::getOccupiedRoomsForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
|
||||
$averageDailyRates = self::getAverageDailyRateForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$totalOccupiedRooms = array_sum($occupiedRoomByDates);
|
||||
$totalRoomsRevenues = array_sum($roomsRevenueByDates);
|
||||
|
||||
if (!is_array($averageDailyRates)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$sumAverageDailyRates = 0;
|
||||
$countAverageDailyRates = 0;
|
||||
foreach ($averageDailyRates as $averageDailyRate) {
|
||||
if ($averageDailyRate != 0) {
|
||||
$sumAverageDailyRates += $averageDailyRate;
|
||||
$countAverageDailyRates++;
|
||||
}
|
||||
}
|
||||
|
||||
return $countAverageDailyRates ? $sumAverageDailyRates / $countAverageDailyRates : 0;
|
||||
return $totalRoomsRevenues ? ($totalRoomsRevenues / $totalOccupiedRooms) : 0;
|
||||
}
|
||||
|
||||
public static function getCancellationRate($dateFrom, $dateTo, $idHotel = false)
|
||||
{
|
||||
$numAllOrders = Db::getInstance()->getValue(
|
||||
'SELECT COUNT(DISTINCT o.`id_order`) FROM `'._DB_PREFIX_.'orders` o
|
||||
LEFT JOIN `'._DB_PREFIX_.'htl_booking_detail` hbd ON (hbd.`id_order` = o.`id_order`)
|
||||
WHERE o.`date_add` BETWEEN "'.pSQL($dateFrom).' 00:00:00" AND "'.pSQL($dateTo).' 23:59:59"'.
|
||||
$totalBookings = Db::getInstance()->getValue(
|
||||
'SELECT COUNT(hbd.`id`) FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
WHERE hbd.`date_add` BETWEEN "'.pSQL($dateFrom).' 00:00:00" AND "'.pSQL($dateTo).' 23:59:59"'.
|
||||
HotelBranchInformation::addHotelRestriction($idHotel, 'hbd')
|
||||
);
|
||||
|
||||
$numCancelledOrders = Db::getInstance()->getValue(
|
||||
'SELECT COUNT(DISTINCT o.`id_order`) FROM `'._DB_PREFIX_.'orders` o
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state` os ON (os.`id_order_state` = o.`current_state`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'htl_booking_detail` hbd ON (hbd.`id_order` = o.`id_order`)
|
||||
WHERE o.`date_add` BETWEEN "'.pSQL($dateFrom).' 00:00:00" AND "'.pSQL($dateTo).' 23:59:59"
|
||||
AND o.`current_state` IN ('.implode(',', array(
|
||||
(int) Configuration::get('PS_OS_CANCELED'),
|
||||
(int) Configuration::get('PS_OS_REFUND'))
|
||||
).')'.
|
||||
$cancelledBookings = Db::getInstance()->getValue(
|
||||
'SELECT COUNT(hbd.`id`) FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
WHERE hbd.`is_refunded` = 1 AND hbd.`date_add` BETWEEN "'.pSQL($dateFrom).' 00:00:00" AND "'.pSQL($dateTo).' 23:59:59"'.
|
||||
HotelBranchInformation::addHotelRestriction($idHotel, 'hbd')
|
||||
);
|
||||
|
||||
if ($numAllOrders != 0) {
|
||||
return $numCancelledOrders / $numAllOrders;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return $totalBookings ? (($cancelledBookings / $totalBookings) * 100) : 0;
|
||||
}
|
||||
|
||||
public static function getRevenue($dateFrom, $dateTo, $idHotel = false)
|
||||
public static function getRevenue($dateFrom, $dateTo, $idHotel = false, $orderSource = '')
|
||||
{
|
||||
$result = Db::getInstance()->getValue(
|
||||
'SELECT SUM(total_paid_tax_excl - refunded_amount)
|
||||
FROM (
|
||||
SELECT o.`total_paid_tax_excl` / o.`conversion_rate` AS total_paid_tax_excl,
|
||||
IFNULL(orr.`refunded_amount`, 0) AS refunded_amount,
|
||||
(
|
||||
SELECT hbd.`id_hotel`
|
||||
FROM`'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
WHERE hbd.`id_order` = o.`id_order` LIMIT 1
|
||||
) AS id_hotel
|
||||
FROM `'._DB_PREFIX_.'orders` o
|
||||
LEFT JOIN `' ._DB_PREFIX_.'order_return` orr ON orr.`id_order` = o.`id_order`
|
||||
WHERE o.`valid` = 1 AND o.`invoice_date` BETWEEN "'.pSQL($dateFrom).' 00:00:00" AND "'.pSQL($dateTo).' 23:59:59"
|
||||
HAVING 1 '.HotelBranchInformation::addHotelRestriction($idHotel).'
|
||||
) AS t'
|
||||
);
|
||||
$sql = 'SELECT SUM(total_paid_tax_excl - refunded_amount)
|
||||
FROM (SELECT o.`total_paid_tax_excl` / o.`conversion_rate` AS total_paid_tax_excl,
|
||||
IFNULL(orr.`refunded_amount`, 0) AS refunded_amount,
|
||||
(SELECT hbd.`id_hotel` FROM`'._DB_PREFIX_.'htl_booking_detail` hbd WHERE hbd.`id_order` = o.`id_order` LIMIT 1) AS id_hotel
|
||||
FROM `'._DB_PREFIX_.'orders` o
|
||||
LEFT JOIN `' ._DB_PREFIX_.'order_return` orr ON orr.`id_order` = o.`id_order`
|
||||
WHERE o.`valid` = 1 AND o.`invoice_date` BETWEEN "'.pSQL($dateFrom).' 00:00:00" AND "'.pSQL($dateTo).' 23:59:59"';
|
||||
|
||||
if ($orderSource) {
|
||||
$sql .= ' AND o.`source` = "'.pSQL($orderSource).'"';
|
||||
}
|
||||
|
||||
$sql .= ' HAVING 1 '.HotelBranchInformation::addHotelRestriction($idHotel).') as t';
|
||||
|
||||
$result = Db::getInstance()->getValue($sql);
|
||||
|
||||
return $result ? $result : 0;
|
||||
}
|
||||
@@ -1156,15 +1138,13 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
|
||||
public static function getRecentOrdersByHotel($idHotel = null, $limit = null)
|
||||
{
|
||||
$idLang = Context::getContext()->language->id;
|
||||
return Db::getInstance()->executeS(
|
||||
'SELECT *, (
|
||||
SELECT osl.`name`
|
||||
FROM `'._DB_PREFIX_.'order_state_lang` osl
|
||||
WHERE osl.`id_order_state` = o.`current_state`
|
||||
AND osl.`id_lang` = '.(int) Context::getContext()->language->id.'
|
||||
LIMIT 1
|
||||
) AS `state_name`, o.`date_add` AS `date_add`, o.`date_upd` AS `date_upd`
|
||||
'SELECT *, osl.`name` AS `state_name`, os.`color` AS `state_color`, o.`date_add` AS `date_add`, o.`date_upd` AS `date_upd`
|
||||
FROM `'._DB_PREFIX_.'orders` o
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state` os ON (o.`current_state` = os.`id_order_state`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state_lang` osl
|
||||
ON (osl.`id_order_state` = o.`current_state` AND osl.`id_lang` = '.(int) $idLang.')
|
||||
LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = o.`id_customer`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'htl_booking_detail` hbd ON (hbd.`id_order` = o.`id_order`)
|
||||
WHERE 1'.HotelBranchInformation::addHotelRestriction($idHotel, 'hbd').'
|
||||
@@ -1190,6 +1170,7 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Departures: Guest is going to depart from the hotel today
|
||||
public static function getDeparturesInfoByDate($date, $idHotel = null)
|
||||
{
|
||||
$sql = 'SELECT hbd.*, o.`with_occupancy`, CONCAT(c.`firstname`, " ", c.`lastname`) AS customer_name,
|
||||
@@ -1205,6 +1186,7 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
return $result;
|
||||
}
|
||||
|
||||
// In-House or Stay Over: The guest is not expected to check out today and will remain at least one more night.
|
||||
public static function getInHousesInfo($idHotel = null)
|
||||
{
|
||||
$sql = 'SELECT hbd.*, o.`with_occupancy`, CONCAT(c.`firstname`, " ", c.`lastname`) AS customer_name,
|
||||
@@ -1212,7 +1194,10 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
LEFT JOIN `'._DB_PREFIX_.'orders` o ON (o.`id_order` = hbd.`id_order`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = hbd.`id_customer`)
|
||||
WHERE hbd.`is_refunded` = 0 AND hbd.`id_status` = '.(int) HotelBookingDetail::STATUS_CHECKED_IN.
|
||||
WHERE hbd.`is_refunded` = 0
|
||||
AND (hbd.`id_status` = '.(int) HotelBookingDetail::STATUS_CHECKED_IN.'
|
||||
OR (hbd.`id_status` = '.(int) HotelBookingDetail::STATUS_CHECKED_OUT.' AND hbd.`check_out` > "'.pSQL(date('Y-m-d')).' 00:00:00"))
|
||||
AND hbd.`date_to` != "'.pSQL(date('Y-m-d')).' 00:00:00"'.
|
||||
(!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '');
|
||||
$result = Db::getInstance()->executeS($sql);
|
||||
|
||||
@@ -1221,17 +1206,20 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
|
||||
public static function getNewBookingsInfoByDate($date, $idHotel = null)
|
||||
{
|
||||
$sql = 'SELECT hbd.`id_customer`, CONCAT(c.`firstname`, " ", c.`lastname`) AS customer_name,
|
||||
COUNT(hbd.`id`) AS total_rooms, SUM(hbd.`adults` + hbd.`children`) AS total_guests,
|
||||
hbd.`id_hotel`, hbd.`hotel_name`, hbd.`id_order`, o.`with_occupancy`, o.`total_paid_tax_excl`, o.`id_currency`, osl.`name` AS `state_name`
|
||||
$sql = 'SELECT hbd.`id_customer`, CONCAT(c.`firstname`, " ", c.`lastname`) AS customer_name, COUNT(hbd.`id`) AS total_rooms,
|
||||
SUM(hbd.`adults` + hbd.`children`) AS total_guests, hbd.`id_hotel`, hbd.`hotel_name`, hbd.`id_order`, o.`with_occupancy`,
|
||||
o.`total_paid_tax_excl`, o.`id_currency`, osl.`name` AS `state_name`, os.`color` AS `state_color`
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
LEFT JOIN `'._DB_PREFIX_.'orders` o ON (o.`id_order` = hbd.`id_order`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state` os ON (o.`current_state` = os.`id_order_state`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state_lang` osl
|
||||
ON (osl.`id_order_state` = o.`current_state` AND osl.`id_lang` = '.(int) Context::getContext()->language->id.')
|
||||
LEFT JOIN `'._DB_PREFIX_.'customer` c ON (c.`id_customer` = hbd.`id_customer`)
|
||||
WHERE hbd.`date_add` BETWEEN "'.pSQL($date).' 00:00:00" AND "'.pSQL($date).' 23:59:59"'.
|
||||
WHERE hbd.`date_add` BETWEEN "'.pSQL($date).' 00:00:00" AND "'.pSQL($date).' 23:59:59"'.'
|
||||
AND o.`current_state` NOT IN ('.implode(',', array((int) Configuration::get('PS_OS_CANCELED'), (int) Configuration::get('PS_OS_REFUND'))).')'.
|
||||
(!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '').'
|
||||
GROUP BY hbd.`id_order`';
|
||||
|
||||
$result = Db::getInstance()->executeS($sql);
|
||||
|
||||
return $result;
|
||||
@@ -1258,6 +1246,19 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getTotalOccupiedRooms($dateFrom, $dateTo, $idHotel = null)
|
||||
{
|
||||
$sql = 'SELECT COUNT(hbd.`id_room`)
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
WHERE hbd.`is_refunded` = 0
|
||||
AND hbd.`date_from` <= "'.pSQL($dateTo).' 00:00:00" AND hbd.`date_to` > "'.pSQL($dateFrom).' 00:00:00"'.
|
||||
(!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '');
|
||||
|
||||
$result = Db::getInstance()->getValue($sql);
|
||||
|
||||
return $result ? $result : 0;
|
||||
}
|
||||
|
||||
public static function getOccupiedRoomsForDiscreteDates($dateFrom, $dateTo = null, $idHotel = null, $useCache = true)
|
||||
{
|
||||
$dateTo = !$dateTo ? date('Y-m-d', strtotime('+1 day', strtotime($dateFrom))) : $dateTo;
|
||||
@@ -1323,9 +1324,10 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
if (!Cache::isStored($cacheKey) || !$useCache) {
|
||||
$sql = 'SELECT IFNULL(SUM(hbd.`total_price_tax_excl` / DATEDIFF(hbd.`date_to`, hbd.`date_from`)), 0)
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
LEFT JOIN `'._DB_PREFIX_.'product` p
|
||||
ON (p.`id_product` = hbd.`id_product`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'product` p ON (p.`id_product` = hbd.`id_product`)
|
||||
LEFT JOIN `'._DB_PREFIX_.'orders` o ON (o.`id_order` = hbd.`id_order`)
|
||||
WHERE p.`active` = 1
|
||||
AND o.`valid` = 1
|
||||
AND hbd.`is_refunded` = 0
|
||||
AND hbd.`date_from` < "'.pSQL($discreteDate['date_to']).' 00:00:00" AND hbd.`date_to` > "'.pSQL($discreteDate['date_from']).' 00:00:00"'.
|
||||
(!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '');
|
||||
@@ -1368,20 +1370,18 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
(
|
||||
SELECT SUM(DATEDIFF(hbd.`date_to`, hbd.`date_from`))
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
INNER JOIN `'._DB_PREFIX_.'product` p
|
||||
ON (p.`id_product` = hbd.`id_product`)
|
||||
INNER JOIN `'._DB_PREFIX_.'product` p ON (p.`id_product` = hbd.`id_product`)
|
||||
WHERE hbd.`id_order` = o.`id_order`
|
||||
AND p.`active` = 1 AND hbd.`is_refunded` = 0'.
|
||||
AND p.`active` = 1 AND hbd.`is_refunded` = 0 AND o.`valid` = 1'.
|
||||
(!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '').'
|
||||
) AS total_parts,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
INNER JOIN `'._DB_PREFIX_.'product` p
|
||||
ON (p.`id_product` = hbd.`id_product`)
|
||||
INNER JOIN `'._DB_PREFIX_.'product` p ON (p.`id_product` = hbd.`id_product`)
|
||||
WHERE hbd.`id_order` = o.`id_order`
|
||||
AND hbd.`date_from` <= "'.pSQL($discreteDate['date_from']).' 00:00:00" AND hbd.`date_to` >= "'.pSQL($discreteDate['date_to']).' 00:00:00"
|
||||
AND p.`active` = 1 AND hbd.`is_refunded` = 0'.
|
||||
AND p.`active` = 1 AND hbd.`is_refunded` = 0 AND o.`valid` = 1'.
|
||||
(!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '').'
|
||||
) AS current_parts
|
||||
FROM `'._DB_PREFIX_.'orders` o
|
||||
@@ -1578,88 +1578,54 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
return $occupancyRates;
|
||||
}
|
||||
|
||||
// Occupancy rate is the percentage of occupied rooms in your hotel at a given time
|
||||
public static function getAverageOccupancyRate($dateFrom, $dateTo, $idHotel = null)
|
||||
{
|
||||
if ($dateFrom == $dateTo) {
|
||||
$dateTo = date('Y-m-d', strtotime('+1 day', strtotime($dateTo)));
|
||||
}
|
||||
$occupiedRooms = self::getOccupiedRoomsForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$allRooms = self::getTotalRoomsForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
|
||||
$occupancyRates = self::getOccupancyRateForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$totalOccupiedRooms = array_sum($occupiedRooms);
|
||||
$totalRooms = array_sum($allRooms);
|
||||
|
||||
if (!is_array($occupancyRates)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return array_sum($occupancyRates) / count($occupancyRates);
|
||||
return $totalRooms ? ($totalOccupiedRooms / $totalRooms) * 100 : 0;
|
||||
}
|
||||
|
||||
// Revenue Per Available Room is the average income on all rooms in a given time period
|
||||
// RevPAR relates only to room revenue
|
||||
public static function getRevenuePerAvailableRoom($dateFrom, $dateTo, $idHotel = null)
|
||||
{
|
||||
if ($dateFrom == $dateTo) {
|
||||
$dateTo = date('Y-m-d', strtotime('+1 day', strtotime($dateTo)));
|
||||
}
|
||||
$roomsRevenueByDates = self::getRoomsRevenueForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$allRoomsByDate = self::getTotalRoomsForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
|
||||
$averageDailyRates = self::getAverageDailyRateForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$occupancyRates = self::getOccupancyRateForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$totalRoomsRevenues = array_sum($roomsRevenueByDates);
|
||||
$totalRooms = array_sum($allRoomsByDate);
|
||||
|
||||
if (count($averageDailyRates) != count($occupancyRates)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sumRevPars = 0;
|
||||
$countRevPars = 0;
|
||||
foreach ($averageDailyRates as $key => $averageDailyRate) {
|
||||
if ($averageDailyRate != 0) {
|
||||
$sumRevPars += ($averageDailyRate * $occupancyRates[$key]);
|
||||
$countRevPars++;
|
||||
}
|
||||
}
|
||||
|
||||
return $countRevPars ? $sumRevPars / $countRevPars : 0;
|
||||
return $totalRooms ? ($totalRoomsRevenues / $totalRooms) : 0;
|
||||
}
|
||||
|
||||
// TrevPAR appears very similar to RevPAR
|
||||
// RevPAR relates only to room revenue But TrevPAR is for the total(all types services) revenue against your guest rooms.
|
||||
public static function getTotalRevenuePerAvailableRoom($dateFrom, $dateTo, $idHotel = null)
|
||||
{
|
||||
if ($dateFrom == $dateTo) {
|
||||
$dateTo = date('Y-m-d', strtotime('+1 day', strtotime($dateTo)));
|
||||
}
|
||||
$totalRevenueByDate = self::getTotalRevenueForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$allRoomsByDate = self::getTotalRoomsForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
|
||||
$totalRevenues = self::getTotalRevenueForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$totalRooms = self::getTotalRoomsForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$totalRevenues = array_sum($totalRevenueByDate);
|
||||
$totalRooms = array_sum($allRoomsByDate);
|
||||
|
||||
if (count($totalRevenues) != count($totalRooms)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sumTrevPars = 0;
|
||||
$countTrevPars = 0;
|
||||
foreach ($totalRevenues as $key => $totalRevenue) {
|
||||
if ($totalRevenue != 0) {
|
||||
$sumTrevPars += ($totalRevenue / $totalRooms[$key]);
|
||||
$countTrevPars++;
|
||||
}
|
||||
}
|
||||
|
||||
return $countTrevPars ? $sumTrevPars / $countTrevPars : 0;
|
||||
return $totalRooms ? ($totalRevenues / $totalRooms) : 0;
|
||||
}
|
||||
|
||||
// ALOS referes to the average number of nights guests stay at your property over a given time
|
||||
// Formula: [number of room nights booked for a given time / number of reservations for the same time]
|
||||
public static function getAverageLengthOfStay($dateFrom, $dateTo, $idHotel = null)
|
||||
{
|
||||
if ($dateFrom == $dateTo) {
|
||||
$dateTo = date('Y-m-d', strtotime('+1 day', strtotime($dateTo)));
|
||||
}
|
||||
$nightsBookedByDate = self::getRoomNightsData($dateFrom, $dateTo, $idHotel);
|
||||
|
||||
$sql = 'SELECT AVG(DATEDIFF(hbd.`date_to`, hbd.`date_from`))
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
LEFT JOIN `'._DB_PREFIX_.'product` p
|
||||
ON (p.`id_product` = hbd.`id_product`)
|
||||
WHERE p.`active` = 1
|
||||
AND hbd.`is_refunded` = 0
|
||||
AND hbd.`date_from` < "'.pSQL($dateTo).' 00:00:00" AND hbd.`date_to` > "'.pSQL($dateFrom).' 00:00:00"'.
|
||||
(!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '');
|
||||
$result = Db::getInstance()->getValue($sql);
|
||||
$totalNightsBooked = array_sum($nightsBookedByDate);
|
||||
$totalOccupiedRoom = self::getTotalOccupiedRooms($dateFrom, $dateTo, $idHotel);
|
||||
|
||||
return $result ? $result : 0;
|
||||
return $totalOccupiedRoom ? ($totalNightsBooked / $totalOccupiedRoom) : 0;
|
||||
}
|
||||
|
||||
public static function getDirectRevenueRatio($dateFrom, $dateTo, $idHotel = null)
|
||||
@@ -1668,32 +1634,13 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
$dateTo = date('Y-m-d', strtotime('+1 day', strtotime($dateTo)));
|
||||
}
|
||||
|
||||
$sql = 'SELECT SUM(total_paid_tax_excl - refunded_amount)
|
||||
FROM (
|
||||
SELECT o.`id_order`, o.`total_paid_tax_excl` / o.`conversion_rate` AS total_paid_tax_excl,
|
||||
IFNULL(orr.`refunded_amount`, 0) AS refunded_amount,
|
||||
(SELECT hbd.`id_hotel`
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
WHERE hbd.`id_order` = o.`id_order` LIMIT 1
|
||||
) AS id_hotel
|
||||
FROM `'._DB_PREFIX_.'orders` o
|
||||
INNER JOIN `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
ON (hbd.`id_order` = o.`id_order`)
|
||||
INNER JOIN `'._DB_PREFIX_.'htl_branch_info` hbi
|
||||
ON (hbi.`id` = hbd.`id_hotel`)
|
||||
LEFT JOIN `' ._DB_PREFIX_.'order_return` orr
|
||||
ON (orr.`id_order` = o.`id_order`)
|
||||
WHERE o.`valid` = 1 AND hbi.`active` = 1 AND hbd.`is_refunded` = 0
|
||||
AND o.`invoice_date` BETWEEN "'.pSQL($dateFrom).' 00:00:00" AND "'.pSQL($dateTo).' 23:59:59"
|
||||
AND o.`source` = "'.pSQL(Configuration::get('PS_SHOP_DOMAIN')).'"'.
|
||||
(!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '').'
|
||||
GROUP BY o.`id_order`
|
||||
) AS t';
|
||||
$directRevenue = (float) Db::getInstance()->getValue($sql);
|
||||
|
||||
// Direct revenue will be revenue from all the channels
|
||||
$totalRevenue = (float) self::getRevenue($dateFrom, $dateTo, $idHotel);
|
||||
|
||||
return $totalRevenue ? ($directRevenue / $totalRevenue) : 0;
|
||||
// Direct revenue will be revenue from this website only
|
||||
$directRevenue = (float) self::getRevenue($dateFrom, $dateTo, $idHotel, Configuration::get('PS_SHOP_DOMAIN'));
|
||||
|
||||
return $totalRevenue ? (($directRevenue / $totalRevenue) * 100) : 0;
|
||||
}
|
||||
|
||||
public static function getOperatingExpensesForDiscreteDates($dateFrom, $dateTo = null, $idHotel = null, $useCache = true)
|
||||
@@ -1776,26 +1723,17 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
|
||||
public static function getGrossOperatingProfitPerAvailableRoom($dateFrom, $dateTo, $idHotel = null)
|
||||
{
|
||||
if ($dateFrom == $dateTo) {
|
||||
$dateTo = date('Y-m-d', strtotime('+1 day', strtotime($dateTo)));
|
||||
}
|
||||
$totalRevenueByDate = self::getTotalRevenueForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$operatingExpensesByDate = self::getOperatingExpensesForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$allRoomsByDate = self::getTotalRoomsForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
|
||||
$grossOpProfitPars = self::getGrossOperatingProfitPerAvailableRoomForDiscreteDates($dateFrom, $dateTo, $idHotel);
|
||||
$totalRevenues = array_sum($totalRevenueByDate);
|
||||
$totalExpenses = array_sum($operatingExpensesByDate);
|
||||
$totalRooms = array_sum($allRoomsByDate);
|
||||
|
||||
if (!count($grossOpProfitPars)) {
|
||||
return false;
|
||||
}
|
||||
$totalProfit = $totalRevenues - $totalExpenses;
|
||||
|
||||
$sumGrossOpProfitPars = 0;
|
||||
$countGrossOpProfitPars = 0;
|
||||
foreach ($grossOpProfitPars as $grossOpProfitPar) {
|
||||
if ($grossOpProfitPar != 0) {
|
||||
$sumGrossOpProfitPars += $grossOpProfitPar;
|
||||
$countGrossOpProfitPars++;
|
||||
}
|
||||
}
|
||||
|
||||
return $countGrossOpProfitPars ? $sumGrossOpProfitPars / $countGrossOpProfitPars : 0;
|
||||
return $totalProfit ? ($totalProfit / $totalRooms) : 0;
|
||||
}
|
||||
|
||||
public static function getRoomNightsData($dateFrom, $dateTo = null, $idHotel = null, $useCache = true, $average = false)
|
||||
@@ -1851,28 +1789,29 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
public static function getOccupiedRoomsForDayOfTheWeek($dow, $dateFrom, $dateTo = null, $idHotel = null, $useCache = true)
|
||||
{
|
||||
$dateTo = !$dateTo ? date('Y-m-d', strtotime('+1 day', strtotime($dateFrom))) : $dateTo;
|
||||
$dateToNext = date('Y-m-d', strtotime('+1 day', strtotime($dateTo)));
|
||||
|
||||
$result = 0;
|
||||
$cacheKey = 'AdminStats::getOccupiedRoomsForDayOfTheWeek'.'_'.(int) $dow.(int) strtotime($dateFrom).
|
||||
(int) strtotime($dateTo).(!is_array($idHotel) ? (int) $idHotel : implode('_', $idHotel));
|
||||
if (!Cache::isStored($cacheKey) || !$useCache) {
|
||||
// (los/7) + {if(los%7 has $dow) + 1 else + 0)}
|
||||
// dow(start_of(remaining_days)) = dow_date_from && dow(end_of(remaining_days)) = dow_date_to
|
||||
$sql = 'SELECT SUM((full_weeks + IF(('.(int) $dow.' >= dow_date_from AND '.(int) $dow.' < dow_date_to), 1, 0)))
|
||||
AS total_occupied
|
||||
$sql = 'SELECT SUM((full_weeks) + IF('.(int) $dow.' = dow_date_from || ('.(int) $dow.' > dow_date_from AND (dow_date_from + los - 1) > '.(int) $dow.') || (dow_date_from > '.(int) $dow.' AND ((dow_date_from + los - 1) - 7) >= '.(int) $dow.'), 1, 0)) AS total_occupied
|
||||
FROM (
|
||||
SELECT hbd.`id`, hbd.`date_from`, hbd.`date_to`, DAYOFWEEK(hbd.`date_from`) AS dow_date_from,
|
||||
DAYOFWEEK(hbd.`date_to`) AS dow_date_to,
|
||||
DATEDIFF(hbd.`date_to`, hbd.`date_from`) AS los,
|
||||
ROUND(DATEDIFF(hbd.`date_to`, hbd.`date_from`) / 7) AS full_weeks,
|
||||
DATEDIFF(hbd.`date_to`, hbd.`date_from`) % 7 AS remaining_days
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
WHERE hbd.`is_refunded` = 0
|
||||
AND hbd.`date_from` < "'.pSQL($dateTo).' 00:00:00" AND hbd.`date_to` > "'.pSQL($dateFrom).' 00:00:00"'.
|
||||
(!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '').'
|
||||
) AS t';
|
||||
SELECT DAYOFWEEK(`date_from_final`) AS dow_date_from,
|
||||
DAYOFWEEK(`date_to_final`) AS dow_date_to,
|
||||
DATEDIFF(`date_to_final`, `date_from_final`) as los,
|
||||
IF(('.(int) $dow.' >= DAYOFWEEK(`date_from_final`)), ((DATEDIFF(`date_to_final`, `date_from_final`) -('.(int) $dow.' + 1 - DAYOFWEEK(`date_from_final`))) DIV 7), ((DATEDIFF(`date_to_final`, `date_from_final`) - (7 - DAYOFWEEK(`date_from_final`) + '.(int) $dow.')) DIV 7)) AS full_weeks
|
||||
FROM (
|
||||
SELECT IF(DATEDIFF(hbd.`date_from`, \''.pSQL($dateFrom).'\') < 0, \''.pSQL($dateFrom).'\', hbd.`date_from`) AS date_from_final, IF(DATEDIFF(\''.pSQL($dateTo).'\', hbd.`date_to`) < 0, \''.pSQL($dateToNext).'\', hbd.`date_to`) AS date_to_final
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
WHERE hbd.`is_refunded` = 0
|
||||
AND hbd.`date_from` < \''.pSQL($dateTo).'\' AND hbd.`date_to` > \''.pSQL($dateFrom).'\''.
|
||||
(!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '').'
|
||||
) AS t
|
||||
) AS t1';
|
||||
|
||||
$value = (int) Db::getInstance()->getValue($sql);
|
||||
|
||||
Cache::store($cacheKey, $value);
|
||||
|
||||
$result = Cache::retrieve($cacheKey);
|
||||
@@ -1939,17 +1878,13 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
$cacheKey = 'AdminStats::getLengthOfStayRatio'.'_'.(int) $losMinimum.(int) $losMaximum.
|
||||
(int) strtotime($dateFrom).(int) strtotime($dateTo).(!is_array($idHotel) ? (int) $idHotel : implode('_', $idHotel));
|
||||
if (!Cache::isStored($cacheKey) || !$useCache) {
|
||||
if ($dateFrom == $dateTo) {
|
||||
$dateTo = date('Y-m-d', strtotime('+1 day', strtotime($dateTo)));
|
||||
}
|
||||
|
||||
$sql = 'SELECT COUNT(hbd.`id`) AS total
|
||||
FROM `'._DB_PREFIX_.'htl_booking_detail` hbd
|
||||
LEFT JOIN `'._DB_PREFIX_.'product` p
|
||||
ON (p.`id_product` = hbd.`id_product`)
|
||||
WHERE p.`active` = 1
|
||||
AND hbd.`is_refunded` = 0
|
||||
AND hbd.`date_from` < "'.pSQL($dateTo).' 00:00:00" AND hbd.`date_to` > "'.pSQL($dateFrom).' 00:00:00"'.
|
||||
AND hbd.`date_from` <= "'.pSQL($dateTo).' 00:00:00" AND hbd.`date_to` > "'.pSQL($dateFrom).' 00:00:00"'.
|
||||
(!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '');
|
||||
|
||||
$total = Db::getInstance()->getValue($sql);
|
||||
@@ -1962,7 +1897,7 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
ON (p.`id_product` = hbd.`id_product`)
|
||||
WHERE p.`active` = 1
|
||||
AND hbd.`is_refunded` = 0
|
||||
AND hbd.`date_from` < "'.pSQL($dateTo).' 00:00:00" AND hbd.`date_to` > "'.pSQL($dateFrom).' 00:00:00"'.
|
||||
AND hbd.`date_from` <= "'.pSQL($dateTo).' 00:00:00" AND hbd.`date_to` > "'.pSQL($dateFrom).' 00:00:00"'.
|
||||
(!is_null($idHotel) ? HotelBranchInformation::addHotelRestriction($idHotel, 'hbd') : '').'
|
||||
) AS t
|
||||
WHERE los >= '.(int) $losMinimum.' AND los <= '.(int) ($losMaximum ? $losMaximum : $losMinimum);
|
||||
@@ -1978,7 +1913,7 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function getLengthOfStayPercentages($days, $dateFrom, $dateTo, $idHotel = null, $useCache = true, $average = false)
|
||||
public static function getLengthOfStayInfo($days, $dateFrom, $dateTo, $idHotel = null, $useCache = true)
|
||||
{
|
||||
$idsHotel = array();
|
||||
if (is_int($idHotel)) {
|
||||
@@ -2017,18 +1952,11 @@ class AdminStatsControllerCore extends AdminStatsTabController
|
||||
}
|
||||
}
|
||||
|
||||
// calculate averages
|
||||
if ($average) {
|
||||
$totalHotels = count($idsHotel);
|
||||
if ($totalHotels > 1) {
|
||||
foreach ($result as $key => $ratio) {
|
||||
$result[$key] = $ratio['total'] ? ($ratio['fraction'] / $ratio['total']) : 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($result as $key => $ratio) {
|
||||
$result[$key] = $ratio['total'] ? ($ratio['fraction'] / $ratio['total']) : 0;
|
||||
}
|
||||
foreach ($result as $key => $ratio) {
|
||||
$result[$key] = array(
|
||||
'rooms_occupied' => $ratio['fraction'],
|
||||
'percent' => $ratio['total'] ? ($ratio['fraction'] / $ratio['total'] * 100) : 0
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
@@ -281,6 +281,25 @@ function saveDashConfig(widget_name) {
|
||||
});
|
||||
}
|
||||
|
||||
// This function is used to generate the tooltip content on the dashboard different sections
|
||||
function getTooltipContent(title, content = '', color = '') {
|
||||
var tooltipContent = '';
|
||||
tooltipContent += '<div class="dashboard-chart-tooltip">';
|
||||
tooltipContent += '<label>';
|
||||
if (color) {
|
||||
tooltipContent += '<span class="tooltip-data-color"';
|
||||
tooltipContent += ' style="background:' + color + '"';
|
||||
tooltipContent += '">' + '</span> ';
|
||||
}
|
||||
tooltipContent += title + '</label>';
|
||||
tooltipContent += '<div class="tooltip-content">';
|
||||
tooltipContent += content;
|
||||
tooltipContent += '</div>';
|
||||
tooltipContent += '</div>';
|
||||
|
||||
return tooltipContent;
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#calendar_form input[type="submit"]').on('click', function(elt) {
|
||||
elt.preventDefault();
|
||||
|
||||
10
js/vendor/d3.v3.min.js
vendored
10
js/vendor/d3.v3.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -140,9 +140,24 @@ class Dashactivity extends Module
|
||||
'dash_trends_chart1' => array(
|
||||
'chart_type' => 'pie_chart_trends',
|
||||
'data' => array(
|
||||
array('key' => 'qloapps.com', 'y' => round($visits / 2), 'color' => self::$colors[0]),
|
||||
array('key' => 'google.com', 'y' => round($visits / 3), 'color' => self::$colors[1]),
|
||||
array('key' => 'Direct Traffic', 'y' => round($visits / 4), 'color' => self::$colors[2])
|
||||
array(
|
||||
'key' => 'qloapps.com',
|
||||
'y' => round($visits / 2),
|
||||
'color' => self::$colors[0],
|
||||
'percent' => ($visits / 2) ? (Tools::ps_round((100 / 2), 2)) : 0,
|
||||
),
|
||||
array(
|
||||
'key' => 'google.com',
|
||||
'y' => round($visits / 3),
|
||||
'color' => self::$colors[1],
|
||||
'percent' => ($visits / 3) ? (Tools::ps_round((100 / 3), 2)) : 0,
|
||||
),
|
||||
array(
|
||||
'key' => 'Direct Traffic',
|
||||
'y' => round($visits / 4),
|
||||
'color' => self::$colors[2],
|
||||
'percent' => ($visits / 4) ? (Tools::ps_round((100 / 4), 2)) : 0,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -195,29 +210,37 @@ class Dashactivity extends Module
|
||||
Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($sql);
|
||||
$online_visitor = Db::getInstance()->NumRows();
|
||||
|
||||
// Pending bookings will be those bookings which are not paid yet and not in Canceled|Refunded|Payment error state.
|
||||
$pending_orders = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
|
||||
SELECT COUNT(*)
|
||||
SELECT COUNT(DISTINCT o.`id_order`)
|
||||
FROM `'._DB_PREFIX_.'orders` o
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state` os ON (o.current_state = os.id_order_state)
|
||||
LEFT JOIN `'._DB_PREFIX_.'htl_booking_detail` hbd ON (hbd.`id_order` = o.`id_order`)
|
||||
WHERE os.paid = 1 AND os.shipped = 0
|
||||
'.Shop::addSqlRestriction(Shop::SHARE_ORDER).
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_state` os ON (o.`current_state` = os.`id_order_state`)
|
||||
WHERE os.`paid` = 0
|
||||
AND o.`current_state` NOT IN ('.implode(',', array(
|
||||
Configuration::get('PS_OS_CANCELED'),
|
||||
Configuration::get('PS_OS_REFUND'),
|
||||
Configuration::get('PS_OS_ERROR')
|
||||
)).')'.Shop::addSqlRestriction(Shop::SHARE_ORDER).
|
||||
(!is_null($params['id_hotel']) ? HotelBranchInformation::addHotelRestriction($params['id_hotel'], 'hbd') : '')
|
||||
);
|
||||
|
||||
// Abandoned cart are which are added to the cart between Min and Max hours conditions
|
||||
$abandoned_cart = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
|
||||
SELECT COUNT(*)
|
||||
FROM `'._DB_PREFIX_.'cart`
|
||||
WHERE `date_upd` BETWEEN "'.pSQL(date('Y-m-d H:i:s', strtotime('-'.(int)Configuration::get('DASHACTIVITY_CART_ABANDONED_MAX').' MIN'))).'" AND "'.pSQL(date('Y-m-d H:i:s', strtotime('-'.(int)Configuration::get('DASHACTIVITY_CART_ABANDONED_MIN').' MIN'))).'"
|
||||
WHERE `date_upd` BETWEEN "'.pSQL(date('Y-m-d H:i:s', strtotime('-'.(int)Configuration::get('DASHACTIVITY_CART_ABANDONED_MAX').' HOUR'))).'" AND "'.pSQL(date('Y-m-d H:i:s', strtotime('-'.(int)Configuration::get('DASHACTIVITY_CART_ABANDONED_MIN').' HOUR'))).'"
|
||||
AND id_cart NOT IN (SELECT id_cart FROM `'._DB_PREFIX_.'orders`)
|
||||
'.Shop::addSqlRestriction(Shop::SHARE_ORDER)
|
||||
);
|
||||
|
||||
// pending refunds are the refunds requests which are not denied or refunded yet
|
||||
$return_exchanges = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
|
||||
SELECT COUNT(*)
|
||||
FROM `'._DB_PREFIX_.'orders` o
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_return` or2 ON o.id_order = or2.id_order
|
||||
WHERE or2.`date_add` BETWEEN "'.pSQL($params['date_from']).'" AND "'.pSQL($params['date_to']).'"
|
||||
LEFT JOIN `'._DB_PREFIX_.'order_return_state` ors ON (or2.state = ors.id_order_return_state)
|
||||
WHERE (ors.`denied` = 0 AND ors.`refunded` = 0)
|
||||
'.Shop::addSqlRestriction(Shop::SHARE_ORDER, 'o')
|
||||
);
|
||||
|
||||
@@ -253,6 +276,7 @@ class Dashactivity extends Module
|
||||
AND newsletter = 1
|
||||
'.Shop::addSqlRestriction(Shop::SHARE_ORDER)
|
||||
);
|
||||
|
||||
$total_suscribers = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
|
||||
SELECT COUNT(*)
|
||||
FROM `'._DB_PREFIX_.'customer`
|
||||
@@ -308,8 +332,15 @@ class Dashactivity extends Module
|
||||
$referers = $this->getReferer($date_from, $date_to);
|
||||
$return = array('chart_type' => 'pie_chart_trends', 'data' => array());
|
||||
$i = 0;
|
||||
|
||||
$totalTraffic = array_sum(array_values($referers));
|
||||
foreach ($referers as $referer_name => $n) {
|
||||
$return['data'][] = array('key' => $referer_name, 'y' => $n, 'color' => self::$colors[$i++]);
|
||||
$return['data'][] = array(
|
||||
'key' => $referer_name,
|
||||
'y' => $n,
|
||||
'color' => self::$colors[$i++],
|
||||
'percent' => $n ? (Tools::ps_round($n / $totalTraffic * 100, 2)) : 0,
|
||||
);
|
||||
}
|
||||
|
||||
return $return;
|
||||
@@ -437,7 +468,7 @@ class Dashactivity extends Module
|
||||
);
|
||||
$fields_form['form']['input'][] = array(
|
||||
'label' => $this->l('Abandoned cart (max)'),
|
||||
'hint' => $this->l('How long (in hours) after the last action a cart is no longer to be considered as abandoned (default: 24 hrs).'),
|
||||
'hint' => $this->l('How long (in hours) after the last action a cart is no longer to be considered as abandoned (default: 48 hrs).'),
|
||||
'name' => 'DASHACTIVITY_CART_ABANDONED_MAX',
|
||||
'type' => 'text',
|
||||
'suffix' => $this->l('hrs'),
|
||||
@@ -462,6 +493,38 @@ class Dashactivity extends Module
|
||||
return $helper->generateForm(array($fields_form));
|
||||
}
|
||||
|
||||
// Validation of the configuration form
|
||||
public function validateDashConfig($configs)
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
if (!Validate::isUnsignedInt($configs['DASHACTIVITY_CART_ACTIVE'])
|
||||
|| !$configs['DASHACTIVITY_CART_ACTIVE']
|
||||
) {
|
||||
$errors[] = $this->l('Active cart must be a positive integer.');
|
||||
}
|
||||
|
||||
if (!Validate::isUnsignedInt($configs['DASHACTIVITY_VISITOR_ONLINE'])
|
||||
|| !$configs['DASHACTIVITY_VISITOR_ONLINE']
|
||||
) {
|
||||
$errors[] = $this->l('Online visitor must be a positive integer.');
|
||||
}
|
||||
|
||||
if (!Validate::isUnsignedFloat($configs['DASHACTIVITY_CART_ABANDONED_MIN'])
|
||||
|| !$configs['DASHACTIVITY_CART_ABANDONED_MIN']
|
||||
) {
|
||||
$errors[] = $this->l('Minimum abandoned cart must be valid hours.');
|
||||
}
|
||||
|
||||
if (!Validate::isUnsignedFloat($configs['DASHACTIVITY_CART_ABANDONED_MAX'])
|
||||
|| !$configs['DASHACTIVITY_CART_ABANDONED_MAX']
|
||||
) {
|
||||
$errors[] = $this->l('Maximum abandoned cart must be valid hours.');
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
public function getConfigFieldsValues()
|
||||
{
|
||||
return array(
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
/* end */
|
||||
#dashactivity .activity-section {
|
||||
position: relative;
|
||||
padding-bottom: 2px;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 15px;
|
||||
border-bottom: 2px solid #D3D8DB;
|
||||
}
|
||||
@@ -39,7 +39,7 @@
|
||||
#dashactivity .title span {
|
||||
margin-bottom: 4px;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
#dashactivity .stats-list .item-label span {
|
||||
margin-bottom: 4px;
|
||||
font-size: 16px;
|
||||
font-size: 14px;
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
|
||||
#dashactivity .stats-list .item-value span {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
text-align: right;
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
@@ -31,10 +31,18 @@ function pie_chart_trends(widget_name, chart_details)
|
||||
.y(function(d) { return d.y })
|
||||
.color(d3.scale.category10().range())
|
||||
.valueFormat(d3.format(".0f"))
|
||||
.donut(true)
|
||||
.showLabels(false)
|
||||
.showLegend(false)
|
||||
.donutRatio(0.4);
|
||||
.showLabels(true)
|
||||
.showLegend(false)
|
||||
.labelThreshold(.01)
|
||||
.labelType('percent')
|
||||
.donut(true)
|
||||
.donutRatio(0.35);
|
||||
|
||||
// create content for the tooltip of chart
|
||||
chart.tooltip.contentGenerator((obj, element) => {
|
||||
var tooltipContent = '<p><b>' + obj.data.y + '</b> (' + obj.data.percent + '%)</p>';
|
||||
return getTooltipContent(obj.data.key, tooltipContent, obj.color);
|
||||
});
|
||||
|
||||
d3.select("#dash_traffic_chart2 svg")
|
||||
.datum(chart_details.data)
|
||||
|
||||
@@ -23,189 +23,177 @@
|
||||
* International Registered Trademark & Property of PrestaShop SA
|
||||
*}
|
||||
|
||||
<section id="dashactivity" class="widget allow_push">
|
||||
<div class="panel">
|
||||
<div class="panel-heading">
|
||||
<i class="icon-time"></i> {l s="Activity overview" mod="dashactivity"}
|
||||
<span class="panel-heading-action">
|
||||
<a class="list-toolbar-btn" href="#" onclick="toggleDashConfig('dashactivity'); return false;" title="{l s="Configure" mod="dashactivity"}">
|
||||
<i class="process-icon-configure"></i>
|
||||
</a>
|
||||
<a class="list-toolbar-btn" href="#" onclick="refreshDashboard('dashactivity'); return false;" title="{l s="Refresh" mod="dashactivity"}">
|
||||
<i class="process-icon-refresh"></i>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<section id="dashactivity_config" class="dash_config hide">
|
||||
<header><i class="icon-wrench"></i> {l s="Configuration" mod="dashactivity"}</header>
|
||||
{$dashactivity_config_form}
|
||||
</section>
|
||||
<section id="dashactivity" class="panel widget {if $allow_push} allow_push{/if}">
|
||||
<header class="panel-heading">
|
||||
<i class="icon-time"></i> {l s="Activity overview" mod="dashactivity"}
|
||||
<span class="panel-heading-action">
|
||||
<a class="list-toolbar-btn" href="#" onclick="toggleDashConfig('dashactivity'); return false;" title="{l s="Configure" mod="dashactivity"}">
|
||||
<i class="process-icon-configure"></i>
|
||||
</a>
|
||||
<a class="list-toolbar-btn" href="#" onclick="refreshDashboard('dashactivity'); return false;" title="{l s="Refresh" mod="dashactivity"}">
|
||||
<i class="process-icon-refresh"></i>
|
||||
</a>
|
||||
</span>
|
||||
</header>
|
||||
<section id="dashactivity_config" class="dash_config hide row">
|
||||
<header><i class="icon-wrench"></i> {l s="Configuration" mod="dashactivity"}</header>
|
||||
{$dashactivity_config_form}
|
||||
</section>
|
||||
|
||||
<section class="activity-section dash-live">
|
||||
<span class="title">
|
||||
<a href="{$link->getAdminLink("AdminStats")|escape:"html":"UTF-8"}&module=statslive" target="_blank">
|
||||
<span>{l s="Online Visitors" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="value">
|
||||
<span id="online_visitor"></span>
|
||||
</span>
|
||||
<div class="sub-title">
|
||||
<small class="text-muted">
|
||||
{l s="in the last %d minutes" sprintf=$DASHACTIVITY_VISITOR_ONLINE|intval mod="dashactivity"}
|
||||
</small>
|
||||
</div>
|
||||
</section>
|
||||
<section class="activity-section dash-live">
|
||||
<span class="title">
|
||||
<a href="{$link->getAdminLink("AdminStats")|escape:"html":"UTF-8"}&module=statslive" target="_blank">
|
||||
<span>{l s="Online Visitors" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="value">
|
||||
<span id="online_visitor"></span>
|
||||
</span>
|
||||
<div class="sub-title">
|
||||
<small class="text-muted">
|
||||
{l s="in the last %d minutes" sprintf=$DASHACTIVITY_VISITOR_ONLINE|intval mod="dashactivity"}
|
||||
</small>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="activity-section dash-live">
|
||||
<span class="title">
|
||||
<a href="{$link->getAdminLink("AdminCarts")|escape:"html":"UTF-8"}" target="_blank">
|
||||
<span>{l s="Active Booking Carts" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="value">
|
||||
<span id="active_shopping_cart"></span>
|
||||
</span>
|
||||
<div class="sub-title">
|
||||
<small class="text-muted">
|
||||
{l s="in the last %d minutes" sprintf=$DASHACTIVITY_CART_ACTIVE|intval mod="dashactivity"}
|
||||
</small>
|
||||
</div>
|
||||
</section>
|
||||
<section class="activity-section dash-live">
|
||||
<span class="title">
|
||||
<a href="{$link->getAdminLink("AdminCarts")|escape:"html":"UTF-8"}" target="_blank">
|
||||
<span>{l s="Active Booking Carts" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="value">
|
||||
<span id="active_shopping_cart"></span>
|
||||
</span>
|
||||
<div class="sub-title">
|
||||
<small class="text-muted">
|
||||
{l s="in the last %d minutes" sprintf=$DASHACTIVITY_CART_ACTIVE|intval mod="dashactivity"}
|
||||
</small>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="dash_pending" class="activity-section">
|
||||
<span class="title">
|
||||
<span>{l s="Currently Pending" mod="dashactivity"}</span>
|
||||
</span>
|
||||
<ul class="stats-list">
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminOrders")|escape:"html":"UTF-8"}" target="_blank">
|
||||
<span>{l s="Bookings" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="pending_orders"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminOrderRefundRequests")|escape:"html":"UTF-8"}" target="_blank">
|
||||
<span>{l s="Refunds" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="return_exchanges"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminCarts")|escape:"html":"UTF-8"}" target="_blank">
|
||||
<span>{l s="Abandoned Carts" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="abandoned_cart"></span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
<section id="dash_pending" class="activity-section">
|
||||
<span class="title">
|
||||
<span>{l s="Currently Pending" mod="dashactivity"}</span>
|
||||
</span>
|
||||
<ul class="stats-list">
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminOrders")|escape:"html":"UTF-8"}" target="_blank">
|
||||
<span>{l s="Bookings (not paid)" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="pending_orders"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminOrderRefundRequests")|escape:"html":"UTF-8"}&&submitFilterorder_return=1&order_returnFilter_total_pending_requests=1" target="_blank">
|
||||
<span>{l s="Refunds" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="return_exchanges"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminCarts")|escape:"html":"UTF-8"}&submitFiltercart=1&cartFilter_filter_ids_order=Abandoned cart" target="_blank">
|
||||
<span>{l s="Abandoned Carts" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="abandoned_cart"></span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-heading">
|
||||
<i class="icon-time"></i> {l s="Activity overview" mod="dashactivity"}
|
||||
<span class="panel-heading-action">
|
||||
<a class="list-toolbar-btn" href="#" onclick="refreshDashboard('dashactivity'); return false;" title="{l s="Refresh" mod="dashactivity"}">
|
||||
<i class="process-icon-refresh"></i>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<section id="dash_customers" class="activity-section">
|
||||
<span class="title">
|
||||
<span>{l s="Customers & Newsletters" mod="dashactivity"}</span>
|
||||
</span>
|
||||
<div class="sub-title">
|
||||
<small class="text-muted" id="customers-newsletters-subtitle"></small>
|
||||
</div>
|
||||
<section id="dash_customers" class="activity-section">
|
||||
<span class="title">
|
||||
<span>{l s="Customers & Newsletters" mod="dashactivity"}</span>
|
||||
</span>
|
||||
<div class="sub-title">
|
||||
<small class="text-muted" id="customers-newsletters-subtitle"></small>
|
||||
</div>
|
||||
|
||||
<ul class="stats-list">
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminCustomers")|escape:"html":"UTF-8"}" target="_blank">
|
||||
<span>{l s="New Customers" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="new_customers"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminStats")|escape:"html":"UTF-8"}&module=statsnewsletter"
|
||||
target="_blank">
|
||||
<span>{l s="New Subscriptions" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="new_registrations"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminModules")|escape}&configure=blocknewsletter&module_name=blocknewsletter"
|
||||
target="_blank">
|
||||
<span>{l s="Total Subscribers" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="total_suscribers"></span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<ul class="stats-list">
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminCustomers")|escape:"html":"UTF-8"}" target="_blank">
|
||||
<span>{l s="New Customers" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="new_customers"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminStats")|escape:"html":"UTF-8"}&module=statsnewsletter"
|
||||
target="_blank">
|
||||
<span>{l s="New Subscriptions" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="new_registrations"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminModules")|escape}&configure=blocknewsletter&module_name=blocknewsletter"
|
||||
target="_blank">
|
||||
<span>{l s="Total Subscribers" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="total_suscribers"></span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="dash_traffic" class="activity-section">
|
||||
<span class="title">
|
||||
<span>{l s="Traffic" mod="dashactivity"}</span>
|
||||
</span>
|
||||
<div class="sub-title">
|
||||
<small class="text-muted" id="traffic-subtitle"></small>
|
||||
</div>
|
||||
<ul class="stats-list">
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminStats")|escape:"html":"UTF-8"}&module=statsforecast"
|
||||
target="_blank">
|
||||
<span>{l s="Visits" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="visits"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminStats")|escape:"html":"UTF-8"}&module=statsvisits"
|
||||
target="_blank">
|
||||
<span>{l s="Unique Visitors" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="unique_visitors"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item-label heading">
|
||||
<span>{l s="Traffic Sources" mod="dashactivity"}</span>
|
||||
</span>
|
||||
<section id="dash_traffic" class="activity-section">
|
||||
<span class="title">
|
||||
<span>{l s="Traffic" mod="dashactivity"}</span>
|
||||
</span>
|
||||
<div class="sub-title">
|
||||
<small class="text-muted" id="traffic-subtitle"></small>
|
||||
</div>
|
||||
<ul class="stats-list">
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminStats")|escape:"html":"UTF-8"}&module=statsforecast"
|
||||
target="_blank">
|
||||
<span>{l s="Visits" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="visits"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item-label">
|
||||
<a href="{$link->getAdminLink("AdminStats")|escape:"html":"UTF-8"}&module=statsvisits"
|
||||
target="_blank">
|
||||
<span>{l s="Unique Visitors" mod="dashactivity"}</span>
|
||||
</a>
|
||||
</span>
|
||||
<span class="item-value">
|
||||
<span id="unique_visitors"></span>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="item-label heading">
|
||||
<span>{l s="Traffic Sources" mod="dashactivity"}</span>
|
||||
</span>
|
||||
|
||||
<ul class="data_list_small" id="dash_traffic_source"></ul>
|
||||
<div id="dash_traffic_chart2" class="chart with-transitions">
|
||||
<svg></svg>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
<ul class="data_list_small" id="dash_traffic_source"></ul>
|
||||
<div id="dash_traffic_chart2" class="chart with-transitions">
|
||||
<svg></svg>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -52,6 +52,9 @@ class DashAvailability extends Module
|
||||
public function hookActionAdminControllerSetMedia()
|
||||
{
|
||||
if (get_class($this->context->controller) == 'AdminDashboardController') {
|
||||
Media::addJsDef(array(
|
||||
'avail_rooms_txt' => $this->l('Available Rooms'),
|
||||
));
|
||||
$this->context->controller->addJs($this->_path.'views/js/'.$this->name.'.js');
|
||||
$this->context->controller->addCSS($this->_path.'views/css/'.$this->name.'.css');
|
||||
}
|
||||
@@ -99,7 +102,7 @@ class DashAvailability extends Module
|
||||
$availability_data,
|
||||
array(
|
||||
'id' => 'availabilities',
|
||||
'key' => $this->l('Availabilities'),
|
||||
'key' => $this->l('Availability'),
|
||||
'border_color' => '#11f0fc',
|
||||
'color' => '#72C3F0',
|
||||
)
|
||||
|
||||
@@ -30,17 +30,49 @@ function line_chart_availability(widget_name, chart_details) {
|
||||
.margin({
|
||||
left: 30,
|
||||
right: 30,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
chart.xAxis.tickFormat(function(d) {
|
||||
chart.xAxis
|
||||
.tickFormat(function(d) {
|
||||
date = new Date(d * 1000);
|
||||
return date.format(chart_details['date_format']);
|
||||
})
|
||||
// through this function we are also fixing the x axis date and values alignment issue
|
||||
.tickValues(function(values) {
|
||||
var indexInterval = Math.floor(values[0].values.length / 10);
|
||||
|
||||
var dates = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
if (values[0].values[(indexInterval+1) * i]) {
|
||||
dates.push(values[0].values[(indexInterval+1) * i]);
|
||||
}
|
||||
}
|
||||
var dates = dates.map(function(v) {
|
||||
return v[0]
|
||||
});
|
||||
|
||||
return dates;
|
||||
});
|
||||
|
||||
chart.yAxis.tickFormat(function(d) {
|
||||
return Number.isInteger(d) ? d : '';
|
||||
});
|
||||
|
||||
// create content for the tooltip of chart
|
||||
chart.interactiveLayer.tooltip.contentGenerator((obj, element) => {
|
||||
if (typeof obj.series[0] !== 'undefined') {
|
||||
var date = new Date(obj.value * 1000);
|
||||
date = date.format(chart_details['date_format']);
|
||||
|
||||
tooltipContent = '';
|
||||
tooltipContent += '<p>' + date_txt + ': <b>' + date + '</b></p>';
|
||||
tooltipContent += '<p>' + avail_rooms_txt + ': <b>' + obj.series[0].value + '</b></p>';
|
||||
}
|
||||
|
||||
return getTooltipContent(obj.series[0].key, tooltipContent, obj.series[0].color);
|
||||
});
|
||||
|
||||
chart.legend.updateState(false);
|
||||
|
||||
dashavailability_data = chart_details.data;
|
||||
|
||||
@@ -37,6 +37,10 @@ class Dashgoals extends Module
|
||||
protected static $more_color = array('#803E84', '#008E7E', '#20B2E7', '#F66E1B');
|
||||
protected static $less_color = array('#BC77BE', '#00C2BB', '#51D6F2', '#FBB244');
|
||||
|
||||
const DG_VALUE_TYPE_TEXT = 1;
|
||||
const DG_VALUE_TYPE_PRICE = 2;
|
||||
const DG_VALUE_TYPE_PERCENT = 3;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->name = 'dashgoals';
|
||||
@@ -120,6 +124,13 @@ class Dashgoals extends Module
|
||||
public function hookActionAdminControllerSetMedia()
|
||||
{
|
||||
if (get_class($this->context->controller) == 'AdminDashboardController') {
|
||||
Media::addJsDef(array(
|
||||
'goal_set_txt' => $this->l('Goal Set'),
|
||||
'goal_diff_txt' => $this->l('Goal Difference'),
|
||||
'VALUE_TYPE_PRICE' => self::DG_VALUE_TYPE_PRICE,
|
||||
'VALUE_TYPE_PERCENT' => self::DG_VALUE_TYPE_PERCENT,
|
||||
));
|
||||
|
||||
$this->context->controller->addJS($this->_path.'views/js/'.$this->name.'.js');
|
||||
$this->context->controller->addCSS($this->_path.'views/css/'.$this->name.'.css');
|
||||
}
|
||||
@@ -191,20 +202,19 @@ class Dashgoals extends Module
|
||||
{
|
||||
// There are stream types (different charts) and for each types there are 3 available zones (one color for the goal, one if you over perform and one if you under perfom)
|
||||
$stream_types = array(
|
||||
array('type' => 'traffic', 'title' => $this->l('Traffic'), 'unit_text' => $this->l('visits')),
|
||||
array('type' => 'conversion', 'title' => $this->l('Conversion'), 'unit_text' => ''),
|
||||
array('type' => 'avg_cart_value', 'title' => $this->l('Average order value'), 'unit_text' => ''),
|
||||
array('type' => 'sales', 'title' => $this->l('Sales'), 'unit_text' => '')
|
||||
array('type' => 'traffic', 'title' => $this->l('Traffic')),
|
||||
array('type' => 'conversion', 'title' => $this->l('Conversion rate')),
|
||||
array('type' => 'avg_cart_value', 'title' => $this->l('Average order value')),
|
||||
array('type' => 'sales', 'title' => $this->l('Sales')),
|
||||
);
|
||||
$stream_zones = array(
|
||||
array('zone' => 'real', 'text' => ''),
|
||||
array('zone' => 'more', 'text' => $this->l('Goal exceeded')),
|
||||
array('zone' => 'less', 'text' => $this->l('Goal not reached'))
|
||||
array('zone' => 'real'),
|
||||
array('zone' => 'more'),
|
||||
array('zone' => 'less')
|
||||
);
|
||||
|
||||
// We initialize all the streams types for all the zones
|
||||
$streams = array();
|
||||
$average_goals = array();
|
||||
|
||||
foreach ($stream_types as $key => $stream_type) {
|
||||
$streams[$stream_type['type']] = array();
|
||||
@@ -212,17 +222,11 @@ class Dashgoals extends Module
|
||||
$streams[$stream_type['type']][$stream_zone['zone']] = array(
|
||||
'key' => $stream_type['type'].'_'.$stream_zone['zone'],
|
||||
'title' => $stream_type['title'],
|
||||
'unit_text' => $stream_type['unit_text'],
|
||||
'zone_text' => $stream_zone['text'],
|
||||
'color' => ($stream_zone['zone'] == 'more' ? self::$more_color[$key] : ($stream_zone['zone'] == 'less' ? self::$less_color[$key] : self::$real_color[$key])),
|
||||
'values' => array(),
|
||||
'disabled' => (isset($stream_type['type']) && $stream_type['type'] == 'sales') ? false : true
|
||||
);
|
||||
}
|
||||
|
||||
if (isset($stream_type['type'])) {
|
||||
$average_goals[$stream_type['type']] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (Configuration::get('PS_DASHBOARD_SIMULATION')) {
|
||||
@@ -235,133 +239,131 @@ class Dashgoals extends Module
|
||||
$sales[$date] = round(rand(3000, 9000), 2);
|
||||
}
|
||||
|
||||
// We need to calculate the average value of each goals for the year, this will be the base rate for "100%"
|
||||
for ($i = '01'; $i <= 12; $i = sprintf('%02d', $i + 1)) {
|
||||
$average_goals['traffic'] += $this->fakeConfigurationKPI_get('DASHGOALS_TRAFFIC_'.$i.'_'.$year);
|
||||
$average_goals['conversion'] += $this->fakeConfigurationKPI_get('DASHGOALS_CONVERSION_'.$i.'_'.$year);
|
||||
$average_goals['avg_cart_value'] += $this->fakeConfigurationKPI_get('DASHGOALS_AVG_CART_VALUE_'.$i.'_'.$year);
|
||||
}
|
||||
|
||||
foreach ($average_goals as &$average_goal) {
|
||||
$average_goal /= 12;
|
||||
}
|
||||
|
||||
$average_goals['sales'] = $average_goals['traffic'] * $average_goals['conversion'] / 100 * $average_goals['avg_cart_value'];
|
||||
|
||||
// Now we can calculate the value for every months
|
||||
for ($i = '01'; $i <= 12; $i = sprintf('%02d', $i + 1)) {
|
||||
$timestamp = strtotime($year.'-'.$i.'-01');
|
||||
|
||||
// Start setting traffic dummy data
|
||||
$month_goal = $this->fakeConfigurationKPI_get('DASHGOALS_TRAFFIC_'.$i.'_'.$year);
|
||||
$value = (isset($visits[$timestamp]) ? $visits[$timestamp] : 0);
|
||||
$stream_values = $this->getValuesFromGoals($average_goals['traffic'], $month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
$goal_diff = $value - $month_goal;
|
||||
$stream_values['real']['traffic'] = $value;
|
||||
$stream_values['real']['goal'] = $month_goal;
|
||||
if ($value > 0) {
|
||||
$stream_values['real']['goal_diff'] = round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2);
|
||||
}
|
||||
$stream_values = $this->getValuesFromGoals($month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
|
||||
$stream_values['less']['traffic'] = $value;
|
||||
$stream_values['more']['traffic'] = $value;
|
||||
$goal_diff = $value - $month_goal;
|
||||
|
||||
if ($value > 0 && $value < $month_goal) {
|
||||
$stream_values['less']['goal_diff'] = $goal_diff;
|
||||
} elseif ($value > 0) {
|
||||
$stream_values['more']['goal_diff'] = $goal_diff;
|
||||
}
|
||||
$monthGoalInfo = array();
|
||||
if ($goal_diff < 0) {
|
||||
$monthGoalInfo['complete'] = 0;
|
||||
} else {
|
||||
$monthGoalInfo['complete'] = 1;
|
||||
}
|
||||
$goal_diff = abs($goal_diff);
|
||||
$monthGoalInfo['value_type'] = self::DG_VALUE_TYPE_TEXT;
|
||||
$monthGoalInfo['value'] = Tools::ps_round($value, 2);
|
||||
$monthGoalInfo['goal'] = Tools::ps_round($month_goal, 2);
|
||||
$monthGoalInfo['goal_diff'] = Tools::ps_round($goal_diff, 2);
|
||||
$monthGoalInfo['goal_diff_percent'] = ($value > 0) ? Tools::ps_round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2) : 100;
|
||||
// for dummy data future goal will be 0 always
|
||||
$monthGoalInfo['is_future_goal'] = 0;
|
||||
|
||||
if ($value == 0) {
|
||||
$streams['traffic']['less']['zone_text'] = $this->l('Goal set:');
|
||||
$stream_values['less']['goal'] = $month_goal;
|
||||
}
|
||||
$stream_values['real'] = array_merge($stream_values['real'], $monthGoalInfo);
|
||||
$stream_values['less'] = array_merge($stream_values['less'], $monthGoalInfo);
|
||||
$stream_values['more'] = array_merge($stream_values['more'], $monthGoalInfo);
|
||||
|
||||
foreach ($stream_zones as $stream_zone) {
|
||||
$stream_values[$stream_zone['zone']]['title'] = $streams['traffic'][$stream_zone['zone']]['title'];
|
||||
$streams['traffic'][$stream_zone['zone']]['values'][] = $stream_values[$stream_zone['zone']];
|
||||
}
|
||||
|
||||
// Start setting conversion rate dummy data
|
||||
$month_goal = $this->fakeConfigurationKPI_get('DASHGOALS_CONVERSION_'.$i.'_'.$year);
|
||||
$value = 100 * ((isset($visits[$timestamp]) && $visits[$timestamp] && isset($orders[$timestamp]) && $orders[$timestamp]) ? ($orders[$timestamp] / $visits[$timestamp]) : 0);
|
||||
$stream_values = $this->getValuesFromGoals($average_goals['conversion'], $month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
$goal_diff = $value - $month_goal;
|
||||
$stream_values['real']['conversion'] = round($value, 2);
|
||||
$stream_values['real']['goal'] = round($month_goal, 2);
|
||||
if ($value > 0) {
|
||||
$stream_values['real']['goal_diff'] = round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2);
|
||||
}
|
||||
$stream_values = $this->getValuesFromGoals($month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
|
||||
$stream_values['less']['conversion'] = $value;
|
||||
$stream_values['more']['conversion'] = $value;
|
||||
$goal_diff = $value - $month_goal;
|
||||
|
||||
if ($value > 0 && $value < $month_goal) {
|
||||
$stream_values['less']['goal_diff'] = round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2);
|
||||
} elseif ($value > 0) {
|
||||
$stream_values['more']['goal_diff'] = round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2);
|
||||
}
|
||||
$monthGoalInfo = array();
|
||||
if ($goal_diff < 0) {
|
||||
$monthGoalInfo['complete'] = 0;
|
||||
} else {
|
||||
$monthGoalInfo['complete'] = 1;
|
||||
}
|
||||
$goal_diff = abs($goal_diff);
|
||||
$monthGoalInfo['value_type'] = self::DG_VALUE_TYPE_PERCENT;
|
||||
$monthGoalInfo['value'] = Tools::ps_round($value, 2);
|
||||
$monthGoalInfo['goal'] = Tools::ps_round($month_goal, 2);
|
||||
$monthGoalInfo['goal_diff'] = Tools::ps_round($goal_diff, 2);
|
||||
$monthGoalInfo['goal_diff_percent'] = ($value > 0) ? Tools::ps_round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2) : 100;
|
||||
// for dummy data future goal will be 0 always
|
||||
$monthGoalInfo['is_future_goal'] = 0;
|
||||
|
||||
if ($value == 0) {
|
||||
$streams['conversion']['less']['zone_text'] = $this->l('Goal set:');
|
||||
$stream_values['less']['goal'] = $month_goal;
|
||||
}
|
||||
$stream_values['real'] = array_merge($stream_values['real'], $monthGoalInfo);
|
||||
$stream_values['less'] = array_merge($stream_values['less'], $monthGoalInfo);
|
||||
$stream_values['more'] = array_merge($stream_values['more'], $monthGoalInfo);
|
||||
|
||||
foreach ($stream_zones as $stream_zone) {
|
||||
$stream_values[$stream_zone['zone']]['title'] = $streams['conversion'][$stream_zone['zone']]['title'];
|
||||
$streams['conversion'][$stream_zone['zone']]['values'][] = $stream_values[$stream_zone['zone']];
|
||||
}
|
||||
|
||||
// Start setting avg cart value dummy data
|
||||
$month_goal = $this->fakeConfigurationKPI_get('DASHGOALS_AVG_CART_VALUE_'.$i.'_'.$year);
|
||||
$value = ((isset($orders[$timestamp]) && $orders[$timestamp] && isset($sales[$timestamp]) && $sales[$timestamp]) ? ($sales[$timestamp] / $orders[$timestamp]) : 0);
|
||||
$stream_values = $this->getValuesFromGoals($average_goals['avg_cart_value'], $month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
$stream_values = $this->getValuesFromGoals($month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
|
||||
$goal_diff = $value - $month_goal;
|
||||
$stream_values['real']['avg_cart_value'] = $value;
|
||||
$stream_values['real']['goal'] = $month_goal;
|
||||
if ($value > 0) {
|
||||
$stream_values['real']['goal_diff'] = round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2);
|
||||
}
|
||||
|
||||
$stream_values['less']['avg_cart_value'] = $value;
|
||||
$stream_values['more']['avg_cart_value'] = $value;
|
||||
$monthGoalInfo = array();
|
||||
if ($goal_diff < 0) {
|
||||
$monthGoalInfo['complete'] = 0;
|
||||
} else {
|
||||
$monthGoalInfo['complete'] = 1;
|
||||
}
|
||||
$goal_diff = abs($goal_diff);
|
||||
$monthGoalInfo['value_type'] = self::DG_VALUE_TYPE_PRICE;
|
||||
$monthGoalInfo['value'] = Tools::ps_round($value, 2);
|
||||
$monthGoalInfo['goal'] = Tools::ps_round($month_goal, 2);
|
||||
$monthGoalInfo['goal_diff'] = Tools::ps_round($goal_diff, 2);
|
||||
$monthGoalInfo['goal_diff_percent'] = ($value > 0) ? Tools::ps_round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2) : 100;
|
||||
// for dummy data future goal will be 0 always
|
||||
$monthGoalInfo['is_future_goal'] = 0;
|
||||
|
||||
if ($value > 0 && $value < $month_goal) {
|
||||
$stream_values['less']['goal_diff'] = $goal_diff;
|
||||
} elseif ($value > 0) {
|
||||
$stream_values['more']['goal_diff'] = $goal_diff;
|
||||
}
|
||||
|
||||
if ($value == 0) {
|
||||
$streams['avg_cart_value']['less']['zone_text'] = $this->l('Goal set:');
|
||||
$stream_values['less']['goal'] = $month_goal;
|
||||
}
|
||||
$stream_values['real'] = array_merge($stream_values['real'], $monthGoalInfo);
|
||||
$stream_values['less'] = array_merge($stream_values['less'], $monthGoalInfo);
|
||||
$stream_values['more'] = array_merge($stream_values['more'], $monthGoalInfo);
|
||||
|
||||
foreach ($stream_zones as $stream_zone) {
|
||||
$stream_values[$stream_zone['zone']]['title'] = $streams['avg_cart_value'][$stream_zone['zone']]['title'];
|
||||
$streams['avg_cart_value'][$stream_zone['zone']]['values'][] = $stream_values[$stream_zone['zone']];
|
||||
}
|
||||
|
||||
// Start setting sales dummy data
|
||||
$month_goal = $this->fakeConfigurationKPI_get('DASHGOALS_TRAFFIC_'.$i.'_'.$year) * $this->fakeConfigurationKPI_get('DASHGOALS_CONVERSION_'.$i.'_'.$year) / 100 * $this->fakeConfigurationKPI_get('DASHGOALS_AVG_CART_VALUE_'.$i.'_'.$year);
|
||||
$value = (isset($sales[$timestamp]) ? $sales[$timestamp] : 0);
|
||||
$stream_values = $this->getValuesFromGoals($average_goals['sales'], $month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
$goal_diff = $value - $month_goal;
|
||||
$stream_values['real']['sales'] = $value;
|
||||
$stream_values['real']['goal'] = $month_goal;
|
||||
$stream_values = $this->getValuesFromGoals($month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
|
||||
if ($value > 0) {
|
||||
$stream_values['real']['goal_diff'] = round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2);
|
||||
}
|
||||
$goal_diff = $value - $month_goal;
|
||||
|
||||
$stream_values['less']['sales'] = $value;
|
||||
$stream_values['more']['sales'] = $value;
|
||||
$monthGoalInfo = array();
|
||||
if ($goal_diff < 0) {
|
||||
$monthGoalInfo['complete'] = 0;
|
||||
} else {
|
||||
$monthGoalInfo['complete'] = 1;
|
||||
}
|
||||
$goal_diff = abs($goal_diff);
|
||||
$monthGoalInfo['value_type'] = self::DG_VALUE_TYPE_PRICE;
|
||||
$monthGoalInfo['value'] = Tools::ps_round($value, 2);
|
||||
$monthGoalInfo['goal'] = Tools::ps_round($month_goal, 2);
|
||||
$monthGoalInfo['goal_diff'] = Tools::ps_round($goal_diff, 2);
|
||||
$monthGoalInfo['goal_diff_percent'] = ($value > 0) ? Tools::ps_round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2) : 100;
|
||||
// for dummy data future goal will be 0 always
|
||||
$monthGoalInfo['is_future_goal'] = 0;
|
||||
|
||||
if ($value > 0 && $value < $month_goal) {
|
||||
$stream_values['less']['goal_diff'] = $goal_diff;
|
||||
} elseif ($value > 0) {
|
||||
$stream_values['more']['goal_diff'] = $goal_diff;
|
||||
}
|
||||
|
||||
if ($value == 0) {
|
||||
$streams['sales']['less']['zone_text'] = $this->l('Goal set:');
|
||||
$stream_values['less']['goal'] = $month_goal;
|
||||
}
|
||||
$stream_values['real'] = array_merge($stream_values['real'], $monthGoalInfo);
|
||||
$stream_values['less'] = array_merge($stream_values['less'], $monthGoalInfo);
|
||||
$stream_values['more'] = array_merge($stream_values['more'], $monthGoalInfo);
|
||||
|
||||
foreach ($stream_zones as $stream_zone) {
|
||||
$stream_values[$stream_zone['zone']]['title'] = $streams['sales'][$stream_zone['zone']]['title'];
|
||||
$streams['sales'][$stream_zone['zone']]['values'][] = $stream_values[$stream_zone['zone']];
|
||||
}
|
||||
}
|
||||
@@ -371,138 +373,141 @@ class Dashgoals extends Module
|
||||
$orders = AdminStatsController::getOrders($year.date('-01-01'), $year.date('-12-31'), 'month');
|
||||
$sales = AdminStatsController::getTotalSales($year.date('-01-01'), $year.date('-12-31'), 'month');
|
||||
|
||||
// We need to calculate the average value of each goals for the year, this will be the base rate for "100%"
|
||||
for ($i = '01'; $i <= 12; $i = sprintf('%02d', $i + 1)) {
|
||||
$average_goals['traffic'] += ConfigurationKPI::get('DASHGOALS_TRAFFIC_'.$i.'_'.$year);
|
||||
$average_goals['conversion'] += ConfigurationKPI::get('DASHGOALS_CONVERSION_'.$i.'_'.$year) / 100;
|
||||
$average_goals['avg_cart_value'] += ConfigurationKPI::get('DASHGOALS_AVG_CART_VALUE_'.$i.'_'.$year);
|
||||
}
|
||||
foreach ($average_goals as &$average_goal) {
|
||||
$average_goal /= 12;
|
||||
}
|
||||
|
||||
$average_goals['sales'] = $average_goals['traffic'] * $average_goals['conversion'] * $average_goals['avg_cart_value'];
|
||||
|
||||
// Now we can calculate the value for every months
|
||||
for ($i = '01'; $i <= 12; $i = sprintf('%02d', $i + 1)) {
|
||||
$timestamp = strtotime($year.'-'.$i.'-01');
|
||||
|
||||
// send if goal is for future or past
|
||||
$isFutureGoal = 0;
|
||||
if (strtotime($year.'-'.$i) > strtotime(date('Y-m'))) {
|
||||
$isFutureGoal = 1;
|
||||
}
|
||||
|
||||
// Start setting traffic data
|
||||
$month_goal = ConfigurationKPI::get('DASHGOALS_TRAFFIC_'.$i.'_'.$year);
|
||||
$value = (isset($visits[$timestamp]) ? $visits[$timestamp] : 0);
|
||||
$stream_values = $this->getValuesFromGoals($average_goals['traffic'], $month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
$stream_values = $this->getValuesFromGoals($month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
|
||||
$goal_diff = $value - $month_goal;
|
||||
$stream_values['real']['traffic'] = $value;
|
||||
$stream_values['real']['goal'] = $month_goal;
|
||||
if ($value > 0) {
|
||||
$stream_values['real']['goal_diff'] = round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2);
|
||||
}
|
||||
|
||||
$stream_values['less']['traffic'] = $value;
|
||||
$stream_values['more']['traffic'] = $value;
|
||||
$monthGoalInfo = array();
|
||||
if ($goal_diff < 0) {
|
||||
$monthGoalInfo['complete'] = 0;
|
||||
} else {
|
||||
$monthGoalInfo['complete'] = 1;
|
||||
}
|
||||
$goal_diff = abs($goal_diff);
|
||||
$monthGoalInfo['value_type'] = self::DG_VALUE_TYPE_TEXT;
|
||||
$monthGoalInfo['value'] = Tools::ps_round($value, 2);
|
||||
$monthGoalInfo['goal'] = Tools::ps_round($month_goal, 2);
|
||||
$monthGoalInfo['goal_diff'] = Tools::ps_round($goal_diff, 2);
|
||||
$monthGoalInfo['goal_diff_percent'] = ($value > 0) ? Tools::ps_round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2) : 100;
|
||||
$monthGoalInfo['is_future_goal'] = $isFutureGoal;
|
||||
|
||||
if ($value > 0 && $value < $month_goal) {
|
||||
$stream_values['less']['goal_diff'] = $goal_diff;
|
||||
} elseif ($value > 0) {
|
||||
$stream_values['more']['goal_diff'] = $goal_diff;
|
||||
}
|
||||
|
||||
if ($value == 0) {
|
||||
$streams['traffic']['less']['zone_text'] = $this->l('Goal set:');
|
||||
$stream_values['less']['goal'] = $month_goal;
|
||||
}
|
||||
$stream_values['real'] = array_merge($stream_values['real'], $monthGoalInfo);
|
||||
$stream_values['less'] = array_merge($stream_values['less'], $monthGoalInfo);
|
||||
$stream_values['more'] = array_merge($stream_values['more'], $monthGoalInfo);
|
||||
|
||||
foreach ($stream_zones as $stream_zone) {
|
||||
$stream_values[$stream_zone['zone']]['title'] = $streams['traffic'][$stream_zone['zone']]['title'];
|
||||
$streams['traffic'][$stream_zone['zone']]['values'][] = $stream_values[$stream_zone['zone']];
|
||||
}
|
||||
|
||||
// Start setting conversion rate data
|
||||
$month_goal = ConfigurationKPI::get('DASHGOALS_CONVERSION_'.$i.'_'.$year);
|
||||
$value = 100 * ((isset($visits[$timestamp]) && $visits[$timestamp] && isset($orders[$timestamp]) && $orders[$timestamp]) ? ($orders[$timestamp] / $visits[$timestamp]) : 0);
|
||||
$stream_values = $this->getValuesFromGoals($average_goals['conversion'] * 100, $month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
$stream_values = $this->getValuesFromGoals($month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
|
||||
$goal_diff = $value - $month_goal;
|
||||
$stream_values['real']['conversion'] = round($value, 2);
|
||||
$stream_values['real']['goal'] = round($month_goal, 2);
|
||||
if ($value > 0) {
|
||||
$stream_values['real']['goal_diff'] = round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2);
|
||||
}
|
||||
|
||||
$stream_values['less']['conversion'] = $value;
|
||||
$stream_values['more']['conversion'] = $value;
|
||||
$monthGoalInfo = array();
|
||||
if ($goal_diff < 0) {
|
||||
$monthGoalInfo['complete'] = 0;
|
||||
} else {
|
||||
$monthGoalInfo['complete'] = 1;
|
||||
}
|
||||
$goal_diff = abs($goal_diff);
|
||||
$monthGoalInfo['value'] = Tools::ps_round($value, 2);
|
||||
$monthGoalInfo['value_type'] = self::DG_VALUE_TYPE_PERCENT;
|
||||
$monthGoalInfo['goal'] = Tools::ps_round($month_goal, 2);
|
||||
$monthGoalInfo['goal_diff'] = Tools::ps_round($goal_diff, 2);
|
||||
$monthGoalInfo['goal_diff_percent'] = ($value > 0) ? Tools::ps_round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2) : 100;
|
||||
$monthGoalInfo['is_future_goal'] = $isFutureGoal;
|
||||
|
||||
if ($value > 0 && $value < $month_goal) {
|
||||
$stream_values['less']['goal_diff'] = round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2);
|
||||
} elseif ($value > 0) {
|
||||
$stream_values['more']['goal_diff'] = round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2);
|
||||
}
|
||||
|
||||
if ($value == 0) {
|
||||
$streams['conversion']['less']['zone_text'] = $this->l('Goal set:');
|
||||
$stream_values['less']['goal'] = $month_goal;
|
||||
}
|
||||
$stream_values['real'] = array_merge($stream_values['real'], $monthGoalInfo);
|
||||
$stream_values['less'] = array_merge($stream_values['less'], $monthGoalInfo);
|
||||
$stream_values['more'] = array_merge($stream_values['more'], $monthGoalInfo);
|
||||
|
||||
foreach ($stream_zones as $stream_zone) {
|
||||
$stream_values[$stream_zone['zone']]['title'] = $streams['conversion'][$stream_zone['zone']]['title'];
|
||||
$streams['conversion'][$stream_zone['zone']]['values'][] = $stream_values[$stream_zone['zone']];
|
||||
}
|
||||
|
||||
// Start setting average cart value data
|
||||
$month_goal = ConfigurationKPI::get('DASHGOALS_AVG_CART_VALUE_'.$i.'_'.$year);
|
||||
$value = ((isset($orders[$timestamp]) && $orders[$timestamp] && isset($sales[$timestamp]) && $sales[$timestamp]) ? ($sales[$timestamp] / $orders[$timestamp]) : 0);
|
||||
$stream_values = $this->getValuesFromGoals($average_goals['avg_cart_value'], $month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
$stream_values = $this->getValuesFromGoals($month_goal, $value, Dashgoals::$month_labels[$i]);
|
||||
|
||||
$goal_diff = $value - $month_goal;
|
||||
$stream_values['real']['avg_cart_value'] = $value;
|
||||
$stream_values['real']['goal'] = $month_goal;
|
||||
if ($value > 0) {
|
||||
$stream_values['real']['goal_diff'] = round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2);
|
||||
}
|
||||
|
||||
$stream_values['less']['avg_cart_value'] = $value;
|
||||
$stream_values['more']['avg_cart_value'] = $value;
|
||||
$monthGoalInfo = array();
|
||||
if ($goal_diff < 0) {
|
||||
$monthGoalInfo['complete'] = 0;
|
||||
} else {
|
||||
$monthGoalInfo['complete'] = 1;
|
||||
}
|
||||
$goal_diff = abs($goal_diff);
|
||||
$monthGoalInfo['value'] = Tools::ps_round($value, 2);
|
||||
$monthGoalInfo['value_type'] = self::DG_VALUE_TYPE_PRICE;
|
||||
$monthGoalInfo['goal'] = Tools::ps_round($month_goal, 2);
|
||||
$monthGoalInfo['goal_diff'] = Tools::ps_round($goal_diff, 2);
|
||||
$monthGoalInfo['goal_diff_percent'] = ($value > 0) ? Tools::ps_round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2) : 100;
|
||||
$monthGoalInfo['is_future_goal'] = $isFutureGoal;
|
||||
|
||||
if ($value > 0 && $value < $month_goal) {
|
||||
$stream_values['less']['goal_diff'] = $goal_diff;
|
||||
} elseif ($value > 0) {
|
||||
$stream_values['more']['goal_diff'] = $goal_diff;
|
||||
}
|
||||
|
||||
if ($value == 0) {
|
||||
$streams['avg_cart_value']['less']['zone_text'] = $this->l('Goal set:');
|
||||
$stream_values['less']['goal'] = $month_goal;
|
||||
}
|
||||
$stream_values['real'] = array_merge($stream_values['real'], $monthGoalInfo);
|
||||
$stream_values['less'] = array_merge($stream_values['less'], $monthGoalInfo);
|
||||
$stream_values['more'] = array_merge($stream_values['more'], $monthGoalInfo);
|
||||
|
||||
foreach ($stream_zones as $stream_zone) {
|
||||
$stream_values[$stream_zone['zone']]['title'] = $streams['avg_cart_value'][$stream_zone['zone']]['title'];
|
||||
$streams['avg_cart_value'][$stream_zone['zone']]['values'][] = $stream_values[$stream_zone['zone']];
|
||||
}
|
||||
|
||||
// Start setting up data for sales
|
||||
$month_goal = ConfigurationKPI::get('DASHGOALS_TRAFFIC_'.$i.'_'.$year) * ConfigurationKPI::get('DASHGOALS_CONVERSION_'.$i.'_'.$year) / 100 * ConfigurationKPI::get('DASHGOALS_AVG_CART_VALUE_'.$i.'_'.$year);
|
||||
|
||||
$value = (isset($sales[$timestamp]) && $sales[$timestamp]) ? $sales[$timestamp] : 0;
|
||||
$stream_values = $this->getValuesFromGoals($average_goals['sales'], $month_goal, isset($sales[$timestamp]) ? $sales[$timestamp] : 0, Dashgoals::$month_labels[$i]);
|
||||
|
||||
$stream_values = $this->getValuesFromGoals($month_goal, isset($sales[$timestamp]) ? $sales[$timestamp] : 0, Dashgoals::$month_labels[$i]);
|
||||
|
||||
$goal_diff = $value - $month_goal;
|
||||
$stream_values['real']['sales'] = $value;
|
||||
$stream_values['real']['goal'] = $month_goal;
|
||||
|
||||
if ($value > 0) {
|
||||
$stream_values['real']['goal_diff'] = round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2);
|
||||
}
|
||||
$monthGoalInfo = array();
|
||||
if ($goal_diff < 0) {
|
||||
$monthGoalInfo['complete'] = 0;
|
||||
} else {
|
||||
$monthGoalInfo['complete'] = 1;
|
||||
}
|
||||
$goal_diff = abs($goal_diff);
|
||||
$monthGoalInfo['value'] = Tools::ps_round($value, 2);
|
||||
$monthGoalInfo['value_type'] = self::DG_VALUE_TYPE_PRICE;
|
||||
$monthGoalInfo['goal'] = Tools::ps_round($month_goal, 2);
|
||||
$monthGoalInfo['goal_diff'] = Tools::ps_round($goal_diff, 2);
|
||||
$monthGoalInfo['goal_diff_percent'] = ($value > 0) ? Tools::ps_round(($goal_diff * 100) / ($month_goal > 0 ? $month_goal : 1), 2) : 100;
|
||||
$monthGoalInfo['is_future_goal'] = $isFutureGoal;
|
||||
|
||||
$stream_values['less']['sales'] = $value;
|
||||
$stream_values['more']['sales'] = $value;
|
||||
|
||||
if ($value > 0 && $value < $month_goal) {
|
||||
$stream_values['less']['goal_diff'] = $goal_diff;
|
||||
} elseif ($value > 0) {
|
||||
$stream_values['more']['goal_diff'] = $goal_diff;
|
||||
}
|
||||
|
||||
if ($value == 0) {
|
||||
$streams['sales']['less']['zone_text'] = $this->l('Goal set:');
|
||||
$stream_values['less']['goal'] = $month_goal;
|
||||
}
|
||||
$stream_values['real'] = array_merge($stream_values['real'], $monthGoalInfo);
|
||||
$stream_values['less'] = array_merge($stream_values['less'], $monthGoalInfo);
|
||||
$stream_values['more'] = array_merge($stream_values['more'], $monthGoalInfo);
|
||||
|
||||
foreach ($stream_zones as $stream_zone) {
|
||||
$stream_values[$stream_zone['zone']]['title'] = $streams['sales'][$stream_zone['zone']]['title'];
|
||||
$streams['sales'][$stream_zone['zone']]['values'][] = $stream_values[$stream_zone['zone']];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge all the streams before sending
|
||||
// Merge all the streams before sending
|
||||
$all_streams = array();
|
||||
foreach ($stream_types as $stream_type) {
|
||||
foreach ($stream_zones as $stream_zone) {
|
||||
@@ -513,40 +518,28 @@ class Dashgoals extends Module
|
||||
return array('chart_type' => 'bar_chart_goals', 'data' => $all_streams);
|
||||
}
|
||||
|
||||
protected function getValuesFromGoals($average_goal, $month_goal, $value, $label)
|
||||
{
|
||||
// Initialize value for each zone
|
||||
$stream_values = array(
|
||||
'real' => array('x' => $label, 'y' => 0),
|
||||
'less' => array('x' => $label, 'y' => 0),
|
||||
'more' => array('x' => $label, 'y' => 0)
|
||||
);
|
||||
protected function getValuesFromGoals($month_goal, $value, $label)
|
||||
{
|
||||
// Initialize value for each zone
|
||||
$stream_values = [
|
||||
'real' => ['x' => $label, 'y' => 0],
|
||||
'less' => ['x' => $label, 'y' => 0],
|
||||
'more' => ['x' => $label, 'y' => 0],
|
||||
];
|
||||
|
||||
// Calculate the percentage of fullfilment of the goal
|
||||
$fullfilment = 0;
|
||||
if ($value && $month_goal) {
|
||||
$fullfilment = round($value / $month_goal, 2);
|
||||
}
|
||||
if ($value > 0) {
|
||||
$goalDiff = $value - $month_goal;
|
||||
if (($goalDiff) > 0) {
|
||||
$stream_values['real']['y'] = (float)Tools::ps_round($month_goal, 2);
|
||||
$stream_values['more']['y'] = (float)Tools::ps_round($goalDiff, 2);
|
||||
} elseif (($goalDiff) < 0) {
|
||||
$stream_values['real']['y'] = (float)Tools::ps_round($value, 2);
|
||||
$stream_values['less']['y'] = (float)Tools::ps_round(($month_goal - $value), 2);
|
||||
}
|
||||
} else {
|
||||
$stream_values['less']['y'] = (float)Tools::ps_round($month_goal, 2);
|
||||
}
|
||||
|
||||
// Base rate is essential here : it determines the value of the goal compared to the "100%" of the chart legend
|
||||
$base_rate = 0;
|
||||
if ($average_goal && $month_goal) {
|
||||
$base_rate = $month_goal / $average_goal;
|
||||
}
|
||||
|
||||
// Fullfilment of 1 means that we performed exactly anticipated
|
||||
if ($fullfilment == 1) {
|
||||
$stream_values['real'] = array('x' => $label, 'y' => round($base_rate, 2));
|
||||
} elseif ($fullfilment < 1) {
|
||||
// Fullfilment lower than 1 means that we UNDER performed
|
||||
$stream_values['real'] = array('x' => $label, 'y' => round($fullfilment * $base_rate, 2));
|
||||
$stream_values['less'] = array('x' => $label, 'y' => round($base_rate - ($fullfilment * $base_rate), 2));
|
||||
} elseif ($fullfilment > 1) {
|
||||
// Fullfilment greater than 1 means that we OVER performed
|
||||
$stream_values['real'] = array('x' => $label, 'y' => round($base_rate, 2));
|
||||
$stream_values['more'] = array('x' => $label, 'y' => round(($fullfilment * $base_rate) - $base_rate, 2));
|
||||
}
|
||||
|
||||
return $stream_values;
|
||||
}
|
||||
return $stream_values;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,73 +8,13 @@ function bar_chart_goals(widget_name, chart_details)
|
||||
|
||||
var chart = nv.models.multiBarChart()
|
||||
.stacked(true)
|
||||
.showControls(false)
|
||||
.tooltipContent(function(key, y, e, graph) {
|
||||
if (graph.value == 0)
|
||||
return '';
|
||||
.forceY([0, 1])
|
||||
.showControls(false);
|
||||
|
||||
if (key == 'sales_real') {
|
||||
var result = '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + formatCurrency(parseInt(graph.point.sales), currency_format, currency_sign, currency_blank) + '</strong><br />(' + formatCurrency(parseInt(graph.point.goal), currency_format, currency_sign, currency_blank) + ')<br/>';
|
||||
if (graph.point.sales > graph.point.goal)
|
||||
result += '<span class="dash_trend dash_trend_up">+';
|
||||
else
|
||||
result += '<span class="dash_trend dash_trend_down">';
|
||||
result += graph.point.goal_diff + '%</span></div>';
|
||||
return result;
|
||||
} else if (key == 'sales_less') {
|
||||
if (graph.point.sales > 0)
|
||||
return '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.series.zone_text + '</strong><br /><span class="dash_trend dash_trend_down">' + formatCurrency(parseInt(graph.point.goal_diff), currency_format, currency_sign, currency_blank) + '</span></div>';
|
||||
else
|
||||
return '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.series.zone_text + '</strong><br />(' + formatCurrency(parseInt(graph.point.goal), currency_format, currency_sign, currency_blank) + ')</div>';
|
||||
} else if (key == 'sales_more')
|
||||
return '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.series.zone_text + '</strong><br /><span class="dash_trend dash_trend_up">+' + formatCurrency(parseInt(graph.point.goal_diff), currency_format, currency_sign, currency_blank) + '</span></div>';
|
||||
else if (key == 'avg_cart_value_real') {
|
||||
var result = '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + formatCurrency(parseInt(graph.point.avg_cart_value), currency_format, currency_sign, currency_blank) + '</strong><br />(' + formatCurrency(parseInt(graph.point.goal), currency_format, currency_sign, currency_blank) + ')<br/>';
|
||||
if (graph.point.avg_cart_value > graph.point.goal)
|
||||
result += '<span class="dash_trend dash_trend_up">+';
|
||||
else
|
||||
result += '<span class="dash_trend dash_trend_down">';
|
||||
result += graph.point.goal_diff + '%</span></div>';
|
||||
return result;
|
||||
} else if (key == 'avg_cart_value_less') {
|
||||
if (graph.point.avg_cart_value > 0)
|
||||
return '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.series.zone_text + '</strong><br /><span class="dash_trend dash_trend_down">' + formatCurrency(parseInt(graph.point.goal_diff), currency_format, currency_sign, currency_blank) + '</span></div>';
|
||||
else
|
||||
return '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.series.zone_text + '</strong><br />(' + formatCurrency(parseInt(graph.point.goal), currency_format, currency_sign, currency_blank) + ')</div>';
|
||||
} else if (key == 'avg_cart_value_more')
|
||||
return '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.series.zone_text + '</strong><br /><span class="dash_trend dash_trend_up">+' + formatCurrency(parseInt(graph.point.goal_diff), currency_format, currency_sign, currency_blank) + '</span></div>';
|
||||
else if (key == 'traffic_real') {
|
||||
var result = '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.point.traffic + ' ' +graph.series.unit_text+'</strong><br />(' + graph.point.goal + ' ' +graph.series.unit_text+')<br/>';
|
||||
if (graph.point.traffic > graph.point.goal)
|
||||
result += '<span class="dash_trend dash_trend_up">+';
|
||||
else
|
||||
result += '<span class="dash_trend dash_trend_down">';
|
||||
result += graph.point.goal_diff + '%</span></div>';
|
||||
return result;
|
||||
} else if (key == 'traffic_less') {
|
||||
if (graph.point.traffic > 0)
|
||||
return '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.series.zone_text + '</strong><br /><span class="dash_trend dash_trend_down">' + graph.point.goal_diff + ' ' +graph.series.unit_text+'</span></div>';
|
||||
else
|
||||
return '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.series.zone_text + '</strong><br />(' + graph.point.goal + ' ' +graph.series.unit_text+')</div>';
|
||||
} else if (key == 'traffic_more')
|
||||
return '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.series.zone_text + '</strong><br /><span class="dash_trend dash_trend_up">+' + graph.point.goal_diff + ' ' +graph.series.unit_text+'</span></div>';
|
||||
else if (key == 'conversion_real') {
|
||||
var result = '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.point.conversion + '%</strong><br />(' + graph.point.goal + '%)</strong><br/>';
|
||||
if (graph.point.conversion > graph.point.goal)
|
||||
result += '<span class="dash_trend dash_trend_up">+';
|
||||
else
|
||||
result += '<span class="dash_trend dash_trend_down">';
|
||||
result += graph.point.goal_diff + '%</span></div>';
|
||||
return result;
|
||||
} else if (key == 'conversion_less') {
|
||||
if (graph.point.conversion > 0)
|
||||
return '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.series.zone_text + '</strong><br /><span class="dash_trend dash_trend_down">' + graph.point.goal_diff + '%</span></div>';
|
||||
else
|
||||
return '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.series.zone_text + '</strong><br />(' + graph.point.goal + '%)</div>';
|
||||
} else if (key == 'conversion_more')
|
||||
return '<div class="tooltip-panel"><div class="tooltip-panel-heading">' + graph.series.title + '</div><strong>' + graph.series.zone_text + '</strong><br /><span class="dash_trend dash_trend_up">+' + graph.point.goal_diff + '%</span></div>';
|
||||
|
||||
});
|
||||
// create content for the tooltip of chart
|
||||
chart.tooltip.contentGenerator((obj, element) => {
|
||||
return getDashgoalsTooltipContent(obj, chart_details);
|
||||
});
|
||||
|
||||
dashgoals_chart = chart;
|
||||
|
||||
@@ -87,6 +27,9 @@ function bar_chart_goals(widget_name, chart_details)
|
||||
|
||||
nv.utils.windowResize(chart.update);
|
||||
|
||||
// select first chart to set/format values
|
||||
selectDashgoalsChart('sales');
|
||||
|
||||
return chart;
|
||||
});
|
||||
}
|
||||
@@ -103,19 +46,21 @@ function selectDashgoalsChart(type)
|
||||
});
|
||||
}
|
||||
|
||||
dashgoals_chart.yAxis.tickFormat(d3.format('.1f'));
|
||||
|
||||
if (type == 'avg_cart_value' || type == 'sales') {
|
||||
dashgoals_chart.yAxis.tickFormat(d3.format('.2f'));
|
||||
|
||||
dashgoals_chart.yAxis.tickFormat(function(d) {
|
||||
return formatCurrency(parseFloat(d), currency_format, currency_sign, currency_blank);
|
||||
});
|
||||
}
|
||||
}else if (type == 'conversion') {
|
||||
dashgoals_chart.yAxis.tickFormat(d3.format('.2f'));
|
||||
|
||||
if (type == 'conversion') {
|
||||
dashgoals_chart.yAxis.tickFormat(function(d) {
|
||||
return d + ' %';
|
||||
});
|
||||
}
|
||||
}else if (type == 'traffic') {
|
||||
dashgoals_chart.yAxis.tickFormat(d3.format('d'));
|
||||
}
|
||||
|
||||
dashgoals_toggleDashConfig();
|
||||
}
|
||||
@@ -174,6 +119,52 @@ function dashgoals_changeYear(xward)
|
||||
});
|
||||
}
|
||||
|
||||
// Get tooltip content for the dashgoals chart
|
||||
function getDashgoalsTooltipContent(graphDataObject)
|
||||
{
|
||||
var tooltipLabel = graphDataObject.data.title + ' (' + graphDataObject.value + ')';
|
||||
var tooltipContent = '';
|
||||
|
||||
var goal = graphDataObject.data.goal;
|
||||
var goalDiffrence = graphDataObject.data.goal_diff;
|
||||
var currentValue = graphDataObject.data.value;
|
||||
|
||||
if (graphDataObject.data.value_type == VALUE_TYPE_PRICE) {
|
||||
goal = formatCurrency(parseFloat(graphDataObject.data.goal), currency_format, currency_sign, currency_blank);
|
||||
currentValue = formatCurrency(parseFloat(graphDataObject.data.value), currency_format, currency_sign, currency_blank);
|
||||
goalDiffrence = formatCurrency(parseFloat(graphDataObject.data.goal_diff), currency_format, currency_sign, currency_blank);
|
||||
} else if (graphDataObject.data.value_type == VALUE_TYPE_PERCENT) {
|
||||
goal = graphDataObject.data.goal + ' %';
|
||||
currentValue = graphDataObject.data.value + ' %';
|
||||
}
|
||||
|
||||
tooltipContent += '<p>' + goal_set_txt + ': <b>' + goal + '</b></p>';
|
||||
|
||||
// some values must be displayed for passed months
|
||||
if (graphDataObject.data.is_future_goal == 0) {
|
||||
tooltipContent += '<p>' + graphDataObject.data.title + ': <b>' + currentValue + '</b></p>';;
|
||||
|
||||
tooltipContent += '<p>' + goal_diff_txt + ': <b>';
|
||||
if (graphDataObject.data.complete == 1) {
|
||||
tooltipContent += '<span class="text-success">+ ';
|
||||
} else {
|
||||
tooltipContent += '<span class="text-danger">- ';
|
||||
}
|
||||
|
||||
tooltipContent += goalDiffrence + '</span></b></p>';
|
||||
|
||||
tooltipContent += '<p class="';
|
||||
if (graphDataObject.data.complete == 1) {
|
||||
tooltipContent += 'text-success"><b><i class="icon-circle-arrow-up"> ';
|
||||
} else {
|
||||
tooltipContent += 'text-danger"><b><i class="icon-circle-arrow-down"> ';
|
||||
}
|
||||
tooltipContent += '</i> ' + graphDataObject.data.goal_diff_percent + '%</b></p>';
|
||||
}
|
||||
|
||||
return getTooltipContent(tooltipLabel, tooltipContent, graphDataObject.color);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$('.dashgoals_config_input').keyup(function() { dashgoals_calc_sales(); });
|
||||
dashgoals_calc_sales();
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<a href="javascript:void(0);" onclick="dashgoals_changeYear('backward');" class="btn btn-default btn-xs"><i class="icon-backward"></i></a>
|
||||
<a href="javascript:void(0);" onclick="dashgoals_changeYear('forward');" class="btn btn-default btn-xs"><i class="icon-forward"></i></a>
|
||||
</span>
|
||||
|
||||
|
||||
<span class="panel-heading-action">
|
||||
<a class="list-toolbar-btn" href="javascript:void(0);" onclick="toggleDashConfig('dashgoals');" title="{l s="Configure" mod="dashtrends"}">
|
||||
<i class="process-icon-configure"></i>
|
||||
@@ -56,9 +56,16 @@
|
||||
{include file='./config.tpl'}
|
||||
<section class="loading text-center">
|
||||
<div class="alert alert-info text-left">
|
||||
<p>{l s='Please use the configuration button right above here to set targets.' mod='dashgoals'}</p>
|
||||
<p>{l s='Set your targets by clicking the configuration button at right position of the header in this section.' mod='dashgoals'}</p>
|
||||
</div>
|
||||
<div class="dashgoals row">
|
||||
<div class="col-xs-6 col-sm-3">
|
||||
<label class="btn btn-default label-tooltip" style="background-color:{$colors[3]};"
|
||||
data-toggle="tooltip" data-original-title="{l s="Sales is the measure of total sales on your website over a given time period." mod="dashgoals"}">
|
||||
<input type="radio" name="options" onchange="selectDashgoalsChart('sales');"/>
|
||||
{l s="Sales" mod="dashgoals"}
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-3">
|
||||
<label class="btn btn-default label-tooltip" style="background-color:{$colors[0]};"
|
||||
data-toggle="tooltip" data-original-title="{l s="Traffic is the measure of number of visitors on your website over a given time period." mod="dashgoals"}">
|
||||
@@ -80,13 +87,6 @@
|
||||
{l s="Avg. Order Value" mod="dashgoals"}
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-3">
|
||||
<label class="btn btn-default label-tooltip" style="background-color:{$colors[3]};"
|
||||
data-toggle="tooltip" data-original-title="{l s="Sales is the measure of total sales on your website over a given time period." mod="dashgoals"}">
|
||||
<input type="radio" name="options" onchange="selectDashgoalsChart('sales');"/>
|
||||
{l s="Sales" mod="dashgoals"}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dash_goals_chart1" class="chart with-transitions">
|
||||
<svg></svg>
|
||||
|
||||
@@ -93,7 +93,7 @@ class DashGuestCycle extends Module
|
||||
$dataValue['dgc_total_arrivals'] = sprintf('%02d', $arrivalsData['total_arrivals']);
|
||||
$dataValue['dgc_departed'] = sprintf('%02d', $departuresData['departed']);
|
||||
$dataValue['dgc_total_departures'] = sprintf('%02d', $departuresData['total_departures']);
|
||||
$dataValue['dgc_new_bookings'] = sprintf('%02d', AdminStatsController::getBookingsByDate($dateToday, $params['id_hotel']));
|
||||
$dataValue['dgc_new_bookings'] = sprintf('%02d', count(AdminStatsController::getNewBookingsInfoByDate($dateToday, $params['id_hotel'])));
|
||||
$dataValue['dgc_stay_overs'] = sprintf('%02d', AdminStatsController::getStayOversByDate($dateToday, $params['id_hotel']));
|
||||
$dataValue['dgc_new_messages'] = sprintf('%02d', CustomerMessage::getMessagesByDate($dateToday));
|
||||
$dataValue['dgc_cancelled_bookings'] = sprintf('%02d', AdminStatsController::getCancelledBookingsByDate($dateToday, $params['id_hotel']));
|
||||
@@ -365,7 +365,7 @@ class DashGuestCycle extends Module
|
||||
'class' => 'text-right',
|
||||
);
|
||||
$tr[] = array(
|
||||
'value' => $newBookingInfo['state_name'],
|
||||
'value' => '<span class="label color_field" style="background-color:'.$newBookingInfo['state_color'].'">'.$newBookingInfo['state_name'].'</span>',
|
||||
'class' => 'text-left',
|
||||
);
|
||||
|
||||
|
||||
@@ -111,3 +111,7 @@
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#dashguestcycle table tbody>tr>td {
|
||||
padding: 8px 7px;
|
||||
}
|
||||
@@ -51,7 +51,7 @@
|
||||
</li>
|
||||
<li>
|
||||
<a href="#dgc_current_new_bookings" data-toggle="tab">
|
||||
<span>{l s='Bookings' mod='dashguestcycle'}</span>
|
||||
<span>{l s='New Bookings' mod='dashguestcycle'}</span>
|
||||
<span class="label label-info" id="dgc_count_new_bookings">0</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -54,6 +54,14 @@ class DashInsights extends Module
|
||||
public function hookActionAdminControllerSetMedia()
|
||||
{
|
||||
if (Tools::getValue('controller') == 'AdminDashboard') {
|
||||
Media::addJsDef(array(
|
||||
'hotel_txt' => $this->l('Hotel'),
|
||||
'room_occupied_txt' => $this->l('Nights Booked'),
|
||||
'length_of_stay_txt' => $this->l('Length Of Stay'),
|
||||
'total_nights_booked_txt' => $this->l('Total Nights Booked'),
|
||||
'date_txt' => $this->l('Date'),
|
||||
));
|
||||
|
||||
$this->context->controller->addCSS($this->_path.'views/css/'.$this->name.'.css');
|
||||
$this->context->controller->addJS($this->_path.'views/js/'.$this->name.'.js');
|
||||
}
|
||||
@@ -133,13 +141,13 @@ class DashInsights extends Module
|
||||
'chart_type' => 'multibar_chart_dotw_dashinsights',
|
||||
'data' => $daysOfTheWeekData,
|
||||
'date_format' => $this->context->language->date_format_lite,
|
||||
'axis_labels' => array('y' => $this->l('Rooms occupied')),
|
||||
),
|
||||
'dashinsights_multibar_chart2' => array(
|
||||
'chart_type' => 'multibar_chart_los_dashinsights',
|
||||
'chart_type' => 'multibar_chart_los_dashinsights',
|
||||
'data' => $lengthOfStayData,
|
||||
'date_format' => $this->context->language->date_format_lite,
|
||||
'axis_labels' => array('x' => $this->l('Days')),
|
||||
'y_format' => '%',
|
||||
'axis_labels' => array('x' => $this->l('Length of stay'), 'y' => $this->l('Rooms occupied')),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -336,7 +344,7 @@ class DashInsights extends Module
|
||||
$averageDaysOfTheWeekData = AdminStatsController::getOccupiedRoomsForDaysOfTheWeek($dateFrom, $dateTo, $idsHotel, true, true);
|
||||
$averageSeriesInfo = array(
|
||||
'data' => $averageDaysOfTheWeekData,
|
||||
'label' => $this->l('Others Average'),
|
||||
'label' => $this->l('Other Hotels Average'),
|
||||
);
|
||||
|
||||
$seriesWiseDaysOfTheWeek[] = $averageSeriesInfo;
|
||||
@@ -348,21 +356,25 @@ class DashInsights extends Module
|
||||
$daysOfTheWeekFormattedData = array();
|
||||
$colorIndex = 0;
|
||||
$weekDays = array(
|
||||
$this->l('SUN'),
|
||||
$this->l('MON'),
|
||||
$this->l('TUE'),
|
||||
$this->l('WED'),
|
||||
$this->l('THU'),
|
||||
$this->l('FRI'),
|
||||
$this->l('SAT'),
|
||||
array('key' => $this->l('Sun'), 'name' => $this->l('Sunday')),
|
||||
array('key' => $this->l('Mon'), 'name' => $this->l('Monday')),
|
||||
array('key' => $this->l('Tue'), 'name' => $this->l('Tuesday')),
|
||||
array('key' => $this->l('Wed'), 'name' => $this->l('Wednesday')),
|
||||
array('key' => $this->l('Thu'), 'name' => $this->l('Thrusday')),
|
||||
array('key' => $this->l('Fri'), 'name' => $this->l('Friday')),
|
||||
array('key' => $this->l('Sat'), 'name' => $this->l('Saturday'))
|
||||
);
|
||||
|
||||
|
||||
foreach ($seriesWiseDaysOfTheWeek as $key => &$hotelDaysOfTheWeek) {
|
||||
$hotelData = array();
|
||||
$totalBookedRooms = array_sum(array_values($hotelDaysOfTheWeek['data']));
|
||||
foreach ($hotelDaysOfTheWeek['data'] as $dayOfWeek => $hotelDayOfTheWeek) {
|
||||
$hotelData[] = array(
|
||||
'x' => $weekDays[$dayOfWeek - 1],
|
||||
'day' => $weekDays[$dayOfWeek - 1]['name'],
|
||||
'x' => $weekDays[$dayOfWeek - 1]['key'],
|
||||
'y' => $hotelDayOfTheWeek,
|
||||
'percent' => $hotelDayOfTheWeek ? (Tools::ps_round($hotelDayOfTheWeek / $totalBookedRooms * 100, 2)) : 0,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -382,13 +394,13 @@ class DashInsights extends Module
|
||||
|
||||
// day ranges to get length of stay data
|
||||
$day = array(
|
||||
1 => array(1, 1),
|
||||
2 => array(2, 2),
|
||||
3 => array(3, 3),
|
||||
4 => array(4, 4),
|
||||
5 => array(5, 5),
|
||||
6 => array(6, 6),
|
||||
7 => array(7, 100),
|
||||
6 => array(6, 6),
|
||||
5 => array(5, 5),
|
||||
4 => array(4, 4),
|
||||
3 => array(3, 3),
|
||||
2 => array(2, 2),
|
||||
1 => array(1, 1),
|
||||
);
|
||||
|
||||
if (Configuration::get('PS_DASHBOARD_SIMULATION')) {
|
||||
@@ -399,33 +411,66 @@ class DashInsights extends Module
|
||||
'label' => $this->l('All Hotels'),
|
||||
);
|
||||
|
||||
for ($i = 1; $i <= 7; $i++) {
|
||||
$allHotelSeriesInfo['data'][$i] = sprintf('%0.2f', rand(1, 100) / 100);
|
||||
$roomsOccupied = array(
|
||||
7 => rand(1, 100),
|
||||
6 => rand(1, 100),
|
||||
5 => rand(1, 100),
|
||||
4 => rand(1, 100),
|
||||
3 => rand(1, 100),
|
||||
2 => rand(1, 100),
|
||||
1 => rand(1, 100),
|
||||
);
|
||||
$totalOccupiedRooms = array_sum($roomsOccupied);
|
||||
foreach ($roomsOccupied as $key => $value) {
|
||||
$allHotelSeriesInfo['data'][$key]['rooms_occupied'] = $value;
|
||||
$allHotelSeriesInfo['data'][$key]['percent'] = Tools::ps_round(($value / $totalOccupiedRooms * 100), 2);
|
||||
}
|
||||
|
||||
$seriesWiseLengthOfStay[] = $allHotelSeriesInfo;
|
||||
} else { // if one of the hotels is selected
|
||||
$hotelLengthOfStay = array();
|
||||
$otherHotelsLengthOfStay = array();
|
||||
// calculation for currently selected hotel
|
||||
$roomsOccupied = array(
|
||||
7 => rand(1, 100),
|
||||
6 => rand(1, 100),
|
||||
5 => rand(1, 100),
|
||||
4 => rand(1, 100),
|
||||
3 => rand(1, 100),
|
||||
2 => rand(1, 100),
|
||||
1 => rand(1, 100),
|
||||
);
|
||||
|
||||
// current hotel series info
|
||||
$objHotelBranchInformation = new HotelBranchInformation($idHotel, $this->context->language->id);
|
||||
$totalOccupiedRooms = array_sum($roomsOccupied);
|
||||
$currentHotelLengthOfStayData = array();
|
||||
for ($i = 1; $i <= 7; $i++) {
|
||||
$currentHotelLengthOfStayData[$i] = sprintf('%0.2f', rand(1, 100) / 100);
|
||||
foreach ($roomsOccupied as $key => $value) {
|
||||
$currentHotelLengthOfStayData[$key]['rooms_occupied'] = $value;
|
||||
$currentHotelLengthOfStayData[$key]['percent'] = Tools::ps_round(($value / $totalOccupiedRooms * 100), 2);
|
||||
}
|
||||
|
||||
$objHotelBranchInformation = new HotelBranchInformation($idHotel, $this->context->language->id);
|
||||
$currentHotelSeriesInfo = array(
|
||||
'data' => $currentHotelLengthOfStayData,
|
||||
'label' => $objHotelBranchInformation->hotel_name,
|
||||
);
|
||||
|
||||
// average series info
|
||||
// calculation for other hotels average series info
|
||||
$roomsOccupied = array(
|
||||
7 => rand(1, 100),
|
||||
6 => rand(1, 100),
|
||||
5 => rand(1, 100),
|
||||
4 => rand(1, 100),
|
||||
3 => rand(1, 100),
|
||||
2 => rand(1, 100),
|
||||
1 => rand(1, 100),
|
||||
);
|
||||
|
||||
$totalOccupiedRooms = array_sum($roomsOccupied);
|
||||
$averageLengthOfStayData = array();
|
||||
for ($i = 1; $i <= 7; $i++) {
|
||||
$averageLengthOfStayData[$i] = sprintf('%0.2f', rand(1, 100) / 100);
|
||||
foreach ($roomsOccupied as $key => $value) {
|
||||
$averageLengthOfStayData[$key]['rooms_occupied'] = $value;
|
||||
$averageLengthOfStayData[$key]['percent'] = Tools::ps_round(($value / $totalOccupiedRooms * 100), 2);
|
||||
}
|
||||
|
||||
$objHotelBranchInformation = new HotelBranchInformation($idHotel, $this->context->language->id);
|
||||
$averageSeriesInfo = array(
|
||||
'data' => $averageLengthOfStayData,
|
||||
'label' => $this->l('Others Average'),
|
||||
@@ -440,17 +485,16 @@ class DashInsights extends Module
|
||||
if ($idHotel == 0) { // if 'All Hotels' is selected
|
||||
$idsHotel = $this->accessibleIdsHotel;
|
||||
$allHotelSeriesInfo = array(
|
||||
'data' => AdminStatsController::getLengthOfStayPercentages($day, $dateFrom, $dateTo, $idsHotel),
|
||||
'data' => AdminStatsController::getLengthOfStayInfo($day, $dateFrom, $dateTo, $idsHotel),
|
||||
'label' => $this->l('All Hotels'),
|
||||
);
|
||||
|
||||
$seriesWiseLengthOfStay[] = $allHotelSeriesInfo;
|
||||
} else { // if one of the hotels is selected
|
||||
$hotelLengthOfStay = array();
|
||||
$otherHotelsLengthOfStay = array();
|
||||
|
||||
$objHotelBranchInformation = new HotelBranchInformation($idHotel, $this->context->language->id);
|
||||
$currentHotelLengthOfStayData = AdminStatsController::getLengthOfStayPercentages($day, $dateFrom, $dateTo, $idHotel);
|
||||
$currentHotelLengthOfStayData = AdminStatsController::getLengthOfStayInfo($day, $dateFrom, $dateTo, $idHotel);
|
||||
|
||||
$currentHotelSeriesInfo = array(
|
||||
'data' => $currentHotelLengthOfStayData,
|
||||
@@ -466,7 +510,7 @@ class DashInsights extends Module
|
||||
}
|
||||
|
||||
if (count($idsHotel) > 0) { // display average series only if other hotels are available
|
||||
$averageLengthOfStayData = AdminStatsController::getLengthOfStayPercentages($day, $dateFrom, $dateTo, $idsHotel, true, true);
|
||||
$averageLengthOfStayData = AdminStatsController::getLengthOfStayInfo($day, $dateFrom, $dateTo, $idsHotel, true, true);
|
||||
$averageSeriesInfo = array(
|
||||
'data' => $averageLengthOfStayData,
|
||||
'label' => $this->l('Others Average'),
|
||||
@@ -492,10 +536,12 @@ class DashInsights extends Module
|
||||
|
||||
foreach ($seriesWiseLengthOfStay as $idHotel => &$hotelLengthOfStay) {
|
||||
$hotelData = array();
|
||||
foreach ($hotelLengthOfStay['data'] as $numDays => $lengthOfStay) {
|
||||
foreach ($hotelLengthOfStay['data'] as $numDays => $lengthOfStayInfo) {
|
||||
$hotelData[] = array(
|
||||
'x' => $losInfos[$numDays - 1]['label'],
|
||||
'y' => $lengthOfStay,
|
||||
'percent' => Tools::ps_round($lengthOfStayInfo['percent'], 2),
|
||||
'y' => $lengthOfStayInfo['rooms_occupied'],
|
||||
'rooms_occupied' => $lengthOfStayInfo['rooms_occupied'],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,19 +24,64 @@ function line_chart_dashinsights(widget_name, chart_details) {
|
||||
.useInteractiveGuideline(true)
|
||||
.x(function (d) { return (d !== undefined ? d[0] : 0); })
|
||||
.y(function (d) { return (d !== undefined ? d[1] : 0); })
|
||||
.forceY(0)
|
||||
.forceY([0, 1])
|
||||
.margin({
|
||||
left: 40,
|
||||
right: 30,
|
||||
});
|
||||
|
||||
chart.yAxis.scale().domain([0, 1]);
|
||||
// create content for the tooltip of chart
|
||||
chart.interactiveLayer.tooltip.contentGenerator((obj, element) => {
|
||||
var totalNightsBooked = 0;
|
||||
var tooltipContent = '';
|
||||
|
||||
var date = new Date(obj.value * 1000);
|
||||
date = date.format(chart_details['date_format']);
|
||||
var tooltipLabel = date_txt + ': ' + date;
|
||||
|
||||
var hotelsContent = '';
|
||||
$.each( obj.series, function(index, hotelData) {
|
||||
totalNightsBooked = totalNightsBooked + parseInt(hotelData.value);
|
||||
|
||||
hotelsContent += '<p>';
|
||||
hotelsContent += '<span class="tooltip-data-color" style="background:' + hotelData.color + '">' + '</span>';
|
||||
hotelsContent += hotelData.key + ': <b>' + hotelData.value + '</b>';
|
||||
hotelsContent += '</p>';
|
||||
});
|
||||
|
||||
tooltipContent += '<p>' + total_nights_booked_txt + ': <b>' + totalNightsBooked + '</b></p>';
|
||||
tooltipContent += hotelsContent;
|
||||
|
||||
return getTooltipContent(tooltipLabel, tooltipContent);
|
||||
});
|
||||
|
||||
// format date values
|
||||
chart.xAxis.tickFormat(function (d) {
|
||||
date = new Date(d * 1000);
|
||||
return date.format(chart_details['date_format']);
|
||||
})
|
||||
// through this function we are also fixing the x axis date and values alignment issue
|
||||
.tickValues(function(values) {
|
||||
var indexInterval = (values[0].values.length / 4);
|
||||
var dates = [];
|
||||
for (var i = 0; i < 4; i++) {
|
||||
let dateIndex = Math.round(indexInterval) * i;
|
||||
if (values[0].values[dateIndex]) {
|
||||
dates.push(values[0].values[dateIndex]);
|
||||
}
|
||||
}
|
||||
var dates = dates.map(function(v) {
|
||||
return v[0]
|
||||
});
|
||||
|
||||
return dates;
|
||||
});
|
||||
|
||||
chart.yAxis.tickFormat(d3.format('.f'));
|
||||
// Y axis values must be integer only
|
||||
chart.yAxis.tickFormat(d3.format('d'));
|
||||
|
||||
// initialize chart
|
||||
d3.select('#dashinsights_room_nights svg')
|
||||
.datum(chart_details.data)
|
||||
.call(chart);
|
||||
@@ -51,16 +96,31 @@ function multibar_chart_dotw_dashinsights(widget_name, chart_details) {
|
||||
nv.addGraph(function() {
|
||||
var chart = nv.models.multiBarHorizontalChart()
|
||||
.showControls(false)
|
||||
.forceY([0, 1])
|
||||
.margin({
|
||||
left: 40,
|
||||
right: 30,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// create content for the tooltip of chart
|
||||
chart.tooltip.contentGenerator((obj, element) => {
|
||||
var tooltipContent = '<p>' + hotel_txt + ': <b>' + obj.data.key + '</b></p>';
|
||||
tooltipContent += '<p>' + room_occupied_txt + ': <b>' + obj.data.y + '</b> (' + obj.data.percent + '%)</p>';
|
||||
|
||||
return getTooltipContent(obj.data.day, tooltipContent, obj.color);
|
||||
});
|
||||
|
||||
chart.xAxis.tickFormat(function (d) {
|
||||
return d;
|
||||
});
|
||||
|
||||
chart.yAxis.tickFormat(d3.format('.f'));
|
||||
chart.yAxis.axisLabel(chart_details.axis_labels.y)
|
||||
.tickFormat(function (d) {
|
||||
return d;
|
||||
});
|
||||
// Y axis values must be integer only
|
||||
chart.yAxis.tickFormat(d3.format('d'));
|
||||
|
||||
d3.select('#dashinsights_days_of_the_week svg')
|
||||
.datum(chart_details.data)
|
||||
@@ -77,6 +137,7 @@ function multibar_chart_los_dashinsights(widget_name, chart_details) {
|
||||
nv.addGraph(function() {
|
||||
var chart = nv.models.multiBarHorizontalChart()
|
||||
.showControls(false)
|
||||
.forceY([0, 1])
|
||||
.margin({
|
||||
left: 40,
|
||||
right: 30,
|
||||
@@ -86,9 +147,26 @@ function multibar_chart_los_dashinsights(widget_name, chart_details) {
|
||||
.axisLabel(chart_details.axis_labels.x)
|
||||
.tickFormat(function (d) {
|
||||
return d;
|
||||
});
|
||||
}
|
||||
);
|
||||
chart.yAxis
|
||||
.axisLabel(chart_details.axis_labels.y)
|
||||
.tickFormat(function (d) {
|
||||
return d;
|
||||
}
|
||||
);
|
||||
// Y axis values must be integer only
|
||||
chart.yAxis.tickFormat(d3.format('d'));
|
||||
|
||||
chart.yAxis.tickFormat(d3.format(chart_details['y_format']));
|
||||
// create content for the tooltip of chart
|
||||
chart.tooltip.contentGenerator((obj, element) => {
|
||||
var tooltipLabel = length_of_stay_txt + ': ' + obj.data.x;
|
||||
|
||||
var tooltipContent = '<p>' + hotel_txt + ': <b>' + obj.data.key + '</b></p>';
|
||||
tooltipContent += '<p>' + room_occupied_txt + ': <b>' + obj.data.rooms_occupied + '</b> (' + obj.data.percent + '%)</p>';
|
||||
|
||||
return getTooltipContent(tooltipLabel, tooltipContent, obj.color);
|
||||
});
|
||||
|
||||
chart.dispatch.stateChange = function () {
|
||||
setTimeout(function () {
|
||||
@@ -111,9 +189,9 @@ function multibar_chart_los_dashinsights(widget_name, chart_details) {
|
||||
});
|
||||
|
||||
function positionXAxisLabel() {
|
||||
d3.select('#dashinsights_length_of_stay .nv-x .nv-axislabel').attr(
|
||||
'transform',
|
||||
d3.transform('rotate(-90) translate(-130, 40)').toString()
|
||||
);
|
||||
d3.select('#dashinsights_length_of_stay .nv-x .nv-axislabel')
|
||||
.attr('transform', d3.transform('rotate(-90) translate(-130, 40)').toString())
|
||||
.attr('y', '-70')
|
||||
.attr('x', '0');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,11 +74,7 @@ class DashOccupancy extends Module
|
||||
$tmp = $tmp - $occupancyData['count_available'];
|
||||
$occupancyData['count_unavailable'] = sprintf('%02d', $tmp);
|
||||
} else {
|
||||
$occupancyData = AdminStatsController::getOccupancyData(
|
||||
$params['date_from'],
|
||||
$params['date_to'],
|
||||
$params['id_hotel']
|
||||
);
|
||||
$occupancyData = AdminStatsController::getOccupancyData($params['date_from'], $params['date_to'], $params['id_hotel']);
|
||||
}
|
||||
|
||||
$dataValue = array(
|
||||
@@ -91,22 +87,25 @@ class DashOccupancy extends Module
|
||||
if ($occupancyData['count_total']) {
|
||||
$dataPieChartBig = array(
|
||||
array(
|
||||
'label' => $this->l('Occupied'),
|
||||
'value' => $occupancyData['count_total']
|
||||
'label' => $this->l('Occupied Rooms'),
|
||||
'value' => $occupancyData['count_occupied'],
|
||||
'percent' => Tools::ps_round(($occupancyData['count_total']
|
||||
? ($occupancyData['count_occupied'] / $occupancyData['count_total']) * 100
|
||||
: 0,
|
||||
: 0), 2),
|
||||
),
|
||||
array(
|
||||
'label' => $this->l('Available'),
|
||||
'value' => $occupancyData['count_total']
|
||||
'label' => $this->l('Available Rooms'),
|
||||
'value' => $occupancyData['count_available'],
|
||||
'percent' => Tools::ps_round(($occupancyData['count_total']
|
||||
? ($occupancyData['count_available'] / $occupancyData['count_total']) * 100
|
||||
: 0,
|
||||
: 0), 2),
|
||||
),
|
||||
array(
|
||||
'label' => $this->l('Unavailable'),
|
||||
'value' => $occupancyData['count_total']
|
||||
'label' => $this->l('Unavailable Rooms'),
|
||||
'value' => $occupancyData['count_unavailable'],
|
||||
'percent' => Tools::ps_round(($occupancyData['count_total']
|
||||
? ($occupancyData['count_unavailable'] / $occupancyData['count_total']) * 100
|
||||
: 0,
|
||||
: 0), 2),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -69,6 +69,10 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nv-label text {
|
||||
fill: #fff !important;
|
||||
#dashoccupancy .label-tooltip {
|
||||
color: #00aff0;
|
||||
cursor: pointer;
|
||||
}
|
||||
#dashoccupancy .tooltip {
|
||||
text-transform: none;
|
||||
}
|
||||
@@ -31,6 +31,12 @@ function pie_chart_occupancy(widget_name, chart_details) {
|
||||
.donutRatio(0.35)
|
||||
.color(['#A569DF', '#56CE56', '#FF655C']);
|
||||
|
||||
// create content for the tooltip of chart
|
||||
chart.tooltip.contentGenerator((obj, element) => {
|
||||
var tooltipContent = '<p><b>' + obj.data.value + '</b> (' + obj.data.percent + '%)<p>';
|
||||
return getTooltipContent(obj.data.label, tooltipContent, obj.color);
|
||||
});
|
||||
|
||||
d3.select('#availablePieChart svg')
|
||||
.datum(chart_details.data)
|
||||
.transition().duration(350)
|
||||
@@ -46,6 +52,10 @@ $(document).ready(function() {
|
||||
start = Date.parseDate($('#date-start').val(), 'Y-m-d');
|
||||
end = Date.parseDate($('#date-end').val(), 'Y-m-d');
|
||||
|
||||
if (end.getDate() == start.getDate()) {
|
||||
end.setDate(end.getDate() + 1);
|
||||
}
|
||||
|
||||
$('#dashoccupancy_date_range').html(sprintf(date_subtitle, start.format(date_format), end.format(date_format)));
|
||||
});
|
||||
|
||||
@@ -53,6 +63,10 @@ $(document).ready(function() {
|
||||
start = Date.parseDate($('#date-start').val(), 'Y-m-d');
|
||||
end = Date.parseDate($('#date-end').val(), 'Y-m-d');
|
||||
|
||||
if (end.getDate() == start.getDate()) {
|
||||
end.setDate(end.getDate() + 1);
|
||||
}
|
||||
|
||||
$('#dashoccupancy_date_range').html(sprintf(date_subtitle, start.format(date_format), end.format(date_format)));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,10 +20,8 @@
|
||||
<section id="dashoccupancy" class="panel widget allow_push">
|
||||
<header class="panel-heading">
|
||||
<i class="icon-bar-chart"></i>
|
||||
<span>
|
||||
{l s='Occupancy' mod='dashoccupancy'}
|
||||
<small class='text-muted' id='dashoccupancy_date_range'></small>
|
||||
</span>
|
||||
<span>{l s='Occupancy' mod='dashoccupancy'} <small class='text-muted' id='dashoccupancy_date_range'></small></span>
|
||||
<i class="icon-info-circle label-tooltip" data-toggle="tooltip" data-original-title="{l s='Occupancy information will be displayed by considering the selected date range as Check-in and Check-out dates.' mod='dashoccupancy'}"></i>
|
||||
<span class="panel-heading-action">
|
||||
<a class="list-toolbar-btn" href="javascript:void(0);" title="Refresh" onclick="refreshDashboard('dashoccupancy'); return false;">
|
||||
<i class="process-icon-refresh"></i>
|
||||
|
||||
@@ -79,41 +79,48 @@ class DashPerformance extends Module
|
||||
$params['date_to'],
|
||||
$params['id_hotel']
|
||||
));
|
||||
$data['dp_total_revenue_per_available_room'] = Tools::displayPrice(AdminStatsController::getTotalRevenuePerAvailableRoom(
|
||||
|
||||
$data['dp_direct_revenue_ratio'] = sprintf('%0.2f', AdminStatsController::getDirectRevenueRatio(
|
||||
$params['date_from'],
|
||||
$params['date_to'],
|
||||
$params['id_hotel']
|
||||
));
|
||||
)).'%';
|
||||
|
||||
$data['dp_average_occupancy_rate'] = sprintf('%0.2f', AdminStatsController::getAverageOccupancyRate(
|
||||
$params['date_from'],
|
||||
$params['date_to'],
|
||||
$params['id_hotel']
|
||||
) * 100).'%';
|
||||
)).'%';
|
||||
|
||||
$data['dp_cancellation_rate'] = sprintf('%0.2f', AdminStatsController::getCancellationRate(
|
||||
$params['date_from'],
|
||||
$params['date_to'],
|
||||
$params['id_hotel']
|
||||
)).'%';
|
||||
|
||||
$data['dp_revenue_per_available_room'] = Tools::displayPrice(AdminStatsController::getRevenuePerAvailableRoom(
|
||||
$params['date_from'],
|
||||
$params['date_to'],
|
||||
$params['id_hotel']
|
||||
));
|
||||
|
||||
$data['dp_total_revenue_per_available_room'] = Tools::displayPrice(AdminStatsController::getTotalRevenuePerAvailableRoom(
|
||||
$params['date_from'],
|
||||
$params['date_to'],
|
||||
$params['id_hotel']
|
||||
));
|
||||
|
||||
$data['dp_gross_operating_profit_par'] = Tools::displayPrice(AdminStatsController::getGrossOperatingProfitPerAvailableRoom(
|
||||
$params['date_from'],
|
||||
$params['date_to'],
|
||||
$params['id_hotel']
|
||||
));
|
||||
|
||||
$data['dp_average_length_of_stay'] = sprintf('%0.2f', AdminStatsController::getAverageLengthOfStay(
|
||||
$params['date_from'],
|
||||
$params['date_to'],
|
||||
$params['id_hotel']
|
||||
));
|
||||
$data['dp_direct_revenue_ratio'] = sprintf('%0.2f', AdminStatsController::getDirectRevenueRatio(
|
||||
$params['date_from'],
|
||||
$params['date_to'],
|
||||
$params['id_hotel']
|
||||
) * 100).'%';
|
||||
$data['dp_cancellation_rate'] = sprintf('%0.2f', AdminStatsController::getCancellationRate(
|
||||
$params['date_from'],
|
||||
$params['date_to'],
|
||||
$params['id_hotel']
|
||||
) * 100).'%';
|
||||
}
|
||||
|
||||
return array('data_value' => $data);
|
||||
|
||||
@@ -39,27 +39,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6 col-lg-3">
|
||||
<div class="stat-box label-tooltip" data-toggle="tooltip" data-original-title="{l s='Direct Revenue Ratio (DRR) measures the percentage of online revenue that comes directly from your website vs. third party channels.' mod='dashperformance'}" data-placement="top" style="background-color: #EBCDFF;">
|
||||
<div class="title-wrapper">
|
||||
<p>{l s='Direct Revenue Ratio' mod='dashperformance'}</p>
|
||||
</div>
|
||||
<div class="value-wrapper">
|
||||
<span id="dp_direct_revenue_ratio" style="color: #FF4036;">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6 col-lg-3">
|
||||
<div class="stat-box label-tooltip" data-toggle="tooltip" data-original-title="{l s='Total Revenue Per Available Room (TrevPAR) measures the total revenue being generated per available room including additional facilities and service products.' mod='dashperformance'}" data-placement="top" style="background-color: #B6FFB6;">
|
||||
<div class="title-wrapper">
|
||||
<p>{l s='Total Revenue Per Available Room' mod='dashperformance'}</p>
|
||||
</div>
|
||||
<div class="value-wrapper">
|
||||
<span id="dp_total_revenue_per_available_room" style="color: #00B200;">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6 col-lg-3">
|
||||
<div class="col-xs-6 col-lg-3">
|
||||
<div class="stat-box label-tooltip" data-toggle="tooltip" data-original-title="{l s='Average Occupancy Rate (AOR) is the average percentage of rooms booked out over a given time period.' mod='dashperformance'}" data-placement="top" style="background-color: #FFE5B4;">
|
||||
<div class="title-wrapper">
|
||||
<p>{l s='Average Occupancy Rate' mod='dashperformance'}</p>
|
||||
@@ -69,7 +49,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6 col-lg-3">
|
||||
<div class="col-xs-6 col-lg-3">
|
||||
<div class="stat-box label-tooltip" data-toggle="tooltip" data-original-title="{l s='Direct Revenue Ratio (DRR) measures the percentage of online revenue that comes directly from your website vs. third party channels.' mod='dashperformance'}" data-placement="top" style="background-color: #B6FFB6;">
|
||||
<div class="title-wrapper">
|
||||
<p>{l s='Direct Revenue Ratio' mod='dashperformance'}</p>
|
||||
</div>
|
||||
<div class="value-wrapper">
|
||||
<span id="dp_direct_revenue_ratio" style="color: #00B200;">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6 col-lg-3">
|
||||
<div class="stat-box label-tooltip" data-toggle="tooltip" data-original-title="{l s='Cancellation Rate (CR) is the percentage of all cancelled orders out of all orders over a given time period.' mod='dashperformance'}" data-placement="top" style="background-color: #FFBBB8;">
|
||||
<div class="title-wrapper">
|
||||
<p>{l s='Cancellation Rate' mod='dashperformance'}</p>
|
||||
</div>
|
||||
<div class="value-wrapper">
|
||||
<span id="dp_cancellation_rate" style="color: #FF4036;">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6 col-lg-3">
|
||||
<div class="stat-box label-tooltip" data-toggle="tooltip" data-original-title="{l s='Revenue Per Available Room (RevPAR) measures how many rooms are being booked, and how much revenue those bookings are generating.' mod='dashperformance'}" data-placement="top" style="background-color: #B6FFB6;">
|
||||
<div class="title-wrapper">
|
||||
<p>{l s='Revenue Per Available Room' mod='dashperformance'}</p>
|
||||
@@ -80,32 +80,32 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6 col-lg-3">
|
||||
<div class="stat-box label-tooltip" data-toggle="tooltip" data-original-title="{l s='Gross Operating Profit Per Available Room (GOPPAR) measures how much gross operating profit comes from each room.' mod='dashperformance'}" data-placement="top" style="background-color: #FFE5B4;">
|
||||
<div class="stat-box label-tooltip" data-toggle="tooltip" data-original-title="{l s='Total Revenue Per Available Room (TrevPAR) measures the total revenue being generated per available room including additional facilities and service products.' mod='dashperformance'}" data-placement="top" style="background-color: #EBCDFF;">
|
||||
<div class="title-wrapper">
|
||||
<p>{l s='Total Revenue Per Available Room' mod='dashperformance'}</p>
|
||||
</div>
|
||||
<div class="value-wrapper">
|
||||
<span id="dp_total_revenue_per_available_room" style="color: #FF4036;">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6 col-lg-3">
|
||||
<div class="stat-box label-tooltip" data-toggle="tooltip" data-original-title="{l s='Gross Operating Profit Per Available Room (GOPPAR) measures how much gross operating profit comes from each room.' mod='dashperformance'}" data-placement="top" style="background-color: #B7F0FF;">
|
||||
<div class="title-wrapper">
|
||||
<p>{l s='Gross Operating Profit Per Available Room' mod='dashperformance'}</p>
|
||||
</div>
|
||||
<div class="value-wrapper">
|
||||
<span id="dp_gross_operating_profit_par" style="color: #E09400;">--</span>
|
||||
<span id="dp_gross_operating_profit_par" style="color: #0093BA;">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6 col-lg-3">
|
||||
<div class="stat-box label-tooltip" data-toggle="tooltip" data-original-title="{l s='Average Length of Stay (ALOS) is the average amount of days guests stay at the hotel over a given time period.' mod='dashperformance'}" data-placement="top" style="background-color: #B7F0FF;">
|
||||
<div class="stat-box label-tooltip" data-toggle="tooltip" data-original-title="{l s='Average Length of Stay (ALOS) is the average amount of days guests stay at the hotel over a given time period.' mod='dashperformance'}" data-placement="top" style="background-color: #FFE5B4;">
|
||||
<div class="title-wrapper">
|
||||
<p>{l s='Average Length of Stay' mod='dashperformance'}</p>
|
||||
</div>
|
||||
<div class="value-wrapper">
|
||||
<span id="dp_average_length_of_stay" style="color: #0093BA;">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-6 col-lg-3">
|
||||
<div class="stat-box label-tooltip" data-toggle="tooltip" data-original-title="{l s='Cancellation Rate (CR) is the percentage of all cancelled orders out of all orders over a given time period.' mod='dashperformance'}" data-placement="top" style="background-color: #FFBBB8;">
|
||||
<div class="title-wrapper">
|
||||
<p>{l s='Cancellation Rate' mod='dashperformance'}</p>
|
||||
</div>
|
||||
<div class="value-wrapper">
|
||||
<span id="dp_cancellation_rate" style="color: #FF4036;">--</span>
|
||||
<span id="dp_average_length_of_stay" style="color: #E09400;">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -87,6 +87,38 @@ class DashProducts extends Module
|
||||
return $this->display(__FILE__, 'dashboard_zone_two.tpl');
|
||||
}
|
||||
|
||||
// Validation of the configuration form
|
||||
public function validateDashConfig($configs)
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
if (!Validate::isUnsignedInt($configs['DASHPRODUCT_NBR_SHOW_LAST_ORDER'])
|
||||
|| !$configs['DASHPRODUCT_NBR_SHOW_LAST_ORDER']
|
||||
) {
|
||||
$errors[] = $this->l('Number of recent bookings must be a positive integer.');
|
||||
}
|
||||
|
||||
if (!Validate::isUnsignedInt($configs['DASHPRODUCT_NBR_SHOW_BEST_SELLER'])
|
||||
|| !$configs['DASHPRODUCT_NBR_SHOW_BEST_SELLER']
|
||||
) {
|
||||
$errors[] = $this->l('Number of best selling must be a positive integer.');
|
||||
}
|
||||
|
||||
if (!Validate::isUnsignedInt($configs['DASHPRODUCT_NBR_SHOW_MOST_VIEWED'])
|
||||
|| !$configs['DASHPRODUCT_NBR_SHOW_MOST_VIEWED']
|
||||
) {
|
||||
$errors[] = $this->l('Number of most viewed must be a positive integer.');
|
||||
}
|
||||
|
||||
if (!Validate::isUnsignedInt($configs['DASHPRODUCT_NBR_SHOW_TOP_SEARCH'])
|
||||
|| !$configs['DASHPRODUCT_NBR_SHOW_TOP_SEARCH']
|
||||
) {
|
||||
$errors[] = $this->l('Number of top searches must be a positive integer.');
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
public function hookDashboardData($params)
|
||||
{
|
||||
$table_recent_orders = $this->getTableRecentOrders($params['id_hotel']);
|
||||
@@ -169,7 +201,7 @@ class DashProducts extends Module
|
||||
);
|
||||
$tr[] = array(
|
||||
'id' => 'status',
|
||||
'value' => Tools::htmlentitiesUTF8($order['state_name']),
|
||||
'value' => '<span class="label color_field" style="background-color:'.$order['state_color'].'">'.Tools::htmlentitiesUTF8($order['state_name']).'</span>',
|
||||
'class' => 'text-left',
|
||||
);
|
||||
$tr[] = array(
|
||||
@@ -331,7 +363,7 @@ class DashProducts extends Module
|
||||
);
|
||||
|
||||
if (Configuration::get('PS_STATSDATA_PAGESVIEWS')) {
|
||||
$products = $this->getTotalViewed($date_from, $date_to, (int)Configuration::get('DASHPRODUCT_NBR_SHOW_MOST_VIEWED'));
|
||||
$products = $this->getTotalViewed($date_from, $date_to, (int)Configuration::get('DASHPRODUCT_NBR_SHOW_MOST_VIEWED'), $id_hotel);
|
||||
$body = array();
|
||||
if (is_array($products) && count($products)) {
|
||||
foreach ($products as $product) {
|
||||
@@ -406,16 +438,16 @@ class DashProducts extends Module
|
||||
public function getTableTop10MostSearch($date_from, $date_to)
|
||||
{
|
||||
$header = array(
|
||||
array(
|
||||
'id' => 'image',
|
||||
'title' => $this->l('Image'),
|
||||
'class' => 'text-center'
|
||||
),
|
||||
array(
|
||||
'id' => 'reference',
|
||||
'title' => $this->l('Hotel'),
|
||||
'class' => 'text-center'
|
||||
),
|
||||
array(
|
||||
'id' => 'image',
|
||||
'title' => $this->l('Cover image'),
|
||||
'class' => 'text-center'
|
||||
),
|
||||
array(
|
||||
'id' => 'location',
|
||||
'title' => $this->l('Location'),
|
||||
@@ -523,9 +555,8 @@ class DashProducts extends Module
|
||||
{
|
||||
return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue('
|
||||
SELECT count(`id_product`) as count
|
||||
FROM `'._DB_PREFIX_.'cart_product` cp
|
||||
FROM `'._DB_PREFIX_.'htl_cart_booking_data` cp
|
||||
WHERE cp.`id_product` = '.(int)$id_product.'
|
||||
'.Shop::addSqlRestriction(false, 'cp').'
|
||||
AND cp.`date_add` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59"');
|
||||
}
|
||||
|
||||
@@ -539,7 +570,7 @@ class DashProducts extends Module
|
||||
);
|
||||
}
|
||||
|
||||
public function getTotalViewed($date_from, $date_to, $limit = 10)
|
||||
public function getTotalViewed($date_from, $date_to, $limit = 10, $id_hotel = 0)
|
||||
{
|
||||
$objGoogleAnalytics = Module::isEnabled('qlogoogleanalytics') ? Module::getInstanceByName('qlogoogleanalytics') : false;
|
||||
if (Validate::isLoadedObject($objGoogleAnalytics) && $objGoogleAnalytics->isConfigured()) {
|
||||
|
||||
@@ -74,8 +74,7 @@
|
||||
</div>
|
||||
<div class="tab-pane" id="dash_best_sellers">
|
||||
<h3>
|
||||
{l s="Top %d room types" sprintf=$DASHPRODUCT_NBR_SHOW_BEST_SELLER|intval mod="dashproducts"}
|
||||
<span>{l s="From" mod="dashproducts"} {$date_from} {l s="to" mod="dashproducts"} {$date_to}</span>
|
||||
{l s="Top %d room types" sprintf=$DASHPRODUCT_NBR_SHOW_BEST_SELLER|intval mod="dashproducts"} (<span>{l s="From" mod="dashproducts"} {$date_from} {l s="to" mod="dashproducts"} {$date_to}</span>)
|
||||
</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table data_table" id="table_best_sellers">
|
||||
@@ -86,8 +85,7 @@
|
||||
</div>
|
||||
<div class="tab-pane" id="dash_most_viewed">
|
||||
<h3>
|
||||
{l s="Most Viewed" mod="dashproducts"}
|
||||
<span>{l s="From" mod="dashproducts"} {$date_from} {l s="to" mod="dashproducts"} {$date_to}</span>
|
||||
{l s="Most viewed room types" mod="dashproducts"} (<span>{l s="From" mod="dashproducts"} {$date_from} {l s="to" mod="dashproducts"} {$date_to}</span>)
|
||||
</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table data_table" id="table_most_viewed">
|
||||
@@ -98,9 +96,9 @@
|
||||
</div>
|
||||
<div class="tab-pane" id="dash_top_search">
|
||||
<h3>
|
||||
{l s="Top %d most searched hotels" sprintf=$DASHPRODUCT_NBR_SHOW_TOP_SEARCH|intval mod="dashproducts"}
|
||||
<span>{l s="From" mod="dashproducts"} {$date_from} {l s="to" mod="dashproducts"} {$date_to}</span>
|
||||
{l s="Top %d most searched hotels" sprintf=$DASHPRODUCT_NBR_SHOW_TOP_SEARCH|intval mod="dashproducts"} (<span>{l s="From" mod="dashproducts"} {$date_from} {l s="to" mod="dashproducts"} {$date_to}</span>)
|
||||
</h3>
|
||||
<div class="alert alert-info">{l s="Top searched hotel list is independent of hotel selection at the top. It will always display top searched hotels among all hotels." mod="dashproducts"}</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table data_table" id="table_top_10_most_search">
|
||||
<thead></thead>
|
||||
@@ -112,4 +110,4 @@
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -65,6 +65,10 @@ class Dashtrends extends Module
|
||||
public function hookActionAdminControllerSetMedia()
|
||||
{
|
||||
if (get_class($this->context->controller) == 'AdminDashboardController') {
|
||||
Media::addJsDef(array(
|
||||
'date_txt' => $this->l('Date'),
|
||||
));
|
||||
|
||||
$this->context->controller->addJs($this->_path.'views/js/'.$this->name.'.js');
|
||||
$this->context->controller->addCSS($this->_path.'views/css/'.$this->name.'.css');
|
||||
}
|
||||
@@ -106,10 +110,10 @@ class Dashtrends extends Module
|
||||
}
|
||||
} else {
|
||||
$tmp_data['visits'] = AdminStatsController::getVisits(false, $date_from, $date_to, 'day');
|
||||
$tmp_data['orders'] = AdminStatsController::getOrders($date_from, $date_to, 'day', $params['id_hotel']);
|
||||
$tmp_data['total_paid_tax_excl'] = AdminStatsController::getTotalSales($date_from, $date_to, 'day', $params['id_hotel']);
|
||||
$tmp_data['total_purchases'] = AdminStatsController::getPurchases($date_from, $date_to, 'day', $params['id_hotel']);
|
||||
$tmp_data['total_expenses'] = AdminStatsController::getExpenses($date_from, $date_to, 'day', $params['id_hotel']);
|
||||
$tmp_data['orders'] = AdminStatsController::getOrders($date_from, $date_to, 'day', $id_hotel);
|
||||
$tmp_data['total_paid_tax_excl'] = AdminStatsController::getTotalSales($date_from, $date_to, 'day', $id_hotel);
|
||||
$tmp_data['total_purchases'] = AdminStatsController::getPurchases($date_from, $date_to, 'day', $id_hotel);
|
||||
$tmp_data['total_expenses'] = AdminStatsController::getExpenses($date_from, $date_to, 'day', $id_hotel);
|
||||
}
|
||||
|
||||
return $tmp_data;
|
||||
@@ -143,8 +147,7 @@ class Dashtrends extends Module
|
||||
$refined_data['orders'][$date] = isset($gross_data['orders'][$date]) ? $gross_data['orders'][$date] : 0;
|
||||
$refined_data['average_cart_value'][$date] = $refined_data['orders'][$date] ? $refined_data['sales'][$date] / $refined_data['orders'][$date] : 0;
|
||||
$refined_data['visits'][$date] = isset($gross_data['visits'][$date]) ? $gross_data['visits'][$date] : 0;
|
||||
$refined_data['conversion_rate'][$date] = $refined_data['visits'][$date] ? $refined_data['orders'][$date] / $refined_data['visits'][$date] : 0;
|
||||
|
||||
$refined_data['conversion_rate'][$date] = $refined_data['visits'][$date] ? ($refined_data['orders'][$date] / $refined_data['visits'][$date] * 100) : 0;
|
||||
$refined_data['net_profits'][$date] += (isset($gross_data['total_paid_tax_excl'][$date]) ? $gross_data['total_paid_tax_excl'][$date] : 0);
|
||||
$refined_data['net_profits'][$date] -= (isset($gross_data['total_purchases'][$date]) ? $gross_data['total_purchases'][$date] : 0);
|
||||
$refined_data['net_profits'][$date] -= (isset($gross_data['total_expenses'][$date]) ? $gross_data['total_expenses'][$date] : 0);
|
||||
@@ -311,7 +314,7 @@ class Dashtrends extends Module
|
||||
|
||||
$charts = array(
|
||||
'sales' => $this->l('Sales'),
|
||||
'orders' => $this->l('Bookings'),
|
||||
'orders' => $this->l('Orders'),
|
||||
'average_cart_value' => $this->l('Average Order Value'),
|
||||
'visits' => $this->l('Visits'),
|
||||
'conversion_rate' => $this->l('Conversion Rate'),
|
||||
|
||||
@@ -3,59 +3,89 @@ var dashtrends_chart;
|
||||
|
||||
function line_chart_trends(widget_name, chart_details)
|
||||
{
|
||||
if (chart_details.data[0].values.length <= 1) {
|
||||
// when there is no data available do not show the chart
|
||||
if (chart_details.data[0].values.length <= 1) {
|
||||
$('#dash_trends_chart1').hide();
|
||||
$('#no-chart-info').show();
|
||||
} else {
|
||||
$('#dash_trends_chart1').show();
|
||||
$('#no-chart-info').hide();
|
||||
}
|
||||
nv.addGraph(function() {
|
||||
var chart = nv.models.lineChart()
|
||||
.useInteractiveGuideline(true)
|
||||
.x(function(d) { return (d !== undefined ? d[0] : 0); })
|
||||
.y(function(d) { return (d !== undefined ? parseInt(d[1]) : 0); })
|
||||
.margin({
|
||||
left: 70,
|
||||
right: 30,
|
||||
});
|
||||
nv.addGraph(function() {
|
||||
var chart = nv.models.lineChart()
|
||||
.useInteractiveGuideline(true)
|
||||
.x(function(d) { return (d !== undefined ? d[0] : 0); })
|
||||
.y(function(d) { return (d !== undefined ? parseFloat(d[1]) : 0); })
|
||||
.forceY([0, 50])
|
||||
.margin({
|
||||
left: 70,
|
||||
right: 30,
|
||||
});
|
||||
|
||||
chart.xAxis.tickFormat(function(d) {
|
||||
date = new Date(d*1000);
|
||||
return date.format(chart_details['date_format']);
|
||||
});
|
||||
chart.xAxis
|
||||
.tickFormat(function(d) {
|
||||
date = new Date(d*1000);
|
||||
return date.format(chart_details['date_format']);
|
||||
})
|
||||
// through this function we are also fixing the x axis date and values alignment issue
|
||||
.tickValues(function(values) {
|
||||
var indexInterval = Math.floor(values[0].values.length / 10);
|
||||
|
||||
first_data = new Array();
|
||||
$.each(chart_details.data, function(index, value) {
|
||||
if (value.id == 'sales' || value.id == 'sales_compare')
|
||||
{
|
||||
if (value.id == 'sales'){
|
||||
$('#dashtrends_toolbar dl:first').addClass('active').css("border-color", value.border_color);
|
||||
}
|
||||
first_data.push(chart_details.data[index]);
|
||||
}
|
||||
});
|
||||
var dates = [];
|
||||
for (var i = 0; i < 10; i++) {
|
||||
if (values[0].values[(indexInterval+1) * i]) {
|
||||
dates.push(values[0].values[(indexInterval+1) * i]);
|
||||
}
|
||||
}
|
||||
var dates = dates.map(function(v) {
|
||||
return v[0]
|
||||
});
|
||||
|
||||
chart.yAxis.tickFormat(function(d) {
|
||||
return formatCurrency(parseFloat(d), currency_format, currency_sign, currency_blank);
|
||||
});
|
||||
return dates;
|
||||
});
|
||||
|
||||
dashtrends_data = chart_details.data;
|
||||
dashtrends_chart = chart;
|
||||
d3.select('#dash_trends_chart1 svg')
|
||||
.datum(first_data)
|
||||
.call(chart);
|
||||
nv.utils.windowResize(chart.update);
|
||||
first_data = new Array();
|
||||
$.each(chart_details.data, function(index, value) {
|
||||
if (value.id == 'sales' || value.id == 'sales_compare')
|
||||
{
|
||||
if (value.id == 'sales'){
|
||||
$('#dashtrends_toolbar dl:first').addClass('active').css("border-color", value.border_color);
|
||||
}
|
||||
first_data.push(chart_details.data[index]);
|
||||
|
||||
return chart;
|
||||
});
|
||||
// create content for the tooltip of chart for sales as first chart is Sales
|
||||
chart.interactiveLayer.tooltip.contentGenerator((obj, element) => {
|
||||
if (typeof obj.series[0] !== 'undefined') {
|
||||
var date = new Date(obj.value * 1000);
|
||||
date = date.format(chart_details['date_format']);
|
||||
|
||||
tooltipContent = '';
|
||||
tooltipContent += '<p>' + date_txt + ': <b>' + date + '</b></p>';
|
||||
tooltipContent += '<p>' + obj.series[0].key + ': <b>' + formatCurrency(parseFloat(obj.series[0].value), currency_format, currency_sign, currency_blank) + '</b></p>';
|
||||
}
|
||||
|
||||
return getTooltipContent(obj.series[0].key, tooltipContent, obj.series[0].color);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
chart.yAxis.tickFormat(function(d) {
|
||||
return formatCurrency(parseFloat(d), currency_format, currency_sign, currency_blank);
|
||||
});
|
||||
|
||||
dashtrends_data = chart_details;
|
||||
dashtrends_chart = chart;
|
||||
d3.select('#dash_trends_chart1 svg')
|
||||
.datum(first_data)
|
||||
.call(chart);
|
||||
nv.utils.windowResize(chart.update);
|
||||
|
||||
return chart;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function selectDashtrendsChart(element, type)
|
||||
{
|
||||
// $('#dashtrends_toolbar dl').removeClass('active');
|
||||
current_charts = new Array();
|
||||
$.each(dashtrends_data, function(index, value) {
|
||||
$.each(dashtrends_data.data, function(index, value) {
|
||||
if (value.id == type || value.id == type + '_compare')
|
||||
{
|
||||
if (value.id == type)
|
||||
@@ -64,22 +94,53 @@ function selectDashtrendsChart(element, type)
|
||||
$(element).css("border-color", value.border_color);
|
||||
}
|
||||
|
||||
current_charts.push(dashtrends_data[index]);
|
||||
current_charts.push(dashtrends_data.data[index]);
|
||||
value.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
dashtrends_chart.yAxis.tickFormat(d3.format('.f'));
|
||||
if (type == 'orders' || type == 'visits') {
|
||||
dashtrends_chart.yAxis.tickFormat(d3.format('d'));
|
||||
dashtrends_chart.forceY([0, 1]);
|
||||
} else {
|
||||
dashtrends_chart.yAxis.tickFormat(d3.format('.f'));
|
||||
}
|
||||
|
||||
if (type == 'sales' || type == 'average_cart_value' || type == 'net_profits')
|
||||
if (type == 'sales' || type == 'average_cart_value' || type == 'net_profits') {
|
||||
dashtrends_chart.forceY([0, 50]);
|
||||
dashtrends_chart.yAxis.tickFormat(function(d) {
|
||||
return formatCurrency(parseFloat(d), currency_format, currency_sign, currency_blank);
|
||||
});
|
||||
}
|
||||
|
||||
if (type == 'conversion_rate')
|
||||
if (type == 'conversion_rate') {
|
||||
dashtrends_chart.forceY([0, 10]);
|
||||
dashtrends_chart.yAxis.tickFormat(function(d) {
|
||||
return d3.round(d*100, 2)+' %';
|
||||
return d3.round(d, 2)+' %';
|
||||
});
|
||||
}
|
||||
|
||||
// create content for the tooltip of chart for different charts dynamically
|
||||
dashtrends_chart.interactiveLayer.tooltip.contentGenerator((obj, element) => {
|
||||
if (typeof obj.series[0] !== 'undefined') {
|
||||
var date = new Date(obj.value * 1000);
|
||||
date = date.format(dashtrends_data['date_format']);
|
||||
|
||||
if (type == 'sales' || type == 'average_cart_value' || type == 'net_profits') {
|
||||
var trendValue = formatCurrency(parseFloat(obj.series[0].value), currency_format, currency_sign, currency_blank)
|
||||
} else if (type == 'conversion_rate') {
|
||||
var trendValue = d3.round(obj.series[0]['data'][1], 2)+'%';
|
||||
} else {
|
||||
var trendValue = obj.series[0].value;
|
||||
}
|
||||
|
||||
tooltipContent = '';
|
||||
tooltipContent += '<p>' + date_txt + ': <b>' + date + '</b></p>';
|
||||
tooltipContent += '<p>' + obj.series[0].key + ': <b>' + trendValue + '</b></p>';
|
||||
}
|
||||
|
||||
return getTooltipContent(obj.series[0].key, tooltipContent, obj.series[0].color);
|
||||
});
|
||||
|
||||
d3.select('#dash_trends_chart1 svg')
|
||||
.datum(current_charts)
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
</dl>
|
||||
</div>
|
||||
<div class="col-md-4 col-xs-6">
|
||||
<dl class="label-tooltip" onclick="selectDashtrendsChart(this, 'orders');" data-toggle="tooltip" data-placement="top" data-original-title="{l s='Total number of bookings received within the date range that are considered validated.' mod='dashtrends'}" data-placement="bottom" style='background-color: #56CE56;'>
|
||||
<dt>{l s='Bookings' mod='dashtrends'}</dt>
|
||||
<dl class="label-tooltip" onclick="selectDashtrendsChart(this, 'orders');" data-toggle="tooltip" data-placement="top" data-original-title="{l s='Total number of orders received within the date range that are considered validated.' mod='dashtrends'}" data-placement="bottom" style='background-color: #56CE56;'>
|
||||
<dt>{l s='Orders' mod='dashtrends'}</dt>
|
||||
<dd class="data_value size_l"><span id="orders_score"></span></dd>
|
||||
</dl>
|
||||
</div>
|
||||
@@ -86,11 +86,6 @@
|
||||
<div id="dash_trends_chart1" class="chart with-transitions">
|
||||
<svg></svg>
|
||||
</div>
|
||||
<div class="col-sm-12 ">
|
||||
<p id="no-chart-info" class="alert alert-info no-chart-info">
|
||||
{l s='The graph is unavailable when selecting this date range. Select another date range for the graph.' mod='dashtrends'}
|
||||
</p>
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user