From 85572a4bd13d67456640e931451772f27e147e27 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Mon, 5 Aug 2013 17:19:49 +0200 Subject: [PATCH] New: Start to work on box for product distributions Qual: Start to move code of dol_print_graph function into class DolGraph. --- .../facture/class/facturestats.class.php | 28 ++ .../boxes/box_graph_product_distribution.php | 252 +++++++++++++++++ htdocs/core/class/dolgraph.class.php | 253 +++++++++++------- htdocs/core/class/stats.class.php | 127 ++++++++- htdocs/core/modules/modProduct.class.php | 3 +- htdocs/langs/en_US/boxes.lang | 4 +- htdocs/langs/fr_FR/boxes.lang | 2 + 7 files changed, 564 insertions(+), 105 deletions(-) create mode 100644 htdocs/core/boxes/box_graph_product_distribution.php diff --git a/htdocs/compta/facture/class/facturestats.class.php b/htdocs/compta/facture/class/facturestats.class.php index ff9a90088bf..7f33f66fd8b 100644 --- a/htdocs/compta/facture/class/facturestats.class.php +++ b/htdocs/compta/facture/class/facturestats.class.php @@ -62,13 +62,17 @@ class FactureStats extends Stats { $object=new Facture($this->db); $this->from = MAIN_DB_PREFIX.$object->table_element." as f"; + $this->from_line = MAIN_DB_PREFIX.$object->table_element_line." as tl"; $this->field='total'; + $this->field_line='total_ht'; } if ($mode == 'supplier') { $object=new FactureFournisseur($this->db); $this->from = MAIN_DB_PREFIX.$object->table_element." as f"; + $this->from_line = MAIN_DB_PREFIX.$object->table_element_line." as tl"; $this->field='total_ht'; + $this->field_line='total_ht'; } $this->where = " f.fk_statut > 0"; @@ -189,6 +193,30 @@ class FactureStats extends Stats return $this->_getAllByYear($sql); } + + /** + * Return nb, amount of predefined product for year + * + * @param int $year Year to scan + * @return array Array of values + */ + function getAllByProduct($year) + { + global $user; + + $sql = "SELECT product.ref, COUNT(product.ref) as nb, SUM(tl.".$this->field_line.") as total, AVG(tl.".$this->field_line.") as avg"; + $sql.= " FROM ".$this->from.", ".$this->from_line.", ".MAIN_DB_PREFIX."product as product"; + //if (!$user->rights->societe->client->voir && !$user->societe_id) $sql.= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; + $sql.= " WHERE ".$this->where; + $sql.= " AND f.rowid = tl.fk_facture AND tl.fk_product = product.rowid"; + $sql.= " GROUP BY product.ref"; + $sql.= $this->db->order('nb','DESC'); + $sql.= $this->db->plimit(20); + + return $this->_getAllByProduct($sql); + } + + } ?> diff --git a/htdocs/core/boxes/box_graph_product_distribution.php b/htdocs/core/boxes/box_graph_product_distribution.php new file mode 100644 index 00000000000..8fccc4fd8a0 --- /dev/null +++ b/htdocs/core/boxes/box_graph_product_distribution.php @@ -0,0 +1,252 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/core/boxes/box_graph_product_distribution.php.php + * \ingroup factures + * \brief Box to show graph of invoices per month + */ +include_once DOL_DOCUMENT_ROOT.'/core/boxes/modules_boxes.php'; + + +/** + * Class to manage the box to show last invoices + */ +class box_graph_product_distribution extends ModeleBoxes +{ + var $boxcode="productdistribution"; + var $boximg="object_product"; + var $boxlabel="BoxProductDistribution"; + var $depends = array("product|service"); + + var $db; + + var $info_box_head = array(); + var $info_box_contents = array(); + + + /** + * Constructor + * + * @param DoliDB $db Database handler + * @param string $param More parameters + */ + function __construct($db,$param) + { + global $conf; + + $this->db=$db; + } + + /** + * Load data into info_box_contents array to show array later. + * + * @param int $max Maximum number of records to load + * @return void + */ + function loadBox($max=5) + { + global $conf, $user, $langs, $db; + + $this->max=$max; + + $refreshaction='refresh_'.$this->boxcode; + + include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; + include_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php'; + include_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; + $facturestatic=new Facture($db); + + $text = $langs->trans("BoxProductDistribution",$max); + $this->info_box_head = array( + 'text' => $text, + 'limit'=> dol_strlen($text), + 'graph'=> 1, + 'sublink'=>'', + 'subtext'=>$langs->trans("Filter"), + 'subpicto'=>'filter.png', + 'subclass'=>'linkobject', + 'target'=>'none' // Set '' to get target="_blank" + ); + + $param_year='DOLUSERCOOKIE_param'.$this->boxcode.'year'; + $param_showinvoicenb='DOLUSERCOOKIE_param'.$this->boxcode.'showinvoicenb'; + $param_showpropalnb='DOLUSERCOOKIE_param'.$this->boxcode.'showpropalnb'; + $showinvoicenb=GETPOST($param_showinvoicenb,'alpha',4); + $showpropalnb=GETPOST($param_showpropalnb,'alpha',4); + if (empty($showinvoicenb) && empty($showpropalnb)) { $showpropalnb=1; $showinvoicenb=1; } + $nowarray=dol_getdate(dol_now(),true); + $year=(GETPOST($param_year,'',4)?GETPOST($param_year,'int',4):$nowarray['year']); + + if ($user->rights->facture->lire) + { + + include_once DOL_DOCUMENT_ROOT.'/core/class/dolgraph.class.php'; + include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facturestats.class.php'; + $mode='customer'; + $userid=0; + $WIDTH=(($showinvoicenb && $showpropalnb) || ! empty($conf->dol_optimize_smallscreen))?'256':'320'; + $HEIGHT='192'; + + $stats = new FactureStats($this->db, 0, $mode, ($userid>0?$userid:0)); + + // Build graphic number of object. $data = array(array('Lib',val1,val2,val3),...) + if ($showinvoicenb) + { + $data1 = $stats->getAllByProductEntry($year,(GETPOST('action')==$refreshaction?-1:(3600*24))); + + $filenamenb = $dir."/prodserforinvoice-".$year.".png"; + $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=productstats&file=prodserforinvoice-'.$year.'.png'; + + $px1 = new DolGraph(); + $mesg = $px1->isGraphKo(); + if (! $mesg) + { + $px1->SetData($data1); + unset($data1); + $px1->SetPrecisionY(0); + $i=0;$tot=count($data2);$legend=array(); + while ($i <= $tot) + { + $legend[]=$data2[$i][0]; + $i++; + } + $px1->SetLegend($legend); + $px1->SetMaxValue($px1->GetCeilMaxValue()); + $px1->SetWidth($WIDTH); + $px1->SetHeight($HEIGHT); + //$px1->SetYLabel($langs->trans("NumberOfBills")); + $px1->SetShading(3); + $px1->SetHorizTickIncrement(1); + $px1->SetPrecisionY(0); + $px1->SetCssPrefix("cssboxes"); + //$px1->mode='depth'; + $px1->SetType(array('pie')); + $px1->SetTitle($langs->trans("BoxProductDistributionFor",$langs->trans("ProductsServices"),$langs->transnoentitiesnoconv("Invoices"))); + + $px1->draw($filenamenb,$fileurlnb); + } + } + } + +/* if ($user->rights->propal->lire) + { + // Build graphic number of object. $data = array(array('Lib',val1,val2,val3),...) + if ($showpropalnb) + { + $data2 = $stats->getAmountByMonthWithPrevYear($year,(GETPOST('action')==$refreshaction?-1:(3600*24))); + + $filenamenb = $dir."/prodserforpropal-".$year.".png"; + $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=billstats&file=prodserforpropal-'.$year.'.png'; + + $px2 = new DolGraph(); + $mesg = $px2->isGraphKo(); + if (! $mesg) + { + $px2->SetData($data2); + unset($data2); + $px2->SetPrecisionY(0); + $i=0;$tot=count($data2);$legend=array(); + while ($i <= $tot) + { + $legend[]=$data2[$i][0]; + $i++; + } + $px2->SetLegend($legend); + $px2->SetMaxValue($px2->GetCeilMaxValue()); + $px2->SetWidth($WIDTH); + $px2->SetHeight($HEIGHT); + //$px2->SetYLabel($langs->trans("AmountOfBillsHT")); + $px2->SetShading(3); + $px2->SetHorizTickIncrement(1); + $px2->SetPrecisionY(0); + $px2->SetCssPrefix("cssboxes"); + $px2->mode='depth'; + $px2->SetTitle($langs->trans("BoxProductDistributionFor",$langs->trans("ProductsServices"),$langs->transnoentitiesnoconv("Proposals"))); + + $px2->draw($filenamenb,$fileurlnb); + } + } + } + */ + if (! $mesg) + { + $stringtoshow=''; + $stringtoshow.=''; + $stringtoshow.='
'; // hideobject is to start hidden + $stringtoshow.='
'; + $stringtoshow.=''; + $stringtoshow.=''; + $stringtoshow.=' '.$langs->trans("ForInvoice"); + $stringtoshow.='   '; + $stringtoshow.=' '.$langs->trans("ForProposals"); + $stringtoshow.=' '; + $stringtoshow.=' '.$langs->trans("ForOrders"); + $stringtoshow.='
'; + $stringtoshow.=$langs->trans("Year").' '; + $stringtoshow.=''; + $stringtoshow.='
'; + $stringtoshow.='
'; + if ($showinvoicenb && $showpropalnb) + { + $stringtoshow.='
'; + $stringtoshow.='
'; + } + if ($showinvoicenb) $stringtoshow.=$px1->show(); + if ($showinvoicenb && $showpropalnb) + { + $stringtoshow.='
'; + $stringtoshow.='
'; + } +// if ($showpropalnb) $stringtoshow.=$px2->show(); + if ($showinvoicenb && $showpropalnb) + { + $stringtoshow.='
'; + $stringtoshow.='
'; + } + $this->info_box_contents[0][0] = array('td' => 'align="center" class="nohover"','textnoformat'=>$stringtoshow); + } + else + { + $this->info_box_contents[0][0] = array( 'td' => 'align="left" class="nohover"', + 'maxlength'=>500, + 'text' => $mesg); + } + + } + + /** + * Method to show box + * + * @param array $head Array with properties of box title + * @param array $contents Array with properties of box lines + * @return void + */ + function showBox($head = null, $contents = null) + { + parent::showBox($this->info_box_head, $this->info_box_contents); + } + +} + +?> diff --git a/htdocs/core/class/dolgraph.class.php b/htdocs/core/class/dolgraph.class.php index b604ae4f8f0..f0598a00a82 100644 --- a/htdocs/core/class/dolgraph.class.php +++ b/htdocs/core/class/dolgraph.class.php @@ -244,7 +244,7 @@ class DolGraph /** * Set type * - * @param array $type Array with type for each serie + * @param array $type Array with type for each serie. Example: array('pie'), array('lines',...,'bars') * @return void */ function SetType($type) @@ -739,11 +739,16 @@ class DolGraph /** - * Build a graph onto disk using JFlot library - * $graph_data = array(array('labelxA',yA),array('labelxB',yB)); - * $graph_data = array(array('labelxA',yA1,...,yAn),array('labelxB',yB1,...yBn)); // when there is n value to show for each x - * $legend = array("Val1",...,"Valn"); // list of n series name - * + * Build a graph onto disk using JFlot library. Input when calling this method should be: + * $this->data = array(array( 0=>'labelxA', 1=>yA), array('labelxB',yB)); or + * $this->data = array(array('label'=>'labelxA','data'=>yA), array('labelxB',yB)); + * $this->data = array(array(0=>'labelxA',1=>yA1,...,n=>yAn), array('labelxB',yB1,...yBn)); // when there is n value to show for each x + * $this->legend= array("Val1",...,"Valn"); // list of n series name + * $this->type = array('bars',...'lines'); or array('pie') + * $this->mode = 'depth' ??? + * $this->bgcolorgrid + * $this->datacolor + * * @param string $file Image file name to use if we save onto disk * @param string $fileurl Url path to show image if saved onto disk * @return void @@ -757,10 +762,12 @@ class DolGraph // On boucle sur chaque lot de donnees $legends=array(); $nblot=count($this->data[0])-1; // -1 to remove legend + if ($nblot < 0) dol_print_error('Bad value for property ->data. Must be set by mydolgraph->SetData before callinf mydolgrapgh->draw'); $firstlot=0; // work with line but not with bars //if ($nblot > 2) $firstlot = ($nblot - 2); // We limit nblot to 2 because jflot can't manage more than 2 bars on same x + $i=$firstlot; $serie=array(); while ($i < $nblot) @@ -784,19 +791,26 @@ class DolGraph //print "Lot de donnees $i
"; //print_r($values); //print '
'; + // TODO Replace with json_encode $serie[$i]="var d".$i." = [];\n"; $x=0; foreach($newvalues as $key => $val) { - if (isset($val)) $serie[$i].="d".$i.".push([".$x.", ".$val."]);\n"; + if (isset($this->type[$firstlot]) && $this->type[$firstlot] == 'pie') + { + if (isset($val)) $serie[$i].='d'.$i.'.push({"label":"'.dol_escape_js($legends[$x]).'", "data":'.$val.'});'."\n"; + } + else + { + if (isset($val)) $serie[$i].='d'.$i.'.push(['.$x.', '.$val.']);'."\n"; + } $x++; } $i++; } - $tag=dol_escape_htmltag(dol_string_unaccent(dol_string_nospecial(basename($file),'_',array('-','.')))); - + $this->_stringtoshow =''."\n"; if (! empty($this->title)) $this->_stringtoshow.='
'.$this->title.'
'; $this->_stringtoshow.='
'."\n"; @@ -810,90 +824,145 @@ class DolGraph } $this->_stringtoshow.="\n"; - $this->_stringtoshow.=' - function showTooltip_'.$tag.'(x, y, contents) { - $(\'
\' + contents + \'
\').css( { - position: \'absolute\', - display: \'none\', - top: y + 5, - left: x + 5, - border: \'1px solid #ddd\', - padding: \'2px\', - \'background-color\': \'#ffe\', - width: 200, - opacity: 0.80 - }).appendTo("body").fadeIn(20); - } + // Special case for Graph of type 'pie' + if (isset($this->type[$firstlot]) && $this->type[$firstlot] == 'pie') + { + $datacolor=array(); + foreach($this->datacolor as $val) $datacolor[]="#".sprintf("%02x%02x%02x",$val[0],$val[1],$val[2]); + + $this->_stringtoshow.= ' + function plotWithOptions_'.$tag.'() { + $.plot($("#placeholder_'.$tag.'"), d0, + { + series: { + pie: { + show: true, + radius: 3/4, + label: { + show: true, + radius: 3/4, + formatter: function(label, series) { + var percent=Math.round(series.percent); + var number=series.data[0][1]; + return \''; + $this->_stringtoshow.='
'; + if ($url) $this->_stringtoshow.=''; + $this->_stringtoshow.='\'+'.($showlegend?'number':'label+\'
\'+number'); + if (! empty($showpercent)) $this->_stringtoshow.='+\'
\'+percent+\'%\''; + $this->_stringtoshow.='+\''; + if ($url) $this->_stringtoshow.='
'; + $this->_stringtoshow.='
\'; + }, + background: { + opacity: 0.5, + color: \'#000000\' + } + } + } + }, + zoom: { + interactive: true + }, + pan: { + interactive: true + },'; + if (count($datacolor)) + { + $this->_stringtoshow.='colors: '.(! empty($data['seriescolor']) ? json_encode($data['seriescolor']) : json_encode($datacolor)).','; + } + $this->_stringtoshow.='legend: {show: '.($showlegend?'true':'false').', position: \'ne\' } + }); + }'."\n"; + } + // Other cases, graph of type 'bars', 'lines' + else + { + // Add code to support tooltips + $this->_stringtoshow.=' + function showTooltip_'.$tag.'(x, y, contents) { + $(\'
\' + contents + \'
\').css({ + position: \'absolute\', + display: \'none\', + top: y + 5, + left: x + 5, + border: \'1px solid #ddd\', + padding: \'2px\', + \'background-color\': \'#ffe\', + width: 200, + opacity: 0.80 + }).appendTo("body").fadeIn(20); + } + + var previousPoint = null; + $("#placeholder_'.$tag.'").bind("plothover", function (event, pos, item) { + $("#x").text(pos.x.toFixed(2)); + $("#y").text(pos.y.toFixed(2)); + + if (item) { + if (previousPoint != item.dataIndex) { + previousPoint = item.dataIndex; + + $("#tooltip").remove(); + /* console.log(item); */ + var x = item.datapoint[0].toFixed(2); + var y = item.datapoint[1].toFixed(2); + var z = item.series.xaxis.ticks[item.dataIndex].label; + + showTooltip_'.$tag.'(item.pageX, item.pageY, + item.series.label + "
" + z + " => " + y); + } + } + else { + $("#tooltip_'.$tag.'").remove(); + previousPoint = null; + } + }); + '; + + $this->_stringtoshow.='var stack = null, steps = false;'."\n"; + + $this->_stringtoshow.='function plotWithOptions_'.$tag.'() {'."\n"; + $this->_stringtoshow.='$.plot($("#placeholder_'.$tag.'"), [ '."\n"; + $i=$firstlot; + while ($i < $nblot) + { + if ($i > $firstlot) $this->_stringtoshow.=', '."\n"; + $color=sprintf("%02x%02x%02x",$this->datacolor[$i][0],$this->datacolor[$i][1],$this->datacolor[$i][2]); + $this->_stringtoshow.='{ '; + if (! isset($this->type[$i]) || $this->type[$i] == 'bars') $this->_stringtoshow.='bars: { show: true, align: "'.($i==$firstlot?'center':'left').'", barWidth: 0.5 }, '; + if (isset($this->type[$i]) && $this->type[$i] == 'lines') $this->_stringtoshow.='lines: { show: true, fill: false }, '; + $this->_stringtoshow.='color: "#'.$color.'", label: "'.(isset($this->Legend[$i]) ? dol_escape_js($this->Legend[$i]) : '').'", data: d'.$i.' }'; + $i++; + } + $this->_stringtoshow.="\n".' ], { series: { stack: stack, lines: { fill: false, steps: steps }, bars: { barWidth: 0.6 } }'."\n"; + + // Xaxis + $this->_stringtoshow.=', xaxis: { ticks: ['."\n"; + $x=0; + foreach($this->data as $key => $valarray) + { + if ($x > 0) $this->_stringtoshow.=', '."\n"; + $this->_stringtoshow.= ' ['.$x.', "'.$valarray[0].'"]'; + $x++; + } + $this->_stringtoshow.='] }'."\n"; + + // Yaxis + $this->_stringtoshow.=', yaxis: { min: '.$this->MinValue.', max: '.($this->MaxValue).' }'."\n"; + + // Background color + $color1=sprintf("%02x%02x%02x",$this->bgcolorgrid[0],$this->bgcolorgrid[0],$this->bgcolorgrid[2]); + $color2=sprintf("%02x%02x%02x",$this->bgcolorgrid[0],$this->bgcolorgrid[1],$this->bgcolorgrid[2]); + $this->_stringtoshow.=', grid: { hoverable: true, backgroundColor: { colors: ["#'.$color1.'", "#'.$color2.'"] } }'."\n"; + //$this->_stringtoshow.=', shadowSize: 20'."\n"; TODO Uncommet this + $this->_stringtoshow.='});'."\n"; + $this->_stringtoshow.='}'."\n"; + + } - var previousPoint = null; - $("#placeholder_'.$tag.'").bind("plothover", function (event, pos, item) { - $("#x").text(pos.x.toFixed(2)); - $("#y").text(pos.y.toFixed(2)); - - if (item) { - if (previousPoint != item.dataIndex) { - previousPoint = item.dataIndex; - - $("#tooltip").remove(); - /* console.log(item); */ - var x = item.datapoint[0].toFixed(2); - var y = item.datapoint[1].toFixed(2); - var z = item.series.xaxis.ticks[item.dataIndex].label; - - showTooltip_'.$tag.'(item.pageX, item.pageY, - item.series.label + "
" + z + " => " + y); - } - } - else { - $("#tooltip_'.$tag.'").remove(); - previousPoint = null; - } - - }); - '; - - $this->_stringtoshow.='var stack = null, steps = false;'."\n"; - - $this->_stringtoshow.='function plotWithOptions_'.$tag.'() {'."\n"; - $this->_stringtoshow.='$.plot($("#placeholder_'.$tag.'"), [ '."\n"; - $i=$firstlot; - while ($i < $nblot) - { - if ($i > $firstlot) $this->_stringtoshow.=', '."\n"; - $color=sprintf("%02x%02x%02x",$this->datacolor[$i][0],$this->datacolor[$i][1],$this->datacolor[$i][2]); - $this->_stringtoshow.='{ '; - if (! isset($this->type[$i]) || $this->type[$i] == 'bars') $this->_stringtoshow.='bars: { show: true, align: "'.($i==$firstlot?'center':'left').'", barWidth: 0.5 }, '; - if (isset($this->type[$i]) && $this->type[$i] == 'lines') $this->_stringtoshow.='lines: { show: true, fill: false }, '; - $this->_stringtoshow.='color: "#'.$color.'", label: "'.(isset($this->Legend[$i]) ? dol_escape_js($this->Legend[$i]) : '').'", data: d'.$i.' }'; - $i++; - } - $this->_stringtoshow.="\n".' ], { series: { stack: stack, lines: { fill: false, steps: steps }, bars: { barWidth: 0.6 } }'."\n"; - - // Xaxis - $this->_stringtoshow.=', xaxis: { ticks: ['."\n"; - $x=0; - foreach($this->data as $key => $valarray) - { - if ($x > 0) $this->_stringtoshow.=', '."\n"; - $this->_stringtoshow.= ' ['.$x.', "'.$valarray[0].'"]'; - $x++; - } - $this->_stringtoshow.='] }'."\n"; - - // Yaxis - $this->_stringtoshow.=', yaxis: { min: '.$this->MinValue.', max: '.($this->MaxValue).' }'."\n"; - - // Background color - $color1=sprintf("%02x%02x%02x",$this->bgcolorgrid[0],$this->bgcolorgrid[0],$this->bgcolorgrid[2]); - $color2=sprintf("%02x%02x%02x",$this->bgcolorgrid[0],$this->bgcolorgrid[1],$this->bgcolorgrid[2]); - $this->_stringtoshow.=', grid: { hoverable: true, backgroundColor: { colors: ["#'.$color1.'", "#'.$color2.'"] } }'."\n"; - //$this->_stringtoshow.=', shadowSize: 20'."\n"; TODO Uncommet this - $this->_stringtoshow.='});'."\n"; - $this->_stringtoshow.='}'."\n"; - - $this->_stringtoshow.='plotWithOptions_'.$tag.'();'."\n"; - $this->_stringtoshow.='});'."\n"; - $this->_stringtoshow.=''."\n"; + $this->_stringtoshow.='plotWithOptions_'.$tag.'();'."\n"; + $this->_stringtoshow.='});'."\n"; + $this->_stringtoshow.=''."\n"; } diff --git a/htdocs/core/class/stats.class.php b/htdocs/core/class/stats.class.php index 24e4ffa8980..a1375ffb79f 100644 --- a/htdocs/core/class/stats.class.php +++ b/htdocs/core/class/stats.class.php @@ -73,14 +73,14 @@ abstract class Stats } else { - 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."); + 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."); } } // Load file into $data if ($foundintocache) // Cache file found and is not too old { - dol_syslog(get_class($this).'_'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate."."); + dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate."."); $data = dol_json_decode(file_get_contents($newpathofdestfile), true); } else @@ -109,7 +109,7 @@ abstract class Stats // Save cache file if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1)) { - dol_syslog(get_class($this).'_'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk."); + dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk."); if (! dol_is_dir($conf->user->dir_temp)) dol_mkdir($conf->user->dir_temp); $fp = fopen($newpathofdestfile, 'w'); fwrite($fp, dol_json_encode($data)); @@ -164,14 +164,14 @@ abstract class Stats } else { - 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."); + 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."); } } // Load file into $data if ($foundintocache) // Cache file found and is not too old { - dol_syslog(get_class($this).'_'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate."."); + dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate."."); $data = dol_json_decode(file_get_contents($newpathofdestfile), true); } else @@ -200,7 +200,7 @@ abstract class Stats // Save cache file if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1)) { - dol_syslog(get_class($this).'_'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk."); + dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk."); if (! dol_is_dir($conf->user->dir_temp)) dol_mkdir($conf->user->dir_temp); $fp = fopen($newpathofdestfile, 'w'); fwrite($fp, dol_json_encode($data)); @@ -250,7 +250,80 @@ abstract class Stats return $data; } + /** + * Return count, and sum of products + * + * @param int $year Year + * @param int $cachedelay Delay we accept for cache file (0=No read, no save of cache, -1=No read but save) + * @return array Array of values + */ + function getAllByProductEntry($year,$cachedelay=0) + { + global $conf,$user,$langs; + $datay=array(); + + // Search into cache + if (! empty($cachedelay)) + { + include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php'; + } + + $newpathofdestfile=$conf->user->dir_temp.'/'.get_class($this).'_'.__FUNCTION__.'_'.(empty($this->cachefilesuffix)?'':$this->cachefilesuffix.'_').$langs->defaultlang.'_user'.$user->id.'.cache'; + $newmask='0644'; + + $nowgmt = dol_now(); + + $foundintocache=0; + if ($cachedelay > 0) + { + $filedate=dol_filemtime($newpathofdestfile); + if ($filedate >= ($nowgmt - $cachedelay)) + { + $foundintocache=1; + + $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$filedate; + } + else + { + 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."); + } + } + + // Load file into $data + if ($foundintocache) // Cache file found and is not too old + { + dol_syslog(get_class($this).'::'.__FUNCTION__." read data from cache file ".$newpathofdestfile." ".$filedate."."); + $data = dol_json_decode(file_get_contents($newpathofdestfile), true); + } + else + { + $data=$this->getAllByProduct($year); + // $data[$i][]=$datay[$year][$i][1]; // set yval for x=i + } + + // Save cache file + if (empty($foundintocache) && ($cachedelay > 0 || $cachedelay == -1)) + { + dol_syslog(get_class($this).'::'.__FUNCTION__." save cache file ".$newpathofdestfile." onto disk."); + if (! dol_is_dir($conf->user->dir_temp)) dol_mkdir($conf->user->dir_temp); + $fp = fopen($newpathofdestfile, 'w'); + fwrite($fp, dol_json_encode($data)); + fclose($fp); + if (! empty($conf->global->MAIN_UMASK)) $newmask=$conf->global->MAIN_UMASK; + @chmod($newpathofdestfile, octdec($newmask)); + + $this->_lastfetchdate[get_class($this).'_'.__FUNCTION__]=$nowgmt; + } + + return $data; + } + + + // Here we have low level of shared code called by XxxStats.class.php + + /** * Return nb of elements by year * @@ -261,7 +334,7 @@ abstract class Stats { $result = array(); - dol_syslog(get_class($this)."::_getNbByYear sql=".$sql); + dol_syslog(get_class($this).'::'.__FUNCTION__." sql=".$sql); $resql=$this->db->query($sql); if ($resql) { @@ -291,7 +364,7 @@ abstract class Stats { $result = array(); - dol_syslog(get_class($this)."::_getAllByYear sql=".$sql); + dol_syslog(get_class($this).'::'.__FUNCTION__." sql=".$sql); $resql=$this->db->query($sql); if ($resql) { @@ -327,7 +400,7 @@ abstract class Stats $result=array(); $res=array(); - dol_syslog(get_class($this)."::_getNbByMonth sql=".$sql); + dol_syslog(get_class($this).'::'.__FUNCTION__." sql=".$sql); $resql=$this->db->query($sql); if ($resql) { @@ -378,7 +451,7 @@ abstract class Stats $result=array(); $res=array(); - dol_syslog(get_class($this)."::_getAmountByMonth sql=".$sql); + dol_syslog(get_class($this).'::'.__FUNCTION__." sql=".$sql); $resql=$this->db->query($sql); if ($resql) @@ -425,7 +498,7 @@ abstract class Stats $result=array(); $res=array(); - dol_syslog(get_class($this)."::_getAverageByMonth sql=".$sql); + dol_syslog(get_class($this).'::'.__FUNCTION__." sql=".$sql); $resql=$this->db->query($sql); if ($resql) { @@ -458,6 +531,38 @@ abstract class Stats return $data; } + + + /** + * Return number or total of product refs + * + * @param string $sql SQL + * @return array + */ + function _getAllByProduct($sql) + { + $result=array(); + $res=array(); + + dol_syslog(get_class($this).'::'.__FUNCTION__." sql=".$sql); + $resql=$this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i = 0; $j = 0; + while ($i < $num) + { + $row = $this->db->fetch_row($resql); + $result[$j] = array($row[0],$row[1]); // Ref of product, nb + $j++; + $i++; + } + $this->db->free($resql); + } + else dol_print_error($this->db); + + return $result; + } } ?> diff --git a/htdocs/core/modules/modProduct.class.php b/htdocs/core/modules/modProduct.class.php index 47fe114480c..3d789d52b72 100644 --- a/htdocs/core/modules/modProduct.class.php +++ b/htdocs/core/modules/modProduct.class.php @@ -92,7 +92,8 @@ class modProduct extends DolibarrModules $this->boxes = array(); $this->boxes[0][1] = "box_produits.php"; $this->boxes[1][1] = "box_produits_alerte_stock.php"; - + $this->boxes[2][1] = "box_graph_product_distribution.php"; + // Permissions $this->rights = array(); $this->rights_class = 'produit'; diff --git a/htdocs/langs/en_US/boxes.lang b/htdocs/langs/en_US/boxes.lang index 0255624cc56..7ac3a28102a 100644 --- a/htdocs/langs/en_US/boxes.lang +++ b/htdocs/langs/en_US/boxes.lang @@ -83,4 +83,6 @@ BoxSuppliersInvoicesPerMonth=Supplier invoices per month BoxCustomersOrdersPerMonth=Customer orders per month BoxSuppliersOrdersPerMonth=Supplier orders per month BoxProposalsPerMonth=Proposals per month -NoTooLowStockProducts=No product under the low stock limit \ No newline at end of file +NoTooLowStockProducts=No product under the low stock limit +BoxProductDistribution=Products/Services distribution +BoxProductDistributionFor=Distribution of %s for %s \ No newline at end of file diff --git a/htdocs/langs/fr_FR/boxes.lang b/htdocs/langs/fr_FR/boxes.lang index 9686d947625..2a163ff382e 100644 --- a/htdocs/langs/fr_FR/boxes.lang +++ b/htdocs/langs/fr_FR/boxes.lang @@ -84,3 +84,5 @@ BoxCustomersOrdersPerMonth=Commandes clients par mois BoxSuppliersOrdersPerMonth=Commandes fournisseurs par mois BoxProposalsPerMonth=Proposition par mois NoTooLowStockProducts=Pas de produits sous le seuil de stock minimal +BoxProductDistribution=Répartition des produis/services +BoxProductDistributionFor=Répartition des %s pour les %s \ No newline at end of file