dolibarr 21.0.0-beta
index.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (c) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2012 Marcos García <marcosgdf@gmail.com>
5 * Copyright (C) 2013 Juanjo Menent <jmenent@2byte.es>
6 * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
7 * Copyright (C) 2020 Maxime DEMAREST <maxime@indelog.fr>
8 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
9 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23 */
24
31// Load Dolibarr environment
32require '../../../main.inc.php';
33require_once DOL_DOCUMENT_ROOT.'/core/class/dolgraph.class.php';
34require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
35require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
36require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
37require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facturestats.class.php';
38if (isModEnabled('category')) {
39 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
40}
41
52
53// Load translation files required by the page
54$langs->loadLangs(array('bills', 'companies', 'other'));
55
56$mode = GETPOST("mode") ? GETPOST("mode") : 'customer';
57
58$hookmanager->initHooks(array('invoicestats', 'globalcard'));
59
60if ($mode == 'customer' && !$user->hasRight('facture', 'lire')) {
62}
63if ($mode == 'supplier' && !$user->hasRight('fournisseur', 'facture', 'lire')) {
65}
66
67$object_status = GETPOST('object_status', 'intcomma');
68$typent_id = GETPOSTINT('typent_id');
69$categ_id = GETPOSTINT('categ_id');
70
71$userid = GETPOSTINT('userid');
72$socid = GETPOSTINT('socid');
73$select_categ_categ_id = GETPOST('select_categ_categ_id', 'array');
74// Security check
75if ($user->socid > 0) {
76 $action = '';
77 $socid = $user->socid;
78}
79
80$parameters = array();
81$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
82if ($reshook < 0) {
83 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
84}
85
86$nowyear = dol_print_date(dol_now('gmt'), "%Y", 'gmt');
87$year = GETPOST('year') > 0 ? GETPOSTINT('year') : $nowyear;
88$startyear = $year - (!getDolGlobalString('MAIN_STATS_GRAPHS_SHOW_N_YEARS') ? 2 : max(1, min(10, getDolGlobalString('MAIN_STATS_GRAPHS_SHOW_N_YEARS'))));
89$endyear = $year;
90
91
92/*
93 * View
94 */
95if (isModEnabled('category')) {
96 $langs->load('categories');
97}
98$form = new Form($db);
99$formcompany = new FormCompany($db);
100$formother = new FormOther($db);
101
102llxHeader();
103
104$picto = 'bill';
105$title = $langs->trans("BillsStatistics");
106$dir = $conf->facture->dir_temp;
107
108if ($mode == 'supplier') {
109 $picto = 'supplier_invoice';
110 $title = $langs->trans("BillsStatisticsSuppliers");
111 $dir = $conf->fournisseur->facture->dir_temp;
112}
113
114
115print load_fiche_titre($title, '', $picto);
116
117dol_mkdir($dir);
118
119$stats = new FactureStats($db, $socid, $mode, ($userid > 0 ? $userid : 0), ($typent_id > 0 ? $typent_id : 0), ($categ_id > 0 ? $categ_id : 0));
120if ($mode == 'customer') {
121 if ($object_status != '' && $object_status >= 0) {
122 $stats->where .= ' AND f.fk_statut IN ('.$db->sanitize($object_status).')';
123 }
124 if (is_array($select_categ_categ_id) && !empty($select_categ_categ_id)) {
125 $stats->from .= ' LEFT JOIN '.MAIN_DB_PREFIX.'categorie_societe as cat ON (f.fk_soc = cat.fk_soc)';
126 $stats->where .= ' AND cat.fk_categorie IN ('.$db->sanitize(implode(',', $select_categ_categ_id)).')';
127 }
128}
129if ($mode == 'supplier') {
130 if ($object_status != '' && $object_status >= 0) {
131 $stats->where .= ' AND f.fk_statut IN ('.$db->sanitize($object_status).')';
132 }
133 if (is_array($select_categ_categ_id) && !empty($select_categ_categ_id)) {
134 $stats->from .= ' LEFT JOIN '.MAIN_DB_PREFIX.'categorie_fournisseur as cat ON (f.fk_soc = cat.fk_soc)';
135 $stats->where .= ' AND cat.fk_categorie IN ('.$db->sanitize(implode(',', $select_categ_categ_id)).')';
136 }
137}
138
139// Build graphic number of object
140// $data = array(array('Lib',val1,val2,val3),...)
141$data = $stats->getNbByMonthWithPrevYear($endyear, $startyear);
142//var_dump($data);
143
144$filenamenb = $dir."/invoicesnbinyear-".$year.".png";
145$fileurlnb = '';
146if ($mode == 'customer') {
147 $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=billstats&file=invoicesnbinyear-'.$year.'.png';
148}
149if ($mode == 'supplier') {
150 $fileurlnb = DOL_URL_ROOT.'/viewimage.php?modulepart=billstatssupplier&file=invoicesnbinyear-'.$year.'.png';
151}
152
153$px1 = new DolGraph();
154$mesg = $px1->isGraphKo();
155if (!$mesg) {
156 $px1->SetData($data);
157 $i = $startyear;
158 $legend = array();
159 while ($i <= $endyear) {
160 $legend[] = $i;
161 $i++;
162 }
163 $px1->SetLegend($legend);
164 $px1->SetMaxValue($px1->GetCeilMaxValue());
165 $px1->SetWidth($WIDTH);
166 $px1->SetHeight($HEIGHT);
167 $px1->SetYLabel($langs->trans("NumberOfBills"));
168 $px1->SetShading(3);
169 $px1->SetHorizTickIncrement(1);
170 $px1->mode = 'depth';
171 $px1->SetTitle($langs->trans("NumberOfBillsByMonth"));
172
173 $px1->draw($filenamenb, $fileurlnb);
174}
175
176// Build graphic amount of object
177$data = $stats->getAmountByMonthWithPrevYear($endyear, $startyear);
178//var_dump($data);
179// $data = array(array('Lib',val1,val2,val3),...)
180
181$filenameamount = $dir."/invoicesamountinyear-".$year.".png";
182$fileurlamount = '';
183if ($mode == 'customer') {
184 $fileurlamount = DOL_URL_ROOT.'/viewimage.php?modulepart=billstats&amp;file=invoicesamountinyear-'.$year.'.png';
185}
186if ($mode == 'supplier') {
187 $fileurlamount = DOL_URL_ROOT.'/viewimage.php?modulepart=billstatssupplier&amp;file=invoicesamountinyear-'.$year.'.png';
188}
189
190$px2 = new DolGraph();
191$mesg = $px2->isGraphKo();
192if (!$mesg) {
193 $px2->SetData($data);
194 $i = $startyear;
195 $legend = array();
196 while ($i <= $endyear) {
197 $legend[] = $i;
198 $i++;
199 }
200 $px2->SetLegend($legend);
201 $px2->SetMaxValue($px2->GetCeilMaxValue());
202 $px2->SetMinValue(min(0, $px2->GetFloorMinValue()));
203 $px2->SetWidth($WIDTH);
204 $px2->SetHeight($HEIGHT);
205 $px2->SetYLabel($langs->trans("AmountOfBills"));
206 $px2->SetShading(3);
207 $px2->SetHorizTickIncrement(1);
208 $px2->mode = 'depth';
209 $px2->SetTitle($langs->trans("AmountOfBillsByMonthHT"));
210
211 $px2->draw($filenameamount, $fileurlamount);
212}
213
214
215$data = $stats->getAverageByMonthWithPrevYear($endyear, $startyear);
216
217$fileurl_avg = '';
218if (!$user->hasRight('societe', 'client', 'voir')) {
219 $filename_avg = $dir.'/ordersaverage-'.$user->id.'-'.$year.'.png';
220 if ($mode == 'customer') {
221 $fileurl_avg = DOL_URL_ROOT.'/viewimage.php?modulepart=orderstats&file=ordersaverage-'.$user->id.'-'.$year.'.png';
222 }
223 if ($mode == 'supplier') {
224 $fileurl_avg = DOL_URL_ROOT.'/viewimage.php?modulepart=orderstatssupplier&file=ordersaverage-'.$user->id.'-'.$year.'.png';
225 }
226} else {
227 $filename_avg = $dir.'/ordersaverage-'.$year.'.png';
228 if ($mode == 'customer') {
229 $fileurl_avg = DOL_URL_ROOT.'/viewimage.php?modulepart=orderstats&file=ordersaverage-'.$year.'.png';
230 }
231 if ($mode == 'supplier') {
232 $fileurl_avg = DOL_URL_ROOT.'/viewimage.php?modulepart=orderstatssupplier&file=ordersaverage-'.$year.'.png';
233 }
234}
235
236$px3 = new DolGraph();
237$mesg = $px3->isGraphKo();
238if (!$mesg) {
239 $px3->SetData($data);
240 $i = $startyear;
241 $legend = array();
242 while ($i <= $endyear) {
243 $legend[] = $i;
244 $i++;
245 }
246 $px3->SetLegend($legend);
247 $px3->SetYLabel($langs->trans("AmountAverage"));
248 $px3->SetMaxValue($px3->GetCeilMaxValue());
249 $px3->SetMinValue($px3->GetFloorMinValue());
250 $px3->SetWidth($WIDTH);
251 $px3->SetHeight($HEIGHT);
252 $px3->SetShading(3);
253 $px3->SetHorizTickIncrement(1);
254 $px3->mode = 'depth';
255 $px3->SetTitle($langs->trans("AmountAverage"));
256
257 $px3->draw($filename_avg, $fileurl_avg);
258}
259
260
261// Show array
262$data = $stats->getAllByYear();
263$arrayyears = array();
264foreach ($data as $val) {
265 $arrayyears[$val['year']] = $val['year'];
266}
267if (!count($arrayyears)) {
268 $arrayyears[$nowyear] = $nowyear;
269}
270
271
272$h = 0;
273$head = array();
274$head[$h][0] = DOL_URL_ROOT.'/compta/facture/stats/index.php?mode='.urlencode($mode);
275$head[$h][1] = $langs->trans("ByMonthYear");
276$head[$h][2] = 'byyear';
277$h++;
278
279if ($mode == 'customer') {
280 $type = 'invoice_stats';
281}
282if ($mode == 'supplier') {
283 $type = 'supplier_invoice_stats';
284}
285
286complete_head_from_modules($conf, $langs, null, $head, $h, $type);
287
288print dol_get_fiche_head($head, 'byyear', '', -1);
289
290print '<div class="fichecenter"><div class="fichethirdleft">';
291
292
293// Show filter box
294print '<form name="stats" method="POST" action="'.$_SERVER["PHP_SELF"].'">';
295print '<input type="hidden" name="token" value="'.newToken().'">';
296print '<input type="hidden" name="mode" value="'.$mode.'">';
297
298print '<table class="noborder centpercent">';
299print '<tr class="liste_titre"><td class="liste_titre" colspan="2">'.$langs->trans("Filter").'</td></tr>';
300// Company
301print '<tr><td>'.$langs->trans("ThirdParty").'</td><td>';
302$filter = '';
303if ($mode == 'customer') {
304 $filter = '(s.client:IN:1,2,3)';
305}
306if ($mode == 'supplier') {
307 $filter = '(s.fournisseur:=:1)';
308}
309print img_picto('', 'company', 'class="pictofixedwidth"');
310print $form->select_company($socid, 'socid', $filter, 1, 0, 0, array(), 0, 'widthcentpercentminusx maxwidth300');
311print '</td></tr>';
312
313// ThirdParty Type
314print '<tr><td>'.$langs->trans("ThirdPartyType").'</td><td>';
315$sortparam_typent = (!getDolGlobalString('SOCIETE_SORT_ON_TYPEENT') ? 'ASC' : $conf->global->SOCIETE_SORT_ON_TYPEENT); // NONE means we keep sort of original array, so we sort on position. ASC, means next function will sort on label.
316print $form->selectarray("typent_id", $formcompany->typent_array(0), $typent_id, 1, 0, 0, '', 0, 0, 0, $sortparam_typent, '', 1);
317if ($user->admin) {
318 print ' '.info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
319}
320print '</td></tr>';
321
322// Category
323if (isModEnabled('category')) {
324 $cat_type = '';
325 $cat_label = '';
326 if ($mode == 'customer') {
327 $cat_type = Categorie::TYPE_CUSTOMER;
328 $cat_label = $langs->trans("Category").' '.lcfirst($langs->trans("Customer"));
329 }
330 if ($mode == 'supplier') {
331 $cat_type = Categorie::TYPE_SUPPLIER;
332 $cat_label = $langs->trans("Category").' '.lcfirst($langs->trans("Supplier"));
333 }
334 print '<tr><td>'.$cat_label.'</td><td>';
335 $cate_arbo = $form->select_all_categories($cat_type, '', 'parent', 0, 0, 1);
336 print img_picto('', 'category', 'class="pictofixedwidth"');
337 print $form->multiselectarray('select_categ_categ_id', $cate_arbo, GETPOST('select_categ_categ_id', 'array'), 0, 0, 'widthcentpercentminusx maxwidth300');
338 //print $formother->select_categories($cat_type, $categ_id, 'categ_id', true);
339 print '</td></tr>';
340}
341
342// User
343print '<tr><td>'.$langs->trans("CreatedBy").'</td><td>';
344print img_picto('', 'user', 'class="pictofixedwidth"');
345print $form->select_dolusers($userid, 'userid', 1, '', 0, '', '', 0, 0, 0, '', 0, '', 'widthcentpercentminusx maxwidth300');
346print '</td></tr>';
347// Status
348print '<tr><td>'.$langs->trans("Status").'</td><td>';
349if ($mode == 'customer') {
350 $liststatus = array('0' => $langs->trans("BillStatusDraft"), '1' => $langs->trans("BillStatusNotPaid"), '2' => $langs->trans("BillStatusPaid"), '1,2' => $langs->trans("BillStatusNotPaid").' / '.$langs->trans("BillStatusPaid"), '3' => $langs->trans("BillStatusCanceled"));
351 print $form->selectarray('object_status', $liststatus, $object_status, 1);
352}
353if ($mode == 'supplier') {
354 $liststatus = array('0' => $langs->trans("BillStatusDraft"), '1' => $langs->trans("BillStatusNotPaid"), '2' => $langs->trans("BillStatusPaid"));
355 print $form->selectarray('object_status', $liststatus, $object_status, 1);
356}
357print '</td></tr>';
358// Year
359print '<tr><td>'.$langs->trans("Year").'</td><td>';
360if (!in_array($year, $arrayyears)) {
361 $arrayyears[$year] = $year;
362}
363if (!in_array($nowyear, $arrayyears)) {
364 $arrayyears[$nowyear] = $nowyear;
365}
366arsort($arrayyears);
367print $form->selectarray('year', $arrayyears, $year, 0, 0, 0, '', 0, 0, 0, '', 'width75');
368print '</td></tr>';
369print '<tr><td class="center" colspan="2"><input type="submit" name="submit" class="button small" value="'.$langs->trans("Refresh").'"></td></tr>';
370print '</table>';
371print '</form>';
372print '<br><br>';
373
374print '<div class="div-table-responsive-no-min">';
375print '<table class="noborder centpercent">';
376print '<tr class="liste_titre" height="24">';
377print '<td class="center">'.$langs->trans("Year").'</td>';
378print '<td class="right">'.$langs->trans("NumberOfBills").'</td>';
379print '<td class="right">%</td>';
380print '<td class="right">'.$langs->trans("AmountTotal").'</td>';
381print '<td class="right">%</td>';
382print '<td class="right">'.$langs->trans("AmountAverage").'</td>';
383print '<td class="right">%</td>';
384print '</tr>';
385
386$oldyear = 0;
387foreach ($data as $val) {
388 $year = (int) $val['year'];
389 while ($year && $oldyear > $year + 1) { // If we have empty year
390 $oldyear--;
391
392 print '<tr class="oddeven" height="24">';
393 print '<td align="center"><a href="'.$_SERVER["PHP_SELF"].'?year='.$oldyear.'&amp;mode='.$mode.($socid > 0 ? '&socid='.$socid : '').($userid > 0 ? '&userid='.$userid : '').'">'.$oldyear.'</a></td>';
394 print '<td class="right">0</td>';
395 print '<td class="right"></td>';
396 print '<td class="right amount">0</td>';
397 print '<td class="right"></td>';
398 print '<td class="right amount">0</td>';
399 print '<td class="right"></td>';
400 print '</tr>';
401 }
402
403 if ($mode == 'supplier') {
404 $greennb = (empty($val['nb_diff']) || $val['nb_diff'] <= 0);
405 $greentotal = (empty($val['total_diff']) || $val['total_diff'] <= 0);
406 $greenavg = (empty($val['avg_diff']) || $val['avg_diff'] <= 0);
407 } else {
408 $greennb = (empty($val['nb_diff']) || $val['nb_diff'] >= 0);
409 $greentotal = (empty($val['total_diff']) || $val['total_diff'] >= 0);
410 $greenavg = (empty($val['avg_diff']) || $val['avg_diff'] >= 0);
411 }
412
413 print '<tr class="oddeven" height="24">';
414 print '<td align="center"><a href="'.$_SERVER["PHP_SELF"].'?year='.$year.'&amp;mode='.$mode.($socid > 0 ? '&socid='.$socid : '').($userid > 0 ? '&userid='.$userid : '').'">'.$year.'</a></td>';
415 print '<td class="right">'.$val['nb'].'</td>';
416 print '<td class="right opacitylow" style="'.($greennb ? 'color: green;' : 'color: red;').'">'.(!empty($val['nb_diff']) && $val['nb_diff'] < 0 ? '' : '+').round(!empty($val['nb_diff']) ? $val['nb_diff'] : 0).'%</td>';
417 print '<td class="right"><span class="amount">'.price(price2num($val['total'], 'MT'), 1).'</span></td>';
418 print '<td class="right opacitylow" style="'.($greentotal ? 'color: green;' : 'color: red;').'">'.(!empty($val['total_diff']) && $val['total_diff'] < 0 ? '' : '+').round(!empty($val['total_diff']) ? $val['total_diff'] : 0).'%</td>';
419 print '<td class="right"><span class="amount">'.price(price2num($val['avg'], 'MT'), 1).'</span></td>';
420 print '<td class="right opacitylow" style="'.($greenavg ? 'color: green;' : 'color: red;').'">'.(!empty($val['avg_diff']) && $val['avg_diff'] < 0 ? '' : '+').round(!empty($val['avg_diff']) ? $val['avg_diff'] : 0).'%</td>';
421 print '</tr>';
422 $oldyear = $year;
423}
424
425print '</table>';
426print '</div>';
427
428print '</div><div class="fichetwothirdright">';
429
430
431// Show graphs
432print '<table class="border centpercent"><tr class="pair nohover"><td align="center">';
433if ($mesg) {
434 print $mesg;
435} else {
436 print $px1->show();
437 print "<br>\n";
438 print $px2->show();
439 print "<br>\n";
440 print $px3->show();
441}
442print '</td></tr></table>';
443
444
445print '</div></div>';
446print '<div class="clearboth"></div>';
447
448
449print dol_get_fiche_end();
450
451// End of page
452llxFooter();
453$db->close();
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:71
Class to build graphs.
static getDefaultGraphSizeForStats($direction, $defaultsize='')
getDefaultGraphSizeForStats
Class to manage stats for invoices (customer and supplier)
Class to build HTML component for third parties management Only common components are here.
Class to manage generation of HTML components Only common components must be here.
Class permettant la generation de composants html autre Only common components are here.
llxFooter()
Footer empty.
Definition document.php:107
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
dol_now($mode='auto')
Return date for now.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode='add', $filterorigmodule='')
Complete or removed entries into a head array (used to build tabs).
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.