From f2bd1461b24b81dddc47eb9e63606cae6a95b4af Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rapha=C3=ABl=20Doursenaud?= <rdoursenaud@gpcsolutions.fr>
Date: Sat, 20 Apr 2013 07:33:10 +0200
Subject: [PATCH] Improved reports

Added amount without VAT for "CREANCES-DETTES" mode
Added turnover by products/services
Added categories filter for users and products/services
Added period management
Added list sorting
---
 htdocs/compta/stats/cabyuser.php        | 248 ++++++++++++++-----
 htdocs/compta/stats/casoc.php           | 302 ++++++++++++++++++------
 htdocs/core/menus/standard/eldy.lib.php |   2 +
 htdocs/langs/en_US/compta.lang          |   3 +-
 htdocs/langs/en_US/errors.lang          |   3 +-
 htdocs/langs/fr_FR/compta.lang          |   3 +-
 htdocs/langs/fr_FR/errors.lang          |   3 +-
 7 files changed, 428 insertions(+), 136 deletions(-)

diff --git a/htdocs/compta/stats/cabyuser.php b/htdocs/compta/stats/cabyuser.php
index 86f16cf3684..d4ef827ad0f 100644
--- a/htdocs/compta/stats/cabyuser.php
+++ b/htdocs/compta/stats/cabyuser.php
@@ -2,6 +2,7 @@
 /* Copyright (C) 2001-2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
  * Copyright (C) 2004-2011 Laurent Destailleur  <eldy@users.sourceforge.net>
  * Copyright (C) 2005-2009 Regis Houssin        <regis.houssin@capnetworks.com>
+ * Copyright (C) 2013      Antoine Iauch        <aiauch@gpcsolutions.fr>
  *
  * 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
@@ -46,6 +47,12 @@ if (! $sortfield) $sortfield="name";
 // Date range
 $year=GETPOST("year");
 $month=GETPOST("month");
+$date_startyear = GETPOST("date_startyear");
+$date_startmonth = GETPOST("date_startmonth");
+$date_startday = GETPOST("date_startday");
+$date_endyear = GETPOST("date_endyear");
+$date_endmonth = GETPOST("date_endmonth");
+$date_endday = GETPOST("date_endday");
 if (empty($year))
 {
 	$year_current = strftime("%Y",dol_now());
@@ -89,9 +96,34 @@ if (empty($date_start) || empty($date_end)) // We define date_start and date_end
 else
 {
 	// TODO We define q
-
 }
 
+$commonparams=array();
+$commonparams['modecompta']=$modecompta;
+$commonparams['sortorder'] = $sortorder;
+$commonparams['sortfield'] = $sortfield;
+
+$headerparams = array();
+$headerparams['date_startyear'] = $date_startyear;
+$headerparams['date_startmonth'] = $date_startmonth;
+$headerparams['date_startday'] = $date_startday;
+$headerparams['date_endyear'] = $date_endyear;
+$headerparams['date_endmonth'] = $date_endmonth;
+$headerparams['date_endday'] = $date_endday;
+$headerparams['q'] = $q;
+
+$tableparams = array();
+$tableparams['search_categ'] = $selected_cat;
+$tableparams['subcat'] = ($subcat === true)?'yes':'';
+
+// Adding common parameters
+$allparams = array_merge($commonparams, $headerparams, $tableparams);
+$headerparams = array_merge($commonparams, $headerparams);
+$tableparams = array_merge($commonparams, $tableparams);
+
+foreach($allparams as $key => $value) {
+    $paramslink .= '&' . $key . '=' . $value;
+}
 
 /*
  * View
@@ -102,9 +134,8 @@ llxHeader();
 
 $form=new Form($db);
 
-// Affiche en-tete du rapport
-if ($modecompta=="CREANCES-DETTES")
-{
+// Show report header
+if ($modecompta=="CREANCES-DETTES") {
     $nom=$langs->trans("SalesTurnover").', '.$langs->trans("ByUserAuthorOfInvoice");
     $nom.='<br>('.$langs->trans("SeeReportInInputOutputMode",'<a href="'.$_SERVER["PHP_SELF"].'?year='.$year.'&modecompta=RECETTES-DEPENSES">','</a>').')';
 	$period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1);
@@ -114,8 +145,7 @@ if ($modecompta=="CREANCES-DETTES")
 	else  $description.= $langs->trans("DepositsAreIncluded");
     $builddate=time();
     //$exportlink=$langs->trans("NotYetAvailable");
-}
-else {
+} else {
     $nom=$langs->trans("SalesTurnover").', '.$langs->trans("ByUserAuthorOfInvoice");
     $nom.='<br>('.$langs->trans("SeeReportInDueDebtMode",'<a href="'.$_SERVER["PHP_SELF"].'?year='.$year.'&modecompta=CREANCES-DETTES">','</a>').')';
 	$period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1);
@@ -131,31 +161,42 @@ if (! empty($modecompta)) $moreparam['modecompta']=$modecompta;
 report_header($nom,$nomlink,$period,$periodlink,$description,$builddate,$exportlink,$moreparam);
 
 
-// Charge tableau
-$catotal=0;
-if ($modecompta == 'CREANCES-DETTES')
+// Show array
+print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
+// Extra parameters management
+foreach($headerparams as $key => $value)
 {
+     print '<input type="hidden" name="'.$key.'" value="'.$value.'">';
+}
+
+$catotal=0;
+if ($modecompta == 'CREANCES-DETTES') {
     $sql = "SELECT u.rowid as rowid, u.lastname as name, u.firstname as firstname, sum(f.total) as amount, sum(f.total_ttc) as amount_ttc";
     $sql.= " FROM ".MAIN_DB_PREFIX."user as u";
     $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as f ON f.fk_user_author = u.rowid";
     $sql.= " WHERE f.fk_statut in (1,2)";
-	if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) $sql.= " AND f.type IN (0,1,2)";
-	else $sql.= " AND f.type IN (0,1,2,3)";
-	if ($date_start && $date_end) $sql.= " AND f.datef >= '".$db->idate($date_start)."' AND f.datef <= '".$db->idate($date_end)."'";
-}
-else
-{
+	if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
+	    $sql.= " AND f.type IN (0,1,2)";
+	    } else {
+	    $sql.= " AND f.type IN (0,1,2,3)";
+	}
+	if ($date_start && $date_end) {
+	    $sql.= " AND f.datef >= '".$db->idate($date_start)."' AND f.datef <= '".$db->idate($date_end)."'";
+	}
+} else {
     /*
      * Liste des paiements (les anciens paiements ne sont pas vus par cette requete car, sur les
      * vieilles versions, ils n'etaient pas lies via paiement_facture. On les ajoute plus loin)
      */
