dolibarr 19.0.3
stats.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (c) 2008-2013 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2012 Marcos GarcĂ­a <marcosgdf@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
30abstract class Stats
31{
32 protected $db;
33 protected $lastfetchdate = array(); // Dates of cache file read by methods
34 public $cachefilesuffix = ''; // Suffix to add to name of cache file (to avoid file name conflicts)
35
39 public $from;
40
44 public $where;
48 public $from_line;
52 public $field_date;
56 public $field;
60 public $field_line;
61
65 public $error;
66
70 public $year;
71
75 public $month;
76
82 abstract protected function getNbByMonth($year, $format = 0);
83
94 public function getNbByMonthWithPrevYear($endyear, $startyear, $cachedelay = 0, $format = 0, $startmonth = 1)
95 {
96 global $conf, $user, $langs;
97
98 if ($startyear > $endyear) {
99 return array();
100 }
101
102 $datay = array();
103
104 // Search into cache
105 if (!empty($cachedelay)) {
106 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
107 include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
108 }
109
110 $newpathofdestfile = $conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix) ? '' : $this->cachefilesuffix.'_').$langs->defaultlang.'_entity.'.$conf->entity.'_user'.$user->id.'.cache';
111 $newmask = '0644';
112
113 $nowgmt = dol_now();
114
115 $foundintocache = 0;
116 if ($cachedelay > 0) {
117 $filedate = dol_filemtime($newpathofdestfile);
118 if ($filedate >= ($nowgmt - $cachedelay)) {
119 $foundintocache = 1;
120
121 $this->lastfetchdate[get_class($this).'_'.__FUNCTION__] = $filedate;
122 } else {
123 dol_syslog(get_class($this).'::'.__FUNCTION__." cache file ".$newpathofdestfile." is not found or older than now - cachedelay (".$nowgmt." - ".$cachedelay.") so we can't use it.");
124 }
125 }
126 // Load file into $data
127 if ($foundintocache) { // Cache file found and is not too old
128 dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate.".");
129 $data = json_decode(file_get_contents($newpathofdestfile), true);
130 } else {
131 $year = $startyear;
132 $sm = $startmonth - 1;
133 if ($sm != 0) {
134 $year = $year - 1;
135 }
136 while ($year <= $endyear) {
137 $datay[$year] = $this->getNbByMonth($year, $format);
138 $year++;
139 }
140
141 $data = array();
142
143 for ($i = 0; $i < 12; $i++) {
144 $data[$i][] = $datay[$endyear][($i + $sm) % 12][0];
145 $year = $startyear;
146 while ($year <= $endyear) {
147 // floor(($i + $sm) / 12)) is 0 if we are after the month start $sm and same year, become 1 when we reach january of next year
148 $data[$i][] = $datay[$year - (1 - floor(($i + $sm) / 12)) + ($sm == 0 ? 1 : 0)][($i + $sm) % 12][1];
149 $year++;
150 }
151 }
152 }
153
154 // Save cache file
155 if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1)) {
156 dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk.");
157 if (!dol_is_dir($conf->user->dir_temp)) {
158 dol_mkdir($conf->user->dir_temp);
159 }
160 $fp = fopen($newpathofdestfile, 'w');
161 if ($fp) {
162 fwrite($fp, json_encode($data));
163 fclose($fp);
164 } else {
165 dol_syslog("Failed to save cache file ".$newpathofdestfile);
166 }
167 dolChmod($newpathofdestfile);
168
169 $this->lastfetchdate[get_class($this).'_'.__FUNCTION__] = $nowgmt;
170 }
171
172 // return array(array('Month',val1,val2,val3),...)
173 return $data;
174 }
175
181 abstract protected function getAmountByMonth($year, $format = 0);
182
196 public function getAmountByMonthWithPrevYear($endyear, $startyear, $cachedelay = 0, $format = 0, $startmonth = 1)
197 {
198 global $conf, $user, $langs;
199
200 if ($startyear > $endyear) {
201 return array();
202 }
203
204 $datay = array();
205
206 // Search into cache
207 if (!empty($cachedelay)) {
208 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
209 include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
210 }
211
212 $newpathofdestfile = $conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix) ? '' : $this->cachefilesuffix.'_').$langs->defaultlang.'_entity.'.$conf->entity.'_user'.$user->id.'.cache';
213 $newmask = '0644';
214
215 $nowgmt = dol_now();
216
217 $foundintocache = 0;
218 if ($cachedelay > 0) {
219 $filedate = dol_filemtime($newpathofdestfile);
220 if ($filedate >= ($nowgmt - $cachedelay)) {
221 $foundintocache = 1;
222
223 $this->lastfetchdate[get_class($this).'_'.__FUNCTION__] = $filedate;
224 } else {
225 dol_syslog(get_class($this).'::'.__FUNCTION__." cache file ".$newpathofdestfile." is not found or older than now - cachedelay (".$nowgmt." - ".$cachedelay.") so we can't use it.");
226 }
227 }
228
229 // Load file into $data
230 if ($foundintocache) { // Cache file found and is not too old
231 dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate.".");
232 $data = json_decode(file_get_contents($newpathofdestfile), true);
233 } else {
234 $year = $startyear;
235 $sm = $startmonth - 1;
236 if ($sm != 0) {
237 $year = $year - 1;
238 }
239 while ($year <= $endyear) {
240 $datay[$year] = $this->getAmountByMonth($year, $format);
241 $year++;
242 }
243
244 $data = array();
245 // $data = array('xval'=>array(0=>xlabel,1=>yval1,2=>yval2...),...)
246 for ($i = 0; $i < 12; $i++) {
247 $data[$i][] = isset($datay[$endyear][($i + $sm) % 12]['label']) ? $datay[$endyear][($i + $sm) % 12]['label'] : $datay[$endyear][($i + $sm) % 12][0]; // set label
248 $year = $startyear;
249 while ($year <= $endyear) {
250 // floor(($i + $sm) / 12)) is 0 if we are after the month start $sm and same year, become 1 when we reach january of next year
251 $data[$i][] = $datay[$year - (1 - floor(($i + $sm) / 12)) + ($sm == 0 ? 1 : 0)][($i + $sm) % 12][1]; // set yval for x=i
252 $year++;
253 }
254 }
255 }
256
257 // Save cache file
258 if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1)) {
259 dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk.");
260 if (!dol_is_dir($conf->user->dir_temp)) {
261 dol_mkdir($conf->user->dir_temp);
262 }
263 $fp = fopen($newpathofdestfile, 'w');
264 if ($fp) {
265 fwrite($fp, json_encode($data));
266 fclose($fp);
267 dolChmod($newpathofdestfile);
268 } else {
269 dol_syslog("Failed to write cache file", LOG_ERR);
270 }
271 $this->lastfetchdate[get_class($this).'_'.__FUNCTION__] = $nowgmt;
272 }
273
274 return $data;
275 }
276
281 abstract protected function getAverageByMonth($year);
282
290 public function getAverageByMonthWithPrevYear($endyear, $startyear)
291 {
292 if ($startyear > $endyear) {
293 return array();
294 }
295
296 $datay = array();
297
298 $year = $startyear;
299 while ($year <= $endyear) {
300 $datay[$year] = $this->getAverageByMonth($year);
301 $year++;
302 }
303
304 $data = array();
305
306 for ($i = 0; $i < 12; $i++) {
307 $data[$i][] = $datay[$endyear][$i][0];
308 $year = $startyear;
309 while ($year <= $endyear) {
310 $data[$i][] = $datay[$year][$i][1];
311 $year++;
312 }
313 }
314
315 return $data;
316 }
317
326 public function getAllByProductEntry($year, $cachedelay = 0, $limit = 10)
327 {
328 global $conf, $user, $langs;
329
330 $data = array();
331
332 // Search into cache
333 if (!empty($cachedelay)) {
334 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
335 include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
336 }
337
338 $newpathofdestfile = $conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix) ? '' : $this->cachefilesuffix.'_').$langs->defaultlang.'_entity.'.$conf->entity.'_user'.$user->id.'.cache';
339 $newmask = '0644';
340
341 $nowgmt = dol_now();
342
343 $foundintocache = 0;
344 if ($cachedelay > 0) {
345 $filedate = dol_filemtime($newpathofdestfile);
346 if ($filedate >= ($nowgmt - $cachedelay)) {
347 $foundintocache = 1;
348
349 $this->lastfetchdate[get_class($this).'_'.__FUNCTION__] = $filedate;
350 } else {
351 dol_syslog(get_class($this).'::'.__FUNCTION__." cache file ".$newpathofdestfile." is not found or older than now - cachedelay (".$nowgmt." - ".$cachedelay.") so we can't use it.");
352 }
353 }
354
355 // Load file into $data
356 if ($foundintocache) { // Cache file found and is not too old
357 dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate.".");
358 $data = json_decode(file_get_contents($newpathofdestfile), true);
359 } else {
360 // This method is defined in parent object only, not into abstract, so we disable phpstan warning
362 $data = $this->getAllByProduct($year, $limit);
363 }
364
365 // Save cache file
366 if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1)) {
367 dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk.");
368 if (!dol_is_dir($conf->user->dir_temp)) {
369 dol_mkdir($conf->user->dir_temp);
370 }
371 $fp = fopen($newpathofdestfile, 'w');
372 if ($fp) {
373 fwrite($fp, json_encode($data));
374 fclose($fp);
375 dolChmod($newpathofdestfile);
376 }
377 $this->lastfetchdate[get_class($this).'_'.__FUNCTION__] = $nowgmt;
378 }
379
380 return $data;
381 }
382
383
384 // Here we have low level of shared code called by XxxStats.class.php
385
386
387 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
394 protected function _getNbByYear($sql)
395 {
396 // phpcs:enable
397 $result = array();
398
399 dol_syslog(get_class($this).'::'.__FUNCTION__, LOG_DEBUG);
400 $resql = $this->db->query($sql);
401 if ($resql) {
402 $num = $this->db->num_rows($resql);
403 $i = 0;
404 while ($i < $num) {
405 $row = $this->db->fetch_row($resql);
406 $result[$i] = $row;
407 $i++;
408 }
409 $this->db->free($resql);
410 } else {
411 dol_print_error($this->db);
412 }
413 return $result;
414 }
415
416 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
423 protected function _getAllByYear($sql)
424 {
425 // phpcs:enable
426 $result = array();
427
428 dol_syslog(get_class($this).'::'.__FUNCTION__, LOG_DEBUG);
429 $resql = $this->db->query($sql);
430 if ($resql) {
431 $num = $this->db->num_rows($resql);
432 $i = 0;
433 while ($i < $num) {
434 $row = $this->db->fetch_object($resql);
435 $result[$i]['year'] = $row->year;
436 $result[$i]['nb'] = $row->nb;
437 if ($i > 0 && $row->nb > 0) {
438 $result[$i - 1]['nb_diff'] = ($result[$i - 1]['nb'] - $row->nb) / $row->nb * 100;
439 }
440 // For some $sql only
441 if (property_exists($row, 'total')) {
442 $result[$i]['total'] = $row->total;
443 if ($i > 0 && $row->total > 0) {
444 $result[$i - 1]['total_diff'] = ($result[$i - 1]['total'] - $row->total) / $row->total * 100;
445 }
446 }
447 // For some $sql only
448 if (property_exists($row, 'total')) {
449 $result[$i]['avg'] = $row->avg;
450 if ($i > 0 && $row->avg > 0) {
451 $result[$i - 1]['avg_diff'] = ($result[$i - 1]['avg'] - $row->avg) / $row->avg * 100;
452 }
453 }
454 // For some $sql only
455 if (property_exists($row, 'weighted')) {
456 $result[$i]['weighted'] = $row->weighted;
457 if ($i > 0 && $row->weighted > 0) {
458 $result[$i - 1]['avg_weighted'] = ($result[$i - 1]['weighted'] - $row->weighted) / $row->weighted * 100;
459 }
460 }
461 $i++;
462 }
463 $this->db->free($resql);
464 } else {
465 dol_print_error($this->db);
466 }
467 return $result;
468 }
469
470 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
480 protected function _getNbByMonth($year, $sql, $format = 0)
481 {
482 // phpcs:enable
483 global $langs;
484
485 $result = array();
486 $res = array();
487
488 dol_syslog(get_class($this).'::'.__FUNCTION__, LOG_DEBUG);
489 $resql = $this->db->query($sql);
490 if ($resql) {
491 $num = $this->db->num_rows($resql);
492 $i = 0;
493 $j = 0;
494 while ($i < $num) {
495 $row = $this->db->fetch_row($resql);
496 $j = $row[0] * 1;
497 $result[$j] = $row[1];
498 $i++;
499 }
500 $this->db->free($resql);
501 } else {
502 dol_print_error($this->db);
503 }
504
505 for ($i = 1; $i < 13; $i++) {
506 $res[$i] = (isset($result[$i]) ? $result[$i] : 0);
507 }
508
509 $data = array();
510
511 for ($i = 1; $i < 13; $i++) {
512 $month = 'unknown';
513 if ($format == 0) {
514 $month = $langs->transnoentitiesnoconv('MonthShort'.sprintf("%02d", $i));
515 } elseif ($format == 1) {
516 $month = $i;
517 } elseif ($format == 2) {
518 $month = $langs->transnoentitiesnoconv('MonthVeryShort'.sprintf("%02d", $i));
519 }
520 //$month=dol_print_date(dol_mktime(12,0,0,$i,1,$year),($format?"%m":"%b"));
521 //$month=dol_substr($month,0,3);
522 $data[$i - 1] = array($month, $res[$i]);
523 }
524
525 return $data;
526 }
527
528 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
537 protected function _getAmountByMonth($year, $sql, $format = 0)
538 {
539 // phpcs:enable
540 global $langs;
541
542 $result = array();
543 $res = array();
544
545 dol_syslog(get_class($this).'::'.__FUNCTION__, LOG_DEBUG);
546
547 $resql = $this->db->query($sql);
548 if ($resql) {
549 $num = $this->db->num_rows($resql);
550 $i = 0;
551 while ($i < $num) {
552 $row = $this->db->fetch_row($resql);
553 $j = $row[0] * 1;
554 $result[$j] = $row[1];
555 $i++;
556 }
557 $this->db->free($resql);
558 } else {
559 dol_print_error($this->db);
560 }
561
562 for ($i = 1; $i < 13; $i++) {
563 $res[$i] = (int) round((isset($result[$i]) ? $result[$i] : 0));
564 }
565
566 $data = array();
567
568 for ($i = 1; $i < 13; $i++) {
569 $month = 'unknown';
570 if ($format == 0) {
571 $month = $langs->transnoentitiesnoconv('MonthShort'.sprintf("%02d", $i));
572 } elseif ($format == 1) {
573 $month = $i;
574 } elseif ($format == 2) {
575 $month = $langs->transnoentitiesnoconv('MonthVeryShort'.sprintf("%02d", $i));
576 }
577 //$month=dol_print_date(dol_mktime(12,0,0,$i,1,$year),($format?"%m":"%b"));
578 //$month=dol_substr($month,0,3);
579 $data[$i - 1] = array($month, $res[$i]);
580 }
581
582 return $data;
583 }
584
585 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
595 protected function _getAverageByMonth($year, $sql, $format = 0)
596 {
597 // phpcs:enable
598 global $langs;
599
600 $result = array();
601 $res = array();
602
603 dol_syslog(get_class($this).'::'.__FUNCTION__, LOG_DEBUG);
604 $resql = $this->db->query($sql);
605 if ($resql) {
606 $num = $this->db->num_rows($resql);
607 $i = 0;
608 $j = 0;
609 while ($i < $num) {
610 $row = $this->db->fetch_row($resql);
611 $j = $row[0] * 1;
612 $result[$j] = $row[1];
613 $i++;
614 }
615 $this->db->free($resql);
616 } else {
617 dol_print_error($this->db);
618 }
619
620 for ($i = 1; $i < 13; $i++) {
621 $res[$i] = (isset($result[$i]) ? $result[$i] : 0);
622 }
623
624 $data = array();
625
626 for ($i = 1; $i < 13; $i++) {
627 $month = 'unknown';
628 if ($format == 0) {
629 $month = $langs->transnoentitiesnoconv('MonthShort'.sprintf("%02d", $i));
630 } elseif ($format == 1) {
631 $month = $i;
632 } elseif ($format == 2) {
633 $month = $langs->transnoentitiesnoconv('MonthVeryShort'.sprintf("%02d", $i));
634 }
635 //$month=dol_print_date(dol_mktime(12,0,0,$i,1,$year),($format?"%m":"%b"));
636 //$month=dol_substr($month,0,3);
637 $data[$i - 1] = array($month, $res[$i]);
638 }
639
640 return $data;
641 }
642
643
644 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
652 protected function _getAllByProduct($sql, $limit = 10)
653 {
654 // phpcs:enable
655 global $langs;
656
657 $result = array();
658
659 dol_syslog(get_class($this).'::'.__FUNCTION__, LOG_DEBUG);
660 $resql = $this->db->query($sql);
661 if ($resql) {
662 $num = $this->db->num_rows($resql);
663 $i = 0;
664 $other = 0;
665 while ($i < $num) {
666 $row = $this->db->fetch_row($resql);
667 if ($i < $limit || $num == $limit) {
668 $result[$i] = array($row[0], $row[1]); // Ref of product, nb
669 } else {
670 $other += $row[1];
671 }
672 $i++;
673 }
674 if ($num > $limit) {
675 $result[$i] = array($langs->transnoentitiesnoconv("Other"), $other);
676 }
677 $this->db->free($resql);
678 } else {
679 dol_print_error($this->db);
680 }
681
682 return $result;
683 }
684
685 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
691 protected function _getAmountByYear($sql)
692 {
693 $result = array();
694 $resql = $this->db->query($sql);
695 if ($resql) {
696 $num = $this->db->num_rows($resql);
697 $i = 0;
698 while ($i < $num) {
699 $row = $this->db->fetch_row($resql);
700 $j = (int) $row[0];
701 $result[] = [
702 0 => (int) $row[0],
703 1 => (int) $row[1],
704 ];
705 $i++;
706 }
707 $this->db->free($resql);
708 }
709 return $result;
710 }
711}
Parent class of statistics class.
getNbByMonth($year, $format=0)
getAmountByMonth($year, $format=0)
getAverageByMonth($year)
_getAmountByYear($sql)
Returns the summed amounts per year for a given number of past years ending now.
_getAverageByMonth($year, $sql, $format=0)
Renvoie le montant moyen par mois pour une annee donnee Return the amount average par month for a giv...
_getAmountByMonth($year, $sql, $format=0)
Return the amount per month for a given year.
_getNbByYear($sql)
Return nb of elements by year.
_getAllByYear($sql)
Return nb of elements, total amount and avg amount each year.
getAmountByMonthWithPrevYear($endyear, $startyear, $cachedelay=0, $format=0, $startmonth=1)
Return amount of elements by month for several years.
getNbByMonthWithPrevYear($endyear, $startyear, $cachedelay=0, $format=0, $startmonth=1)
Return nb of elements by month for several years.
getAllByProductEntry($year, $cachedelay=0, $limit=10)
Return count, and sum of products.
getAverageByMonthWithPrevYear($endyear, $startyear)
Return average of entity by month for several years.
_getAllByProduct($sql, $limit=10)
Return number or total of product refs.
_getNbByMonth($year, $sql, $format=0)
Renvoie le nombre de documents par mois pour une annee donnee Return number of documents per month fo...
dol_filemtime($pathoffile)
Return time of a file.
dol_is_dir($folder)
Test if filename is a directory.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_now($mode='auto')
Return date for now.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)