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.='
';
+ 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.='\';
+ },
+ 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