-	$sql = "SELECT u.rowid as rowid, u.lastname as name, u.firstname as firstname, sum(pf.amount) as amount_ttc";
+	$sql = "SELECT u.rowid as rowid, u.lastname as name, u.firstname as firstname, sum(DISTINCT pf.amount) as amount_ttc";
 	$sql.= " FROM ".MAIN_DB_PREFIX."user as u" ;
 	$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."facture as f ON f.fk_user_author = u.rowid ";
 	$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."paiement_facture as pf ON pf.fk_facture = f.rowid";
 	$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."paiement as p ON p.rowid = pf.fk_paiement";
 	$sql.= " WHERE 1=1";
-	if ($date_start && $date_end) $sql.= " AND p.datep >= '".$db->idate($date_start)."' AND p.datep <= '".$db->idate($date_end)."'";
+	if ($date_start && $date_end) {
+	    $sql.= " AND p.datep >= '".$db->idate($date_start)."' AND p.datep <= '".$db->idate($date_end)."'";
+	}
 }
 $sql.= " AND f.entity = ".$conf->entity;
 if ($socid) $sql.= " AND f.fk_soc = ".$socid;
@@ -163,27 +204,25 @@ $sql .= " GROUP BY u.rowid, u.lastname, u.firstname";
 $sql .= " ORDER BY u.rowid";
 
 $result = $db->query($sql);
