dolibarr  17.0.4
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 
30 abstract 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 
41  protected abstract function getNbByMonth($year, $format = 0);
42 
53  public function getNbByMonthWithPrevYear($endyear, $startyear, $cachedelay = 0, $format = 0, $startmonth = 1)
54  {
55  global $conf, $user, $langs;
56 
57  if ($startyear > $endyear) {
58  return -1;
59  }
60 
61  $datay = array();
62 
63  // Search into cache
64  if (!empty($cachedelay)) {
65  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
66  include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
67  }
68 
69  $newpathofdestfile = $conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix) ? '' : $this->cachefilesuffix.'_').$langs->defaultlang.'_entity.'.$conf->entity.'_user'.$user->id.'.cache';
70  $newmask = '0644';
71 
72  $nowgmt = dol_now();
73 
74  $foundintocache = 0;
75  if ($cachedelay > 0) {
76  $filedate = dol_filemtime($newpathofdestfile);
77  if ($filedate >= ($nowgmt - $cachedelay)) {
78  $foundintocache = 1;
79 
80  $this->lastfetchdate[get_class($this).'_'.__FUNCTION__] = $filedate;
81  } else {
82  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.");
83  }
84  }
85  // Load file into $data
86  if ($foundintocache) { // Cache file found and is not too old
87  dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate.".");
88  $data = json_decode(file_get_contents($newpathofdestfile), true);
89  } else {
90  $year = $startyear;
91  $sm = $startmonth - 1;
92  if ($sm != 0) {
93  $year = $year - 1;
94  }
95  while ($year <= $endyear) {
96  $datay[$year] = $this->getNbByMonth($year, $format);
97  $year++;
98  }
99 
100  $data = array();
101 
102  for ($i = 0; $i < 12; $i++) {
103  $data[$i][] = $datay[$endyear][($i + $sm) % 12][0];
104  $year = $startyear;
105  while ($year <= $endyear) {
106  // 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
107  $data[$i][] = $datay[$year - (1 - floor(($i + $sm) / 12)) + ($sm == 0 ? 1 : 0)][($i + $sm) % 12][1];
108  $year++;
109  }
110  }
111  }
112 
113  // Save cache file
114  if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1)) {
115  dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk.");
116  if (!dol_is_dir($conf->user->dir_temp)) {
117  dol_mkdir($conf->user->dir_temp);
118  }
119  $fp = fopen($newpathofdestfile, 'w');
120  fwrite($fp, json_encode($data));
121  fclose($fp);
122  if (!empty($conf->global->MAIN_UMASK)) {
123  $newmask = $conf->global->MAIN_UMASK;
124  }
125  @chmod($newpathofdestfile, octdec($newmask));
126 
127  $this->lastfetchdate[get_class($this).'_'.__FUNCTION__] = $nowgmt;
128  }
129 
130  // return array(array('Month',val1,val2,val3),...)
131  return $data;
132  }
133 
139  protected abstract function getAmountByMonth($year, $format = 0);
140 
154  public function getAmountByMonthWithPrevYear($endyear, $startyear, $cachedelay = 0, $format = 0, $startmonth = 1)
155  {
156  global $conf, $user, $langs;
157 
158  if ($startyear > $endyear) {
159  return -1;
160  }
161 
162  $datay = array();
163 
164  // Search into cache
165  if (!empty($cachedelay)) {
166  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
167  include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
168  }
169 
170  $newpathofdestfile = $conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix) ? '' : $this->cachefilesuffix.'_').$langs->defaultlang.'_entity.'.$conf->entity.'_user'.$user->id.'.cache';
171  $newmask = '0644';
172 
173  $nowgmt = dol_now();
174 
175  $foundintocache = 0;
176  if ($cachedelay > 0) {
177  $filedate = dol_filemtime($newpathofdestfile);
178  if ($filedate >= ($nowgmt - $cachedelay)) {
179  $foundintocache = 1;
180 
181  $this->lastfetchdate[get_class($this).'_'.__FUNCTION__] = $filedate;
182  } else {
183  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.");
184  }
185  }
186 
187  // Load file into $data
188  if ($foundintocache) { // Cache file found and is not too old
189  dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate.".");
190  $data = json_decode(file_get_contents($newpathofdestfile), true);
191  } else {
192  $year = $startyear;
193  $sm = $startmonth - 1;
194  if ($sm != 0) {
195  $year = $year - 1;
196  }
197  while ($year <= $endyear) {
198  $datay[$year] = $this->getAmountByMonth($year, $format);
199  $year++;
200  }
201 
202  $data = array();
203  // $data = array('xval'=>array(0=>xlabel,1=>yval1,2=>yval2...),...)
204  for ($i = 0; $i < 12; $i++) {
205  $data[$i][] = isset($datay[$endyear][($i + $sm) % 12]['label']) ? $datay[$endyear][($i + $sm) % 12]['label'] : $datay[$endyear][($i + $sm) % 12][0]; // set label
206  $year = $startyear;
207  while ($year <= $endyear) {
208  // 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
209  $data[$i][] = $datay[$year - (1 - floor(($i + $sm) / 12)) + ($sm == 0 ? 1 : 0)][($i + $sm) % 12][1]; // set yval for x=i
210  $year++;
211  }
212  }
213  }
214 
215  // Save cache file
216  if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1)) {
217  dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk.");
218  if (!dol_is_dir($conf->user->dir_temp)) {
219  dol_mkdir($conf->user->dir_temp);
220  }
221  $fp = fopen($newpathofdestfile, 'w');
222  if ($fp) {
223  fwrite($fp, json_encode($data));
224  fclose($fp);
225  if (!empty($conf->global->MAIN_UMASK)) {
226  $newmask = $conf->global->MAIN_UMASK;
227  }
228  @chmod($newpathofdestfile, octdec($newmask));
229  } else {
230  dol_syslog("Failed to write cache file", LOG_ERR);
231  }
232  $this->lastfetchdate[get_class($this).'_'.__FUNCTION__] = $nowgmt;
233  }
234 
235  return $data;
236  }
237 
242  protected abstract function getAverageByMonth($year);
243 
251  public function getAverageByMonthWithPrevYear($endyear, $startyear)
252  {
253  if ($startyear > $endyear) {
254  return -1;
255  }
256 
257  $datay = array();
258 
259  $year = $startyear;
260  while ($year <= $endyear) {
261  $datay[$year] = $this->getAverageByMonth($year);
262  $year++;
263  }
264 
265  $data = array();
266 
267  for ($i = 0; $i < 12; $i++) {
268  $data[$i][] = $datay[$endyear][$i][0];
269  $year = $startyear;
270  while ($year <= $endyear) {
271  $data[$i][] = $datay[$year][$i][1];
272  $year++;
273  }
274  }
275 
276  return $data;
277  }
278 
287  public function getAllByProductEntry($year, $cachedelay = 0, $limit = 10)
288  {
289  global $conf, $user, $langs;
290 
291  $data = array();
292 
293  // Search into cache
294  if (!empty($cachedelay)) {
295  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
296  include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
297  }
298 
299  $newpathofdestfile = $conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix) ? '' : $this->cachefilesuffix.'_').$langs->defaultlang.'_entity.'.$conf->entity.'_user'.$user->id.'.cache';
300  $newmask = '0644';
301 
302  $nowgmt = dol_now();
303 
304  $foundintocache = 0;
305  if ($cachedelay > 0) {
306  $filedate = dol_filemtime($newpathofdestfile);
307  if ($filedate >= ($nowgmt - $cachedelay)) {
308  $foundintocache = 1;
309 
310  $this->lastfetchdate[get_class($this).'_'.__FUNCTION__] = $filedate;
311  } else {
312  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.");
313  }
314  }
315 
316  // Load file into $data
317  if ($foundintocache) { // Cache file found and is not too old
318  dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate.".");
319  $data = json_decode(file_get_contents($newpathofdestfile), true);
320  } else {
321  $data = $this->getAllByProduct($year, $limit);
322  // $data[$i][]=$datay[$year][$i][1]; // set yval for x=i
323  }
324 
325  // Save cache file
326  if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1)) {
327  dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk.");
328  if (!dol_is_dir($conf->user->dir_temp)) {
329  dol_mkdir($conf->user->dir_temp);
330  }
331  $fp = fopen($newpathofdestfile, 'w');
332  if ($fp) {
333  fwrite($fp, json_encode($data));
334  fclose($fp);
335  if (!empty($conf->global->MAIN_UMASK)) {
336  $newmask = $conf->global->MAIN_UMASK;
337  }
338  @chmod($newpathofdestfile, octdec($newmask));
339  }
340  $this->lastfetchdate[get_class($this).'_'.__FUNCTION__] = $nowgmt;
341  }
342 
343  return $data;
344  }
345 
346 
347  // Here we have low level of shared code called by XxxStats.class.php
348 
349 
350  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
357  protected function _getNbByYear($sql)
358  {
359  // phpcs:enable
360  $result = array();
361 
362  dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
363  $resql = $this->db->query($sql);
364  if ($resql) {
365  $num = $this->db->num_rows($resql);
366  $i = 0;
367  while ($i < $num) {
368  $row = $this->db->fetch_row($resql);
369  $result[$i] = $row;
370  $i++;
371  }
372  $this->db->free($resql);
373  } else {
374  dol_print_error($this->db);
375  }
376  return $result;
377  }
378 
379  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
386  protected function _getAllByYear($sql)
387  {
388  // phpcs:enable
389  $result = array();
390 
391  dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
392  $resql = $this->db->query($sql);
393  if ($resql) {
394  $num = $this->db->num_rows($resql);
395  $i = 0;
396  while ($i < $num) {
397  $row = $this->db->fetch_object($resql);
398  $result[$i]['year'] = $row->year;
399  $result[$i]['nb'] = $row->nb;
400  if ($i > 0 && $row->nb > 0) {
401  $result[$i - 1]['nb_diff'] = ($result[$i - 1]['nb'] - $row->nb) / $row->nb * 100;
402  }
403  $result[$i]['total'] = $row->total;
404  if ($i > 0 && $row->total > 0) {
405  $result[$i - 1]['total_diff'] = ($result[$i - 1]['total'] - $row->total) / $row->total * 100;
406  }
407  $result[$i]['avg'] = $row->avg;
408  if ($i > 0 && $row->avg > 0) {
409  $result[$i - 1]['avg_diff'] = ($result[$i - 1]['avg'] - $row->avg) / $row->avg * 100;
410  }
411  // For some $sql only
412  if (isset($row->weighted)) {
413  $result[$i]['weighted'] = $row->weighted;
414  if ($i > 0 && $row->weighted > 0) {
415  $result[$i - 1]['avg_weighted'] = ($result[$i - 1]['weighted'] - $row->weighted) / $row->weighted * 100;
416  }
417  }
418  $i++;
419  }
420  $this->db->free($resql);
421  } else {
422  dol_print_error($this->db);
423  }
424  return $result;
425  }
426 
427  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
437  protected function _getNbByMonth($year, $sql, $format = 0)
438  {
439  // phpcs:enable
440  global $langs;
441 
442  $result = array();
443  $res = array();
444 
445  dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
446  $resql = $this->db->query($sql);
447  if ($resql) {
448  $num = $this->db->num_rows($resql);
449  $i = 0;
450  $j = 0;
451  while ($i < $num) {
452  $row = $this->db->fetch_row($resql);
453  $j = $row[0] * 1;
454  $result[$j] = $row[1];
455  $i++;
456  }
457  $this->db->free($resql);
458  } else {
459  dol_print_error($this->db);
460  }
461 
462  for ($i = 1; $i < 13; $i++) {
463  $res[$i] = (isset($result[$i]) ? $result[$i] : 0);
464  }
465 
466  $data = array();
467 
468  for ($i = 1; $i < 13; $i++) {
469  $month = 'unknown';
470  if ($format == 0) {
471  $month = $langs->transnoentitiesnoconv('MonthShort'.sprintf("%02d", $i));
472  } elseif ($format == 1) {
473  $month = $i;
474  } elseif ($format == 2) {
475  $month = $langs->transnoentitiesnoconv('MonthVeryShort'.sprintf("%02d", $i));
476  }
477  //$month=dol_print_date(dol_mktime(12,0,0,$i,1,$year),($format?"%m":"%b"));
478  //$month=dol_substr($month,0,3);
479  $data[$i - 1] = array($month, $res[$i]);
480  }
481 
482  return $data;
483  }
484 
485  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
494  protected function _getAmountByMonth($year, $sql, $format = 0)
495  {
496  // phpcs:enable
497  global $langs;
498 
499  $result = array();
500  $res = array();
501 
502  dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
503 
504  $resql = $this->db->query($sql);
505  if ($resql) {
506  $num = $this->db->num_rows($resql);
507  $i = 0;
508  while ($i < $num) {
509  $row = $this->db->fetch_row($resql);
510  $j = $row[0] * 1;
511  $result[$j] = $row[1];
512  $i++;
513  }
514  $this->db->free($resql);
515  } else {
516  dol_print_error($this->db);
517  }
518 
519  for ($i = 1; $i < 13; $i++) {
520  $res[$i] = (int) round((isset($result[$i]) ? $result[$i] : 0));
521  }
522 
523  $data = array();
524 
525  for ($i = 1; $i < 13; $i++) {
526  $month = 'unknown';
527  if ($format == 0) {
528  $month = $langs->transnoentitiesnoconv('MonthShort'.sprintf("%02d", $i));
529  } elseif ($format == 1) {
530  $month = $i;
531  } elseif ($format == 2) {
532  $month = $langs->transnoentitiesnoconv('MonthVeryShort'.sprintf("%02d", $i));
533  }
534  //$month=dol_print_date(dol_mktime(12,0,0,$i,1,$year),($format?"%m":"%b"));
535  //$month=dol_substr($month,0,3);
536  $data[$i - 1] = array($month, $res[$i]);
537  }
538 
539  return $data;
540  }
541 
542  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
552  protected function _getAverageByMonth($year, $sql, $format = 0)
553  {
554  // phpcs:enable
555  global $langs;
556 
557  $result = array();
558  $res = array();
559 
560  dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
561  $resql = $this->db->query($sql);
562  if ($resql) {
563  $num = $this->db->num_rows($resql);
564  $i = 0;
565  $j = 0;
566  while ($i < $num) {
567  $row = $this->db->fetch_row($resql);
568  $j = $row[0] * 1;
569  $result[$j] = $row[1];
570  $i++;
571  }
572  $this->db->free($resql);
573  } else {
574  dol_print_error($this->db);
575  }
576 
577  for ($i = 1; $i < 13; $i++) {
578  $res[$i] = (isset($result[$i]) ? $result[$i] : 0);
579  }
580 
581  $data = array();
582 
583  for ($i = 1; $i < 13; $i++) {
584  $month = 'unknown';
585  if ($format == 0) {
586  $month = $langs->transnoentitiesnoconv('MonthShort'.sprintf("%02d", $i));
587  } elseif ($format == 1) {
588  $month = $i;
589  } elseif ($format == 2) {
590  $month = $langs->transnoentitiesnoconv('MonthVeryShort'.sprintf("%02d", $i));
591  }
592  //$month=dol_print_date(dol_mktime(12,0,0,$i,1,$year),($format?"%m":"%b"));
593  //$month=dol_substr($month,0,3);
594  $data[$i - 1] = array($month, $res[$i]);
595  }
596 
597  return $data;
598  }
599 
600 
601  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
609  protected function _getAllByProduct($sql, $limit = 10)
610  {
611  // phpcs:enable
612  global $langs;
613 
614  $result = array();
615 
616  dol_syslog(get_class($this).'::'.__FUNCTION__."", LOG_DEBUG);
617  $resql = $this->db->query($sql);
618  if ($resql) {
619  $num = $this->db->num_rows($resql);
620  $i = 0;
621  $other = 0;
622  while ($i < $num) {
623  $row = $this->db->fetch_row($resql);
624  if ($i < $limit || $num == $limit) {
625  $result[$i] = array($row[0], $row[1]); // Ref of product, nb
626  } else {
627  $other += $row[1];
628  }
629  $i++;
630  }
631  if ($num > $limit) {
632  $result[$i] = array($langs->transnoentitiesnoconv("Other"), $other);
633  }
634  $this->db->free($resql);
635  } else {
636  dol_print_error($this->db);
637  }
638 
639  return $result;
640  }
641 
642  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
648  protected function _getAmountByYear($sql)
649  {
650  $result = array();
651  $resql = $this->db->query($sql);
652  if ($resql) {
653  $num = $this->db->num_rows($resql);
654  $i = 0;
655  while ($i < $num) {
656  $row = $this->db->fetch_row($resql);
657  $j = (int) $row[0];
658  $result[] = [
659  0 => (int) $row[0],
660  1 => (int) $row[1],
661  ];
662  $i++;
663  }
664  $this->db->free($resql);
665  }
666  return $result;
667  }
668 }
Parent class of statistics class.
Definition: stats.class.php:31
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.
Definition: stats.class.php:53
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...
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') &&!empty($user->rights->don->lire)) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:745
dol_filemtime($pathoffile)
Return time of a file.
Definition: files.lib.php:597
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:451
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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)
$conf db
API class for accounts.
Definition: inc.php:41