Files
kupshop/class/class.StatsGraph.php
2025-08-02 16:30:27 +02:00

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
{
}
}