-if ($result)
-{
+if ($result) {
     $num = $db->num_rows($result);
     $i=0;
-    while ($i < $num)
-    {
+    while ($i < $num) {
          $obj = $db->fetch_object($result);
-         $amount[$obj->rowid] = $obj->amount_ttc;
-         $name[$obj->rowid] = $obj->lastname.' '.$obj->firstname;
-         $catotal+=$obj->amount_ttc;
-         $i++;
+	 $amount_ht[$obj->rowid] = $obj->amount;
+	 $amount[$obj->rowid] = $obj->amount_ttc;
+	 $name[$obj->rowid] = $obj->name.' '.$obj->firstname;
+	 $catotal_ht+=$obj->amount;
+	 $catotal+=$obj->amount_ttc;
+	 $i++;
     }
-}
-else {
+} else {
     dol_print_error($db);
 }
 
-// On ajoute les paiements ancienne version, non lies par paiement_facture donc sans user
-if ($modecompta != 'CREANCES-DETTES')
-{
-    $sql = "SELECT -1 as rowidx, '' as lastname, '' as firstname, sum(p.amount) as amount_ttc";
+// Adding old-version payments, non-bound by "paiement_facture" then without User
+if ($modecompta != 'CREANCES-DETTES') {
+    $sql = "SELECT -1 as rowidx, '' as name, '' as firstname, sum(DISTINCT p.amount) as amount_ttc";
     $sql.= " FROM ".MAIN_DB_PREFIX."bank as b";
     $sql.= ", ".MAIN_DB_PREFIX."bank_account as ba";
     $sql.= ", ".MAIN_DB_PREFIX."paiement as p";
@@ -192,42 +231,86 @@ if ($modecompta != 'CREANCES-DETTES')
     $sql.= " AND p.fk_bank = b.rowid";
     $sql.= " AND b.fk_account = ba.rowid";
     $sql.= " AND ba.entity = ".$conf->entity;
-	if ($date_start && $date_end) $sql.= " AND p.datep >= '".$db->idate($date_start)."' AND p.datep <= '".$db->idate($date_end)."'";
+	if ($date_start && $date_end) {
+	    $sql.= " AND p.datep >= '".$db->idate($date_start)."' AND p.datep <= '".$db->idate($date_end)."'";
+	}
     $sql.= " GROUP BY rowidx, name, firstname";
     $sql.= " ORDER BY rowidx";
 
     $result = $db->query($sql);
-    if ($result)
-    {
+    if ($result) {
         $num = $db->num_rows($result);
         $i=0;
-        while ($i < $num)
-        {
+        while ($i < $num) {
             $obj = $db->fetch_object($result);
             $amount[$obj->rowidx] = $obj->amount_ttc;
-            $name[$obj->rowidx] = $obj->lastname.' '.$obj->firstname;
+            $name[$obj->rowidx] = $obj->name.' '.$obj->firstname;
             $catotal+=$obj->amount_ttc;
             $i++;
         }
-    }
-    else {
+    } else {
         dol_print_error($db);
     }
 }
 
-
 $i = 0;
 print "<table class=\"noborder\" width=\"100%\">";
 print "<tr class=\"liste_titre\">";
-print_liste_field_titre($langs->trans("User"),$_SERVER["PHP_SELF"],"name","",'&amp;year='.($year).'&modecompta='.$modecompta,"",$sortfield,$sortorder);
-print_liste_field_titre($langs->trans("AmountTTC"),$_SERVER["PHP_SELF"],"amount_ttc","",'&amp;year='.($year).'&modecompta='.$modecompta,'align="right"',$sortfield,$sortorder);
-print_liste_field_titre($langs->trans("Percentage"),$_SERVER["PHP_SELF"],"amount_ttc","",'&amp;year='.($year).'&modecompta='.$modecompta,'align="right"',$sortfield,$sortorder);
-print_liste_field_titre($langs->trans("OtherStatistics"),$_SERVER["PHP_SELF"],"","","",'align="center" width="20%"');
+print_liste_field_titre(
+	$langs->trans("User"),
+	$_SERVER["PHP_SELF"],
+	"name",
+	"",
+	$paramslink,
+	"",
+	$sortfield,
+	$sortorder
+	);
+if ($modecompta == 'CREANCES-DETTES') {
+    print_liste_field_titre(
+           $langs->trans('AmountHT'),
+           $_SERVER["PHP_SELF"],
+           "amount_ht",
+           "",
+           $paramslink,
+           'align="right"',
+           $sortfield,
+           $sortorder
+	   );
+    } else {
+	print '<td colspan="1"></td>';
+}
+print_liste_field_titre(
+	$langs->trans("AmountTTC"),
+	$_SERVER["PHP_SELF"],
+	"amount_ttc",
+	"",
+	$paramslink,
+	'align="right"',
+	$sortfield,
+	$sortorder
+	);
+print_liste_field_titre(
+	$langs->trans("Percentage"),
+	$_SERVER["PHP_SELF"],"amount_ttc",
+	"",
+	$paramslink,
+	'align="right"',
+	$sortfield,
+	$sortorder
+	);
+print_liste_field_titre(
+	$langs->trans("OtherStatistics"),
+	$_SERVER["PHP_SELF"],
+	"",
+	"",
+	"",
+	'align="center" width="20%"'
+	);
 print "</tr>\n";
 $var=true;
 
-if (count($amount))
-{
+if (count($amount)) {
     $arrayforsort=$name;
 
     // We define arrayforsort
@@ -239,6 +322,14 @@ if (count($amount))
         arsort($name);
         $arrayforsort=$name;
     }
+    if ($sortfield == 'amount_ht' && $sortorder == 'asc') {
+        asort($amount_ht);
+        $arrayforsort=$amount_ht;
+    }
+    if ($sortfield == 'amount_ht' && $sortorder == 'desc') {
+        arsort($amount_ht);
+        $arrayforsort=$amount_ht;
+    }
     if ($sortfield == 'amount_ttc' && $sortorder == 'asc') {
         asort($amount);
         $arrayforsort=$amount;
@@ -248,8 +339,7 @@ if (count($amount))
         $arrayforsort=$amount;
     }
 
-    foreach($arrayforsort as $key => $value)
-    {
+    foreach($arrayforsort as $key => $value) {
         $var=!$var;
         print "<tr ".$bc[$var].">";
 
@@ -257,23 +347,44 @@ if (count($amount))
         $fullname=$name[$key];
         if ($key >= 0) {
             $linkname='<a href="'.DOL_URL_ROOT.'/user/fiche.php?id='.$key.'">'.img_object($langs->trans("ShowUser"),'user').' '.$fullname.'</a>';
-        }
-        else {
+        } else {
             $linkname=$langs->trans("PaymentsNotLinkedToUser");
         }
         print "<td>".$linkname."</td>\n";
 
-        // Amount
+	// Amount w/o VAT
         print '<td align="right">';
         if ($modecompta != 'CREANCES-DETTES')
         {
-            if ($key > 0) print '<a href="'.DOL_URL_ROOT.'/compta/paiement/liste.php?userid='.$key.'">';
-            else print '<a href="'.DOL_URL_ROOT.'/compta/paiement/liste.php?userid=-1">';
+            if ($key > 0) {
+		print '<a href="'.DOL_URL_ROOT.'/compta/paiement/liste.php?userid='.$key.'">';
+		} else {
+		    print '<a href="'.DOL_URL_ROOT.'/compta/paiement/liste.php?userid=-1">';
+		}
+        } else {
+            if ($key > 0) {
+		print '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?userid='.$key.'">';
+	    } else {
+		print '<a href="#">';
+	    }
+	    print price($amount_ht[$key]);
         }
-        else
-        {
-            if ($key > 0) print '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?userid='.$key.'">';
-            else print '<a href="#">';
+        print '</td>';
+
+        // Amount with VAT
+        print '<td align="right">';
+        if ($modecompta != 'CREANCES-DETTES') {
+            if ($key > 0) {
+		print '<a href="'.DOL_URL_ROOT.'/compta/paiement/liste.php?userid='.$key.'">';
+		} else {
+		    print '<a href="'.DOL_URL_ROOT.'/compta/paiement/liste.php?userid=-1">';
+		}
+        } else {
+            if ($key > 0) {
+		print '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?userid='.$key.'">';
+	    } else {
+		print '<a href="#">';
+	    }
         }
         print price($amount[$key]);
         print '</td>';
@@ -283,17 +394,30 @@ if (count($amount))
 
         // Other stats
         print '<td align="center">';
-        if (! empty($conf->propal->enabled) && $key>0) print '&nbsp;<a href="'.DOL_URL_ROOT.'/comm/propal/stats/index.php?userid='.$key.'">'.img_picto($langs->trans("ProposalStats"),"stats").'</a>&nbsp;';
-        if (! empty($conf->commande->enabled) && $key>0) print '&nbsp;<a href="'.DOL_URL_ROOT.'/commande/stats/index.php?userid='.$key.'">'.img_picto($langs->trans("OrderStats"),"stats").'</a>&nbsp;';
-        if (! empty($conf->facture->enabled) && $key>0) print '&nbsp;<a href="'.DOL_URL_ROOT.'/compta/facture/stats/index.php?userid='.$key.'">'.img_picto($langs->trans("InvoiceStats"),"stats").'</a>&nbsp;';
+        if (! empty($conf->propal->enabled) && $key>0) {
+	    print '&nbsp;<a href="'.DOL_URL_ROOT.'/comm/propal/stats/index.php?userid='.$key.'">'.img_picto($langs->trans("ProposalStats"),"stats").'</a>&nbsp;';
+	}
+        if (! empty($conf->commande->enabled) && $key>0) {
+	    print '&nbsp;<a href="'.DOL_URL_ROOT.'/commande/stats/index.php?userid='.$key.'">'.img_picto($langs->trans("OrderStats"),"stats").'</a>&nbsp;';
+	}
+        if (! empty($conf->facture->enabled) && $key>0) {
+	    print '&nbsp;<a href="'.DOL_URL_ROOT.'/compta/facture/stats/index.php?userid='.$key.'">'.img_picto($langs->trans("InvoiceStats"),"stats").'</a>&nbsp;';
+	}
         print '</td>';
-
         print "</tr>\n";
         $i++;
     }
 
     // Total
-    print '<tr class="liste_total"><td>'.$langs->trans("Total").'</td><td align="right">'.price($catotal).'</td><td>&nbsp;</td>';
+    print '<tr class="liste_total">';
+    print '<td>'.$langs->trans("Total").'</td>';
+    if ($modecompta != 'CREANCES-DETTES') {
+	print '<td colspan="1"></td>';
+    } else {
+	    print '<td align="right">'.price($catotal_ht).'</td>';
+    }
+    print '<td align="right">'.price($catotal).'</td>';
+    print '<td>&nbsp;</td>';
     print '<td>&nbsp;</td>';
     print '</tr>';
 
diff --git a/htdocs/compta/stats/casoc.php b/htdocs/compta/stats/casoc.php
index b658cf636d6..32d527cc779 100644
--- a/htdocs/compta/stats/casoc.php
+++ b/htdocs/compta/stats/casoc.php
@@ -3,6 +3,7 @@
  * Copyright (C) 2004-2011 Laurent Destailleur   <eldy@users.sourceforge.net>
  * Copyright (C) 2005-2009 Regis Houssin         <regis.houssin@capnetworks.com>
  * Copyright (C) 2007      Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
+ * Copyright (C) 2013      Antoine Iauch         <aiauch@gpcsolutions.fr>
  *
  * 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
@@ -27,8 +28,10 @@ require '../../main.inc.php';
 require_once DOL_DOCUMENT_ROOT.'/core/lib/report.lib.php';
 require_once DOL_DOCUMENT_ROOT.'/core/lib/tax.lib.php';
 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
 
 $langs->load("companies");
+$langs->load("categories");
 
 // Define modecompta ('CREANCES-DETTES' or 'RECETTES-DEPENSES')
 $modecompta = $conf->global->COMPTA_MODE;
@@ -41,6 +44,13 @@ if (! $sortfield) $sortfield="nom";
 
 $socid = GETPOST('socid','int');
 
+// Category
+$selected_cat = (int)GETPOST('search_categ', 'int');
+$subcat = false;
+if (GETPOST('subcat', 'alpha') === 'yes') {
+    $subcat = true;
+}
+
 // Security check
 if ($user->societe_id > 0) $socid = $user->societe_id;
 if (! empty($conf->comptabilite->enabled)) $result=restrictedArea($user,'compta','','','resultat');
@@ -49,6 +59,12 @@ if (! empty($conf->accounting->enabled)) $result=restrictedArea($user,'accountin
 // Date range
 $year=GETPOST("year");
 $month=GETPOST("month");
+$date_startyear = GETPOST("date_startyear");
+$date_startmonth = GETPOST("date_startmonth");
+$date_startday = GETPOST("date_startday");
+$date_endyear = GETPOST("date_endyear");
+$date_endmonth = GETPOST("date_endmonth");
+$date_endday = GETPOST("date_endday");
 if (empty($year))
 {
 	$year_current = strftime("%Y",dol_now());
@@ -92,10 +108,34 @@ if (empty($date_start) || empty($date_end)) // We define date_start and date_end
 else
 {
 	// TODO We define q
-
 }
 
-
+$commonparams=array();
+$commonparams['modecompta']=$modecompta;
+$commonparams['sortorder'] = $sortorder;
+$commonparams['sortfield'] = $sortfield;
+
+$headerparams = array();
+$headerparams['date_startyear'] = $date_startyear;
+$headerparams['date_startmonth'] = $date_startmonth;
+$headerparams['date_startday'] = $date_startday;
+$headerparams['date_endyear'] = $date_endyear;
+$headerparams['date_endmonth'] = $date_endmonth;
+$headerparams['date_endday'] = $date_endday;
+$headerparams['q'] = $q;
+
+$tableparams = array();
+$tableparams['search_categ'] = $selected_cat;
+$tableparams['subcat'] = ($subcat === true)?'yes':'';
+
+// Adding common parameters
+$allparams = array_merge($commonparams, $headerparams, $tableparams);
+$headerparams = array_merge($commonparams, $headerparams);
+$tableparams = array_merge($commonparams, $tableparams);
+
+foreach($allparams as $key => $value) {
+    $paramslink .= '&' . $key . '=' . $value;
+}
 
 /*
  * View
@@ -105,8 +145,9 @@ llxHeader();
 
 $form=new Form($db);
 $thirdparty_static=new Societe($db);
+$formother = new FormOther($db);
 
-// Affiche en-tete de rapport
+// Show report header
 if ($modecompta=="CREANCES-DETTES")
 {
 	$nom=$langs->trans("SalesTurnover").', '.$langs->trans("ByThirdParties");
@@ -118,8 +159,7 @@ if ($modecompta=="CREANCES-DETTES")
 	else  $description.= $langs->trans("DepositsAreIncluded");
 	$builddate=time();
 	//$exportlink=$langs->trans("NotYetAvailable");
-}
-else {
+} else {
 	$nom=$langs->trans("SalesTurnover").', '.$langs->trans("ByThirdParties");
 	$nom.='<br>('.$langs->trans("SeeReportInDueDebtMode",'<a href="'.$_SERVER["PHP_SELF"].'?year='.$year.'&modecompta=CREANCES-DETTES">','</a>').')';
 	$period=$form->select_date($date_start,'date_start',0,0,0,'',1,0,1).' - '.$form->select_date($date_end,'date_end',0,0,0,'',1,0,1);
@@ -129,26 +169,44 @@ else {
 	$builddate=time();
 	//$exportlink=$langs->trans("NotYetAvailable");
 }
-$moreparam=array();
-if (! empty($modecompta)) $moreparam['modecompta']=$modecompta;
-report_header($nom,$nomlink,$period,$periodlink,$description,$builddate,$exportlink,$moreparam);
 
+report_header($nom,$nomlink,$period,$periodlink,$description,$builddate,$exportlink,$tableparams);
 
-// Charge tableau
+
+// Show Array
 $catotal=0;
-if ($modecompta == 'CREANCES-DETTES')
-{
-	$sql = "SELECT s.rowid as socid, s.nom as name, sum(f.total) as amount, sum(f.total_ttc) as amount_ttc";
+if ($modecompta == 'CREANCES-DETTES') {
+	$sql = "SELECT DISTINCT s.rowid as socid, s.nom as name,";
+	$sql.= " sum(DISTINCT f.total) as amount, sum(DISTINCT f.total_ttc) as amount_ttc";
 	$sql.= " FROM ".MAIN_DB_PREFIX."societe as s";
-	$sql.= ", ".MAIN_DB_PREFIX."facture as f";
+	$sql.= " JOIN ".MAIN_DB_PREFIX."facture as f";
+	if ($selected_cat === -2) {
+	    $sql.= " LEFT OUTER JOIN ".MAIN_DB_PREFIX."categorie_societe as cs ON s.rowid = cs.fk_societe";
+	}
+	if ($selected_cat && $selected_cat !== -2) {
+	    $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."categorie as c ON c.rowid = ".$selected_cat;
+	    if (subcat) {
+		$sql.=" OR c.fk_parent = " . $selected_cat;
+	    }
+	     $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."categorie_societe as cs ON cs.fk_categorie = c.rowid";
+	}
 	$sql.= " WHERE f.fk_statut in (1,2)";
-	if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) $sql.= " AND f.type IN (0,1,2)";
-	else $sql.= " AND f.type IN (0,1,2,3)";
+	if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
+	    $sql.= " AND f.type IN (0,1,2)";
+	} else {
+	    $sql.= " AND f.type IN (0,1,2,3)";
+	}
 	$sql.= " AND f.fk_soc = s.rowid";
-	if ($date_start && $date_end) $sql.= " AND f.datef >= '".$db->idate($date_start)."' AND f.datef <= '".$db->idate($date_end)."'";
-}
-else
-{
+	if ($date_start && $date_end) {
+	    $sql.= " AND f.datef >= '".$db->idate($date_start)."' AND f.datef <= '".$db->idate($date_end)."'";
+	}
+	if ($selected_cat === -2) {
+	    $sql.=" AND cs.fk_societe is null";
+	}
+	if ($selected_cat && $selected_cat !== -2) {
+	    $sql.= " AND cs.fk_societe = s.rowid";
+	}
+    } else {
 	/*
 	 * Liste des paiements (les anciens paiements ne sont pas vus par cette requete car, sur les
 	 * vieilles versions, ils n'etaient pas lies via paiement_facture. On les ajoute plus loin)
@@ -161,7 +219,9 @@ else
 	$sql .= " WHERE p.rowid = pf.fk_paiement";
 	$sql.= " AND pf.fk_facture = f.rowid";
 	$sql.= " AND f.fk_soc = s.rowid";
-	if ($date_start && $date_end) $sql.= " AND p.datep >= '".$db->idate($date_start)."' AND p.datep <= '".$db->idate($date_end)."'";
+	if ($date_start && $date_end) {
+	    $sql.= " AND p.datep >= '".$db->idate($date_start)."' AND p.datep <= '".$db->idate($date_end)."'";
+	}
 }
 $sql.= " AND f.entity = ".$conf->entity;
 if ($socid) $sql.= " AND f.fk_soc = ".$socid;
@@ -169,27 +229,26 @@ $sql.= " GROUP BY s.rowid, s.nom";
 $sql.= " ORDER BY s.rowid";
 
 $result = $db->query($sql);
-if ($result)
-{
+if ($result) {
 	$num = $db->num_rows($result);
 	$i=0;
-	while ($i < $num)
-	{
+	while ($i < $num) {
 		$obj = $db->fetch_object($result);
-		$amount[$obj->socid] += $obj->amount_ttc;
-		$name[$obj->socid] = $obj->name;
-		$catotal+=$obj->amount_ttc;
-		$i++;
+	        $amount_ht[$obj->socid] = $obj->amount;
+	        $amount[$obj->socid] = $obj->amount_ttc;
+	        $name[$obj->socid] = $obj->name.' '.$obj->firstname;
+	        $catotal_ht+=$obj->amount;
+	        $catotal+=$obj->amount_ttc;
+	        $i++;
+
 	}
-}
-else {
+} else {
 	dol_print_error($db);
 }
 
 // On ajoute les paiements anciennes version, non lies par paiement_facture
-if ($modecompta != 'CREANCES-DETTES')
-{
-	$sql = "SELECT '0' as socid, 'Autres' as name, sum(p.amount) as amount_ttc";
+if ($modecompta != 'CREANCES-DETTES') {
+	$sql = "SELECT '0' as socid, 'Autres' as name, sum(DISTINCT p.amount) as amount_ttc";
 	$sql.= " FROM ".MAIN_DB_PREFIX."bank as b";
 	$sql.= ", ".MAIN_DB_PREFIX."bank_account as ba";
 	$sql.= ", ".MAIN_DB_PREFIX."paiement as p";
@@ -203,20 +262,17 @@ if ($modecompta != 'CREANCES-DETTES')
 	$sql.= " ORDER BY name";
 
 	$result = $db->query($sql);
-	if ($result)
-	{
+	if ($result) {
 		$num = $db->num_rows($result);
 		$i=0;
-		while ($i < $num)
-		{
+		while ($i < $num) {
 			$obj = $db->fetch_object($result);
 			$amount[$obj->rowid] += $obj->amount_ttc;
 			$name[$obj->rowid] = $obj->name;
 			$catotal+=$obj->amount_ttc;
 			$i++;
 		}
-	}
-	else {
+	} else {
 		dol_print_error($db);
 	}
 }
@@ -224,20 +280,87 @@ if ($modecompta != 'CREANCES-DETTES')
 
 // Show array
 $i = 0;
+print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
+// Extra parameters management
+foreach($headerparams as $key => $value)
+{
+     print '<input type="hidden" name="'.$key.'" value="'.$value.'">';
+}
 print "<table class=\"noborder\" width=\"100%\">";
-print "<tr class=\"liste_titre\">";
-print_liste_field_titre($langs->trans("Company"),$_SERVER["PHP_SELF"],"nom","",'&amp;year='.($year).'&modecompta='.$modecompta,"",$sortfield,$sortorder);
-print_liste_field_titre($langs->trans("AmountTTC"),$_SERVER["PHP_SELF"],"amount_ttc","",'&amp;year='.($year).'&modecompta='.$modecompta,'align="right"',$sortfield,$sortorder);
-print_liste_field_titre($langs->trans("Percentage"),$_SERVER["PHP_SELF"],"amount_ttc","",'&amp;year='.($year).'&modecompta='.$modecompta,'align="right"',$sortfield,$sortorder);
-print_liste_field_titre($langs->trans("OtherStatistics"),$_SERVER["PHP_SELF"],"","","",'align="center" width="20%"');
+    // Category filter
+print '<tr class="liste_titre">';
+print '<td>';
+print $langs->trans("Category") . ': ' . $formother->select_categories(2, $selected_cat, 'search_categ', true);
+print ' ';
+print $langs->trans("SubCats") . '? ';
+print '<input type="checkbox" name="subcat" value="yes"';
+if ($subcat) {
+    print ' checked="checked"';
+}
+print'></td>';
+print '<td colspan="4" align="right">';
+print '<input type="image" class="liste_titre" name="button_search" src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/search.png"  value="'.dol_escape_htmltag($langs->trans("Search")).'" title="'.dol_escape_htmltag($langs->trans("Search")).'">';
+print '</td>';
+print '</tr>';
+    // Array titles
+print "<tr class='liste_titre'>";
+print_liste_field_titre(
+	$langs->trans("Company"),
+	$_SERVER["PHP_SELF"],
+	"nom",
+	"",
+	$paramslink,
+	"",
+	$sortfield,$sortorder
+	);
+if ($modecompta == 'CREANCES-DETTES') {
+    print_liste_field_titre(
+           $langs->trans('AmountHT'),
+           $_SERVER["PHP_SELF"],
+           "amount_ht",
+           "",
+           $paramslink,
+           'align="right"',
+           $sortfield,
+           $sortorder
+	);
+    } else {
+	print '<td colspan="1"></td>';
+}
+print_liste_field_titre(
+	$langs->trans("AmountTTC"),
+	$_SERVER["PHP_SELF"],
+	"amount_ttc",
+	"",
+	$paramslink,
+	'align="right"',
+	$sortfield,
+	$sortorder
+	);
+print_liste_field_titre(
+	$langs->trans("Percentage"),
+	$_SERVER["PHP_SELF"],
+	"amount_ttc",
+	"",
+	$paramslink,
+	'align="right"',
+	$sortfield,
+	$sortorder
+	);
+print_liste_field_titre(
+	$langs->trans("OtherStatistics"),
+	$_SERVER["PHP_SELF"],
+	"",
+	"",
+	"",
+	'align="center" width="20%"'
+	);
 print "</tr>\n";
 $var=true;
 
-if (count($amount))
-{
+if (count($amount)) {
 	$arrayforsort=$name;
-
-	// On definit tableau arrayforsort
+	// Defining array arrayforsort
 	if ($sortfield == 'nom' && $sortorder == 'asc') {
 		asort($name);
 		$arrayforsort=$name;
@@ -246,6 +369,14 @@ if (count($amount))
 		arsort($name);
 		$arrayforsort=$name;
 	}
+	if ($sortfield == 'amount_ht' && $sortorder == 'asc') {
+	    asort($amount_ht);
+	    $arrayforsort=$amount_ht;
+	}
+	if ($sortfield == 'amount_ht' && $sortorder == 'desc') {
+	    arsort($amount_ht);
+	    $arrayforsort=$amount_ht;
+	}
 	if ($sortfield == 'amount_ttc' && $sortorder == 'asc') {
 		asort($amount);
 		$arrayforsort=$amount;
@@ -255,8 +386,7 @@ if (count($amount))
 		$arrayforsort=$amount;
 	}
 
-	foreach($arrayforsort as $key=>$value)
-	{
+	foreach($arrayforsort as $key=>$value) {
 		$var=!$var;
 		print "<tr ".$bc[$var].">";
 
@@ -267,23 +397,43 @@ if (count($amount))
 		    $thirdparty_static->name=$fullname;
 		    $thirdparty_static->client=1;
 		    $linkname=$thirdparty_static->getNomUrl(1,'customer');
-		}
-		else {
+		} else {
 			$linkname=$langs->trans("PaymentsNotLinkedToInvoice");
 		}
 		print "<td>".$linkname."</td>\n";
 
-		// Amount
+		// Amount w/o VAT
 		print '<td align="right">';
-		if ($modecompta != 'CREANCES-DETTES')
-		{
-    		if ($key > 0) print '<a href="'.DOL_URL_ROOT.'/compta/paiement/liste.php?socid='.$key.'">';
-    		else print '<a href="'.DOL_URL_ROOT.'/compta/paiement/liste.php?orphelins=1">';
+		if ($modecompta != 'CREANCES-DETTES') {
+                    if ($key > 0) {
+			print '<a href="'.DOL_URL_ROOT.'/compta/paiement/liste.php?userid='.$key.'">';
+		    } else {
+			print '<a href="'.DOL_URL_ROOT.'/compta/paiement/liste.php?userid=-1">';
+		    }
+		} else {
+		    if ($key > 0) {
+			print '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?userid='.$key.'">';
+		    } else {
+			print '<a href="#">';
+		    }
+		print price($amount_ht[$key]);
 		}
-		else
-		{
-            if ($key > 0) print '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?socid='.$key.'">';
-            else print '<a href="#">';
+		print '</td>';
+
+		// Amount with VAT
+		print '<td align="right">';
+		if ($modecompta != 'CREANCES-DETTES') {
+                    if ($key > 0) {
+                        print '<a href="'.DOL_URL_ROOT.'/compta/paiement/liste.php?socid='.$key.'">';
+		    } else {
+			print '<a href="'.DOL_URL_ROOT.'/compta/paiement/liste.php?orphelins=1">';
+		    }
+		} else {
+                    if ($key > 0) {
+                        print '<a href="'.DOL_URL_ROOT.'/compta/facture/list.php?socid='.$key.'">';
+		    } else {
+			print '<a href="#">';
+		    }
 		}
 		print price($amount[$key]);
 		print '</a>';
@@ -294,17 +444,30 @@ if (count($amount))
 
         // Other stats
         print '<td align="center">';
-        if (! empty($conf->propal->enabled) && $key>0) print '&nbsp;<a href="'.DOL_URL_ROOT.'/comm/propal/stats/index.php?socid='.$key.'">'.img_picto($langs->trans("ProposalStats"),"stats").'</a>&nbsp;';
-        if (! empty($conf->commande->enabled) && $key>0) print '&nbsp;<a href="'.DOL_URL_ROOT.'/commande/stats/index.php?socid='.$key.'">'.img_picto($langs->trans("OrderStats"),"stats").'</a>&nbsp;';
-        if (! empty($conf->facture->enabled) && $key>0) print '&nbsp;<a href="'.DOL_URL_ROOT.'/compta/facture/stats/index.php?socid='.$key.'">'.img_picto($langs->trans("InvoiceStats"),"stats").'</a>&nbsp;';
+        if (! empty($conf->propal->enabled) && $key>0) {
+	    print '&nbsp;<a href="'.DOL_URL_ROOT.'/comm/propal/stats/index.php?socid='.$key.'">'.img_picto($langs->trans("ProposalStats"),"stats").'</a>&nbsp;';
+	}
+        if (! empty($conf->commande->enabled) && $key>0) {
+	    print '&nbsp;<a href="'.DOL_URL_ROOT.'/commande/stats/index.php?socid='.$key.'">'.img_picto($langs->trans("OrderStats"),"stats").'</a>&nbsp;';
+	}
+        if (! empty($conf->facture->enabled) && $key>0) {
+	    print '&nbsp;<a href="'.DOL_URL_ROOT.'/compta/facture/stats/index.php?socid='.$key.'">'.img_picto($langs->trans("InvoiceStats"),"stats").'</a>&nbsp;';
+	}
         print '</td>';
-
-		print "</tr>\n";
-		$i++;
+	print "</tr>\n";
+	$i++;
 	}
 
 	// Total
-	print '<tr class="liste_total"><td>'.$langs->trans("Total").'</td><td align="right">'.price($catotal).'</td><td>&nbsp;</td>';
+	print '<tr class="liste_total">';
+	print '<td>'.$langs->trans("Total").'</td>';
+	if ($modecompta != 'CREANCES-DETTES') {
+	    print '<td colspan="1"></td>';
+	} else {
+	    print '<td align="right">'.price($catotal_ht).'</td>';
+	}
+	print '<td align="right">'.price($catotal).'</td>';
+	print '<td>&nbsp;</td>';
 	print '<td>&nbsp;</td>';
 	print '</tr>';
 
@@ -312,8 +475,7 @@ if (count($amount))
 }
 
 print "</table>";
-print '<br>';
-
+print '</form>';
 
 llxFooter();
 
diff --git a/htdocs/core/menus/standard/eldy.lib.php b/htdocs/core/menus/standard/eldy.lib.php
index 641925f42a9..f4d7f3581db 100644
--- a/htdocs/core/menus/standard/eldy.lib.php
+++ b/htdocs/core/menus/standard/eldy.lib.php
@@ -852,6 +852,8 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
 				*/
 				if (empty($leftmenu) || $leftmenu=="ca") $newmenu->add("/compta/stats/casoc.php?leftmenu=ca",$langs->trans("ByCompanies"),2,$user->rights->compta->resultat->lire||$user->rights->accounting->comptarapport->lire);
 				if (empty($leftmenu) || $leftmenu=="ca") $newmenu->add("/compta/stats/cabyuser.php?leftmenu=ca",$langs->trans("ByUsers"),2,$user->rights->compta->resultat->lire||$user->rights->accounting->comptarapport->lire);
+				if (empty($leftmenu) || $leftmenu=="ca") $newmenu->add("/compta/stats/cabyprodserv.php?leftmenu=ca", $langs->trans("ByProductsAndServices"),2,$user->rights->compta->resultat->lire||$user->rights->accounting->comptarapport->lire);
+
 
 				// Journaux
 				//if ($leftmenu=="ca") $newmenu->add("/compta/journaux/index.php?leftmenu=ca",$langs->trans("Journaux"),1,$user->rights->compta->resultat->lire||$user->rights->accounting->comptarapport->lire);
diff --git a/htdocs/langs/en_US/compta.lang b/htdocs/langs/en_US/compta.lang
index 5e3a36e5051..a1b3cba5004 100644
--- a/htdocs/langs/en_US/compta.lang
+++ b/htdocs/langs/en_US/compta.lang
@@ -152,4 +152,5 @@ Pcg_type=Pcg type
 Pcg_subtype=Pcg subtype
 InvoiceLinesToDispatch=Invoice lines to dispatch
 InvoiceDispatched=Dispatched invoices
-AccountancyDashboard=Accountancy summary
\ No newline at end of file
+AccountancyDashboard=Accountancy summary
+ByProductsAndServices=By products and services
diff --git a/htdocs/langs/en_US/errors.lang b/htdocs/langs/en_US/errors.lang
index 48795ba2ce7..3cfee357c81 100644
--- a/htdocs/langs/en_US/errors.lang
+++ b/htdocs/langs/en_US/errors.lang
@@ -136,4 +136,5 @@ WarningLockFileDoesNotExists=Warning, once setup is finished, you must disable i
 WarningUntilDirRemoved=All security warnings (visible by admin users only) will remain active as long as the vulnerability is present (or that constant MAIN_REMOVE_INSTALL_WARNING is added in Setup->Other setup).
 WarningCloseAlways=Warning, closing is done even if amount differs between source and target elements. Enable this feature with caution.
 WarningUsingThisBoxSlowDown=Warning, using this box slow down seriously all pages showing the box.
-WarningClickToDialUserSetupNotComplete=Setup of ClickToDial information for your user are not complete (see tab ClickToDial onto your user card). 
\ No newline at end of file
+WarningClickToDialUserSetupNotComplete=Setup of ClickToDial information for your user are not complete (see tab ClickToDial onto your user card).
+WarningNotRelevant=Irrelevant operation for this dataset
diff --git a/htdocs/langs/fr_FR/compta.lang b/htdocs/langs/fr_FR/compta.lang
index 1491a7820f4..227b6b734ad 100644
--- a/htdocs/langs/fr_FR/compta.lang
+++ b/htdocs/langs/fr_FR/compta.lang
@@ -163,4 +163,5 @@ Pcg_type=Classe de compte
 Pcg_subtype=Sous classe de compte
 InvoiceLinesToDispatch=Lignes de factures à ventiler
 InvoiceDispatched=Factures ventilées
-AccountancyDashboard=Synthèse compta/tréso
\ No newline at end of file
+AccountancyDashboard=Synthèse compta/tréso
+ByProductsAndServices=Par produits et services
diff --git a/htdocs/langs/fr_FR/errors.lang b/htdocs/langs/fr_FR/errors.lang
index f1316e03d13..5081f66d5f7 100644
--- a/htdocs/langs/fr_FR/errors.lang
+++ b/htdocs/langs/fr_FR/errors.lang
@@ -137,4 +137,5 @@ WarningLockFileDoesNotExists=Attention, une fois l'installation terminée, les o
 WarningUntilDirRemoved=Les alertes de sécurité sont visibles par les administrateurs uniquement et resteront actives tant que la vulnérabilité sera avérée (ou que la constante MAIN_REMOVE_INSTALL_WARNING aura été définie dans Configuration->Divers)
 WarningCloseAlways=Attention, la fermeture se fait même lorsque le montant diffère. N'activez cette fonctionnalité qu'en connaissance de cause.
 WarningUsingThisBoxSlowDown=Attention, l'utilisation de cette boite provoque de sérieux ralentissement des pages affichant cette boite.
-WarningClickToDialUserSetupNotComplete=La configuration ClickToDial pour votre compte utilisateur n'est pas complète (voir l'onglet ClickToDial sur votre fiche utilisateur)
\ No newline at end of file
+WarningClickToDialUserSetupNotComplete=La configuration ClickToDial pour votre compte utilisateur n'est pas complète (voir l'onglet ClickToDial sur votre fiche utilisateur)
+WarningNotRelevant=Opération non pertinente pour cet ensemble de données
-- 
GitLab