405 lines
13 KiB
PHP
405 lines
13 KiB
PHP
<?php
|
|
|
|
use highcharts\Highchart;
|
|
use highcharts\HighchartJsExpr;
|
|
|
|
class StatsGraphBase
|
|
{
|
|
use \DatabaseCommunication;
|
|
private $months = [
|
|
'',
|
|
'Led',
|
|
'Uno',
|
|
'Bře',
|
|
'Dub',
|
|
'Kvě',
|
|
'Čer',
|
|
'Červ',
|
|
'Srp',
|
|
'Zář',
|
|
'Říj',
|
|
'Lis',
|
|
'Pros',
|
|
];
|
|
|
|
public $dates = [];
|
|
public $type_graph = 'sales';
|
|
public $interval = 'month';
|
|
|
|
/**
|
|
* @var DateTime|false
|
|
*/
|
|
public $to;
|
|
/**
|
|
* @var DateTime|false
|
|
*/
|
|
public $from;
|
|
|
|
public function __construct($from = null, $to = null)
|
|
{
|
|
$this->from = DateTime::createFromFormat(Settings::getDateFormat(), $from);
|
|
$this->to = DateTime::createFromFormat(Settings::getDateFormat(), $to);
|
|
}
|
|
|
|
public function getGraph($json = true)
|
|
{
|
|
$chart = new Highchart(Highchart::HIGHCHART, -1);
|
|
|
|
$chart->chart = [
|
|
'type' => 'line',
|
|
'marginRight' => 130,
|
|
'marginBottom' => 25,
|
|
];
|
|
|
|
$chart->subtitle = [
|
|
'x' => -20,
|
|
];
|
|
|
|
$chart->xAxis->categories = [];
|
|
|
|
$chart->yAxis = [
|
|
'plotLines' => [
|
|
[
|
|
'value' => 0,
|
|
'width' => 1,
|
|
'color' => '#808080',
|
|
],
|
|
],
|
|
];
|
|
|
|
if ($this->type_graph == 'count_order') {
|
|
$chart->chart['renderTo'] = 'count_orders';
|
|
$chart->yAxis['title']['text'] = translate('amount', 'stats');
|
|
$chart->title['text'] = translate('ordersAmount', 'stats');
|
|
$chart->series[0]['name'] = translate('ordersTotal', 'stats');
|
|
|
|
$chart->tooltip->formatter = new HighchartJsExpr(
|
|
"function() { return '<b>'+ this.series.name +'</b><br/>'+ this.x +': '+ this.y;}");
|
|
} elseif ($this->type_graph == 'sales') {
|
|
global $dbcfg;
|
|
$chart->chart['renderTo'] = 'sales';
|
|
$chart->yAxis['title']['text'] = translate('orderValue', 'stats')."({$dbcfg->currency})";
|
|
$chart->title['text'] = translate('revenue', 'stats');
|
|
$chart->series[0]['name'] = translate('revenue', 'stats');
|
|
|
|
$chart->tooltip->formatter = new HighchartJsExpr(
|
|
"function() { return '<b>'+ this.series.name +'</b><br/>'+ this.x +': '+ this.y.toString().replace(/\B(?=(\d{3})+(?!\d))/g, \" \") +' {$dbcfg->currency}';}");
|
|
} elseif ($this->type_graph == 'product') {
|
|
$chart->chart['renderTo'] = 'product-graph';
|
|
$chart->yAxis['title']['text'] = translate('pcsSold', 'stats');
|
|
$chart->title['text'] = $this->getProductName();
|
|
$chart->series[0]['name'] = translate('pcsSold', 'stats');
|
|
|
|
$chart->tooltip->formatter = new HighchartJsExpr(
|
|
"function() { return '<b>'+ this.series.name +'</b><br/>'+ this.x +': '+ this.y;}");
|
|
}
|
|
$chart->title['x'] = -20;
|
|
|
|
$chart->legend = [
|
|
'layout' => 'vertical',
|
|
'align' => 'right',
|
|
'verticalAlign' => 'top',
|
|
'x' => -5,
|
|
'y' => 100,
|
|
'borderWidth' => 0,
|
|
];
|
|
|
|
switch ($this->interval) {
|
|
case 'day':
|
|
$chart->subtitle->text = translate('intervalDaily', 'stats');
|
|
$data = $this->getDaysData();
|
|
break;
|
|
case 'month':
|
|
$chart->subtitle->text = translate('intervalMonthly', 'stats');
|
|
$data = $this->getMonthsData();
|
|
break;
|
|
case 'year':
|
|
default:
|
|
$chart->subtitle->text = translate('intervalYearly', 'stats');
|
|
$data = $this->getYearsData();
|
|
break;
|
|
}
|
|
|
|
$chart->series[0]['data'] = $data['data'];
|
|
$chart->xAxis->categories = $data['x'];
|
|
|
|
if ($json) {
|
|
return $chart->renderOptions();
|
|
} else {
|
|
return $chart;
|
|
}
|
|
}
|
|
|
|
public function getProductName()
|
|
{
|
|
$id_product = getVal('id_product');
|
|
$id_variation = getVal('id_variation');
|
|
|
|
if (empty($id_product)) {
|
|
return '';
|
|
}
|
|
$join = '';
|
|
$fields = 'p.title as product_name';
|
|
if (!empty($id_variation)) {
|
|
$fields .= ', pv.title as variation_name';
|
|
$join = 'LEFT JOIN '.getTableName('products_variations')." as pv ON pv.id={$id_variation} AND pv.id_product={$id_product}";
|
|
}
|
|
|
|
$SQL = sqlQuery("SELECT {$fields} FROM ".getTableName('products')." AS p
|
|
{$join}
|
|
WHERE p.id={$id_product}");
|
|
$names = sqlFetchAssoc($SQL);
|
|
if (!empty($names)) {
|
|
$name = $names['product_name'];
|
|
if (!empty($id_variation)) {
|
|
$name .= " ({$names['variation_name']})";
|
|
}
|
|
|
|
return $name;
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
public function countDays()
|
|
{
|
|
$d1 = $this->from;
|
|
$d2 = $this->to;
|
|
|
|
return $d1->diff($d2)->days + 1;
|
|
}
|
|
|
|
public function countMonths()
|
|
{
|
|
$d1 = $this->from;
|
|
$d2 = $this->to;
|
|
|
|
return ($d1->diff($d2)->m + ($d1->diff($d2)->y * 12)) + 1;
|
|
}
|
|
|
|
public function countYears()
|
|
{
|
|
$d1 = $this->from;
|
|
$d2 = $this->to;
|
|
|
|
return $d1->diff($d2)->y + 1;
|
|
}
|
|
|
|
public function getDaysData()
|
|
{
|
|
$DBData = $this->getDBData();
|
|
|
|
$startDay = $this->from->format('j');
|
|
$startMonth = $this->from->format('n');
|
|
$startYear = $this->from->format('Y');
|
|
|
|
$endDay = $this->from->format('j');
|
|
$endMonth = $this->to->format('n');
|
|
$endYear = $this->to->format('Y');
|
|
|
|
$countMonths = $this->countMonths();
|
|
$countDays = $this->countDays();
|
|
|
|
$x = [];
|
|
$data = [];
|
|
for ($year = 0; $year <= ($endYear - $startYear); $year++) {
|
|
for ($month = $startMonth; $month <= 12; $month++) {
|
|
$days = cal_days_in_month(CAL_GREGORIAN, $month, $startYear + $year);
|
|
for ($day = $startDay; $day <= $days; $day++) {
|
|
if ($countDays == 0) {
|
|
break;
|
|
} else {
|
|
$countDays--;
|
|
}
|
|
$data[] = (!empty($DBData[$year][$month][$day])) ? round($DBData[$year][$month][$day], 2) : 0;
|
|
$x[] = $day.'.';
|
|
}
|
|
$startDay = 1;
|
|
if ($countMonths == 0) {
|
|
break;
|
|
} else {
|
|
$countMonths--;
|
|
}
|
|
}
|
|
$startMonth = 1;
|
|
}
|
|
|
|
return ['x' => $x, 'data' => $data];
|
|
}
|
|
|
|
public function getMonthsData()
|
|
{
|
|
$DBData = $this->getDBData();
|
|
|
|
$startMonth = $this->from->format('n');
|
|
$startYear = $this->from->format('Y');
|
|
|
|
$endMonth = $this->to->format('n');
|
|
$endYear = $this->to->format('Y');
|
|
|
|
$countMonths = $this->countMonths();
|
|
|
|
$x = [];
|
|
$data = [];
|
|
for ($year = 0; $year <= ($endYear - $startYear); $year++) {
|
|
for ($month = $startMonth; $month <= 12; $month++) {
|
|
if ($countMonths == 0) {
|
|
break;
|
|
} else {
|
|
$countMonths--;
|
|
}
|
|
$data[] = (!empty($DBData[$year][$month])) ? round($DBData[$year][$month], 2) : 0;
|
|
$x[] = translate($this->months[$month], 'timedate');
|
|
}
|
|
$startMonth = 1;
|
|
}
|
|
|
|
return ['x' => $x, 'data' => $data];
|
|
}
|
|
|
|
public function getYearsData()
|
|
{
|
|
$DBData = $this->getDBData();
|
|
|
|
$startYear = $this->from->format('Y');
|
|
$endYear = $this->to->format('Y');
|
|
|
|
$x = [];
|
|
$data = [];
|
|
|
|
for ($year = 0; $year <= $endYear - $startYear; $year++) {
|
|
$data[] = (!empty($DBData[$year])) ? round($DBData[$year], 2) : 0;
|
|
$x[] = $startYear + $year;
|
|
}
|
|
|
|
return ['x' => $x, 'data' => $data];
|
|
}
|
|
|
|
public function getDBData()
|
|
{
|
|
if ($this->type_graph == 'sales') {
|
|
$sum = (findModule('currencies')) ? ' * o.currency_rate' : '';
|
|
$sum = ' SUM(o.total_price'.$sum.') as sum';
|
|
} elseif ($this->type_graph == 'product') {
|
|
$sum = ' SUM(oi.pieces) as sum';
|
|
} else {
|
|
$sum = ' COUNT(*) as sum';
|
|
}
|
|
|
|
$what = '';
|
|
$group = '';
|
|
$order = '';
|
|
|
|
switch ($this->interval) {
|
|
case 'month':
|
|
$what = 'MONTH(o.date_created) as month,';
|
|
$group = ', MONTH(o.date_created)';
|
|
$order = ',month';
|
|
break;
|
|
|
|
case 'year':
|
|
break;
|
|
|
|
case 'day':
|
|
default:
|
|
$what = 'DAY(o.date_created) as day, MONTH(o.date_created) as month,';
|
|
$group = ', MONTH(o.date_created), DAY(o.date_created)';
|
|
$order = ',month,day';
|
|
break;
|
|
}
|
|
$format = 'Y-m-d';
|
|
if ($this->type_graph == 'product') {
|
|
$id_product = getVal('id_product');
|
|
$id_variation = getVal('id_variation');
|
|
|
|
if (empty($id_product)) {
|
|
exit;
|
|
}
|
|
|
|
$variation = '';
|
|
if (!empty($id_variation) && $id_variation != '-1') {
|
|
$variation = " AND oi.id_variation={$id_variation}";
|
|
}
|
|
$what = str_replace('date_created', 'date_handle', $what);
|
|
$group = str_replace('date_created', 'date_handle', $group);
|
|
$SQL = sqlQuery("SELECT {$what} (YEAR(o.date_handle)- YEAR('{$this->from->format($format)}')) as year,{$sum}
|
|
FROM order_items AS oi
|
|
LEFT JOIN orders as o ON o.id=oi.id_order
|
|
WHERE oi.id_product={$id_product}{$variation} AND o.date_handle >='{$this->from->format($format)}'
|
|
AND o.date_handle <= '{$this->to->format($format)}'
|
|
GROUP BY YEAR(o.date_handle){$group} ORDER BY year{$order} ASC");
|
|
} else {
|
|
$SQL = sqlQuery("SELECT {$what} (YEAR(o.date_created)- YEAR('{$this->from->format($format)} 00:00:00')) as year,{$sum} FROM orders AS o
|
|
WHERE o.status_storno=0 AND o.date_created >='{$this->from->format($format)} 00:00:00'
|
|
AND o.date_created <= '{$this->to->format($format)} 23:59:59'
|
|
GROUP BY YEAR(o.date_created){$group} ORDER BY year{$order} ASC");
|
|
}
|
|
$data = [];
|
|
if ($this->interval == 'year') {
|
|
foreach ($SQL as $row) {
|
|
$data[$row['year']] = $row['sum'];
|
|
}
|
|
} elseif ($this->interval == 'month') {
|
|
foreach ($SQL as $row) {
|
|
$data[$row['year']][$row['month']] = $row['sum'];
|
|
}
|
|
} elseif ($this->interval == 'day') {
|
|
foreach ($SQL as $row) {
|
|
$data[$row['year']][$row['month']][$row['day']] = $row['sum'];
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
public function getProductsSelling($page = null, $limit = null)
|
|
{
|
|
$limit = (!is_null($page) && !is_null($limit)) ? "LIMIT {$page}, {$limit}" : '';
|
|
|
|
$sql_products = sqlQuery("SELECT p.id, p.title, SUM(oi.pieces) AS sold, p.price
|
|
FROM products AS p, order_items AS oi
|
|
LEFT JOIN orders AS o ON o.id=oi.id_order
|
|
WHERE p.id = oi.id_product AND o.status_storno=0 AND
|
|
o.date_handle >=:time_from AND
|
|
o.date_handle <= :time_to
|
|
GROUP BY oi.id_product
|
|
ORDER BY sold DESC
|
|
{$limit}", ['time_from' => $this->prepareDate($this->from).' 00:00:00', 'time_to' => $this->prepareDate($this->to).' 23:59:59']);
|
|
$products = [];
|
|
foreach ($sql_products as $product) {
|
|
$product['variations'] = [];
|
|
|
|
$sql_variations = sqlQuery('SELECT pv.id, pv.title, SUM(oi.pieces) AS sold, pvcv.value as variation_value, pvcl.label as variation_label, pv.price
|
|
FROM products_variations AS pv
|
|
LEFT JOIN order_items AS oi ON oi.id_product = pv.id_product AND oi.id_variation = pv.id
|
|
LEFT JOIN orders AS o ON o.id=oi.id_order
|
|
LEFT JOIN products_variations_combination pvc ON pvc.id_variation = pv.id
|
|
LEFT JOIN products_variations_choices_values pvcv ON pvcv.id = pvc.id_value
|
|
LEFT JOIN products_variations_choices_labels pvcl ON pvcl.id = pvc.id_label
|
|
WHERE o.status_storno = 0 AND
|
|
o.date_handle >=:time_from AND
|
|
o.date_handle <= :time_to AND
|
|
pv.id_product = :id_product
|
|
GROUP BY oi.id_variation
|
|
ORDER BY sold DESC
|
|
', ['time_from' => $this->prepareDate($this->from).' 00:00:00', 'time_to' => $this->prepareDate($this->to).' 23:59:59',
|
|
'id_product' => $product['id'],
|
|
]);
|
|
|
|
foreach ($sql_variations as $variation) {
|
|
$product['variations'][] = $variation;
|
|
}
|
|
|
|
$products[$product['id']] = $product;
|
|
}
|
|
|
|
return $products;
|
|
}
|
|
}
|
|
|
|
if (empty($subclass)) {
|
|
class StatsGraph extends StatsGraphBase
|
|
{
|
|
}
|
|
}
|