dolibarr 21.0.0-alpha
customreports.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2020-2024 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
4 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
35if (!defined('USE_CUSTOM_REPORT_AS_INCLUDE')) {
36 require '../main.inc.php';
37
38 // Get parameters
39 $action = GETPOST('action', 'aZ09') ? GETPOST('action', 'aZ09') : 'view'; // The action 'add', 'create', 'edit', 'update', 'view', ...
40 $massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists)
41
42 $mode = GETPOST('mode', 'alpha');
43 $objecttype = (string) GETPOST('objecttype', 'aZ09arobase');
44 $tabfamily = GETPOST('tabfamily', 'aZ09');
45
46 $search_measures = GETPOST('search_measures', 'array');
47
48 //$search_xaxis = GETPOST('search_xaxis', 'array');
49 if (GETPOST('search_xaxis', 'alpha') && GETPOST('search_xaxis', 'alpha') != '-1') {
50 $search_xaxis = array(GETPOST('search_xaxis', 'alpha'));
51 } else {
52 $search_xaxis = array();
53 }
54 //$search_groupby = GETPOST('search_groupby', 'array');
55 if (GETPOST('search_groupby', 'alpha') && GETPOST('search_groupby', 'alpha') != '-1') {
56 $search_groupby = array(GETPOST('search_groupby', 'alpha'));
57 } else {
58 $search_groupby = array();
59 }
60
61 '@phan-var-force string[] $search_groupby';
62
63 $search_yaxis = GETPOST('search_yaxis', 'array');
64 $search_graph = GETPOST('search_graph', 'restricthtml');
65
66 // Load variable for pagination
67 $limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
68 $sortfield = GETPOST('sortfield', 'aZ09comma');
69 $sortorder = GETPOST('sortorder', 'aZ09comma');
70 $page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
71 if (empty($page) || $page == -1 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha') || (empty($toselect) && $massaction === '0')) {
72 $page = 0;
73 } // If $page is not defined, or '' or -1 or if we click on clear filters or if we select empty mass action
74 $offset = $limit * $page;
75 $pageprev = $page - 1;
76 $pagenext = $page + 1;
77
78 $diroutputmassaction = $conf->user->dir_temp.'/'.$user->id.'/customreport';
79
80 $object = null;
81} else {
82 // When included
83 '
84 @phan-var-force int<0,1> $SHOWLEGEND
85 @phan-var-force string customreportkey
86 ';
87
88 $langs->load("main");
89 // $search_measures, $search_xaxis or $search_yaxis may have been defined by the parent.
90
91 if (empty($user) || empty($user->id)) {
92 print 'Page is called as an include but $user and its permission loaded with loadRights() are not defined. We stop here.';
93 exit(-1);
94 }
95 if (empty($object)) {
96 print 'Page is called as an include but $object is not defined. We stop here.';
97 exit(-1);
98 }
99}
100
101if (empty($mode)) {
102 $mode = 'graph';
103}
104if (!isset($search_measures)) {
105 $search_measures = array(0 => 't.count');
106}
107if (!empty($object)) {
108 $objecttype = $object->element.($object->module ? '@'.$object->module : '');
109}
110if (empty($objecttype)) {
111 $objecttype = 'thirdparty';
112}
113
114require_once DOL_DOCUMENT_ROOT."/core/class/extrafields.class.php";
115require_once DOL_DOCUMENT_ROOT."/core/class/html.form.class.php";
116require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
117require_once DOL_DOCUMENT_ROOT."/core/lib/company.lib.php";
118require_once DOL_DOCUMENT_ROOT."/core/lib/date.lib.php";
119require_once DOL_DOCUMENT_ROOT."/core/lib/customreports.lib.php";
120require_once DOL_DOCUMENT_ROOT."/core/class/dolgraph.class.php";
121require_once DOL_DOCUMENT_ROOT."/core/class/doleditor.class.php";
122require_once DOL_DOCUMENT_ROOT."/core/class/html.formother.class.php";
123
124// Load traductions files requiredby by page
125$langs->loadLangs(array("companies", "other", "exports", "sendings"));
126
127$extrafields = new ExtraFields($db);
128
129$hookmanager->initHooks(array('customreport')); // Note that conf->hooks_modules contains array
130
131$title = '';
132$picto = '';
133$errormessage = null;
134$keyforlabeloffield = null;
135$head = array();
136$ObjectClassName = '';
137// Objects available by default
138$arrayoftype = array(
139 'thirdparty' => array('langs' => 'companies', 'label' => 'ThirdParties', 'picto' => 'company', 'ObjectClassName' => 'Societe', 'enabled' => isModEnabled('societe'), 'ClassPath' => "/societe/class/societe.class.php"),
140 'contact' => array('label' => 'Contacts', 'picto' => 'contact', 'ObjectClassName' => 'Contact', 'enabled' => isModEnabled('societe'), 'ClassPath' => "/contact/class/contact.class.php"),
141 'proposal' => array('label' => 'Proposals', 'picto' => 'proposal', 'ObjectClassName' => 'Propal', 'enabled' => isModEnabled('propal'), 'ClassPath' => "/comm/propal/class/propal.class.php"),
142 'order' => array('label' => 'Orders', 'picto' => 'order', 'ObjectClassName' => 'Commande', 'enabled' => isModEnabled('order'), 'ClassPath' => "/commande/class/commande.class.php"),
143 'invoice' => array('langs' => 'facture', 'label' => 'Invoices', 'picto' => 'bill', 'ObjectClassName' => 'Facture', 'enabled' => isModEnabled('invoice'), 'ClassPath' => "/compta/facture/class/facture.class.php"),
144 'invoice_template' => array('label' => 'PredefinedInvoices', 'picto' => 'bill', 'ObjectClassName' => 'FactureRec', 'enabled' => isModEnabled('invoice'), 'ClassPath' => "/compta/class/facturerec.class.php", 'langs' => 'bills'),
145 'contract' => array('label' => 'Contracts', 'picto' => 'contract', 'ObjectClassName' => 'Contrat', 'enabled' => isModEnabled('contract'), 'ClassPath' => "/contrat/class/contrat.class.php", 'langs' => 'contracts'),
146 'contractdet' => array('label' => 'ContractLines', 'picto' => 'contract', 'ObjectClassName' => 'ContratLigne', 'enabled' => isModEnabled('contract'), 'ClassPath' => "/contrat/class/contrat.class.php", 'langs' => 'contracts'),
147 'bom' => array('label' => 'BOM', 'picto' => 'bom', 'ObjectClassName' => 'Bom', 'enabled' => isModEnabled('bom')),
148 'mrp' => array('label' => 'MO', 'picto' => 'mrp', 'ObjectClassName' => 'Mo', 'enabled' => isModEnabled('mrp'), 'ClassPath' => "/mrp/class/mo.class.php"),
149 'ticket' => array('label' => 'Ticket', 'picto' => 'ticket', 'ObjectClassName' => 'Ticket', 'enabled' => isModEnabled('ticket')),
150 'member' => array('label' => 'Adherent', 'picto' => 'member', 'ObjectClassName' => 'Adherent', 'enabled' => isModEnabled('member'), 'ClassPath' => "/adherents/class/adherent.class.php", 'langs' => 'members'),
151 'cotisation' => array('label' => 'Subscriptions', 'picto' => 'member', 'ObjectClassName' => 'Subscription', 'enabled' => isModEnabled('member'), 'ClassPath' => "/adherents/class/subscription.class.php", 'langs' => 'members'),
152);
153
154
155// Complete $arrayoftype by external modules
156$parameters = array('objecttype' => $objecttype, 'tabfamily' => $tabfamily);
157$reshook = $hookmanager->executeHooks('loadDataForCustomReports', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
158if ($reshook < 0) {
159 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
160} elseif (is_array($hookmanager->resArray)) {
161 if (!empty($hookmanager->resArray['title'])) { // Add entries for tabs
162 $title = $hookmanager->resArray['title'];
163 }
164 if (!empty($hookmanager->resArray['picto'])) { // Add entries for tabs
165 $picto = $hookmanager->resArray['picto'];
166 }
167 if (!empty($hookmanager->resArray['head'])) { // Add entries for tabs
168 $head = array_merge($head, $hookmanager->resArray['head']);
169 }
170 if (!empty($hookmanager->resArray['arrayoftype'])) { // Add entries from hook
171 foreach ($hookmanager->resArray['arrayoftype'] as $key => $val) {
172 $arrayoftype[$key] = $val;
173 }
174 }
175}
176
177// Load the main $object for statistics
178if ($objecttype) {
179 try {
180 if (!empty($arrayoftype[$objecttype]['ClassPath'])) {
181 $fileforclass = $arrayoftype[$objecttype]['ClassPath'];
182 } else {
183 $fileforclass = "/".$objecttype."/class/".$objecttype.".class.php";
184 }
185 dol_include_once($fileforclass);
186
187 $ObjectClassName = $arrayoftype[$objecttype]['ObjectClassName'];
188 if (!empty($ObjectClassName)) {
189 if (class_exists($ObjectClassName)) {
190 $object = new $ObjectClassName($db);
191 } else {
192 print 'Failed to load class for type '.$objecttype.'. Class file found but Class object named '.$ObjectClassName.' not found.';
193 }
194 } else {
195 print 'Failed to load class for type '.$objecttype.'. Class file name is unknown.';
196 }
197 } catch (Exception $e) {
198 print 'Failed to load class for type '.$objecttype.'. Class path not found.';
199 }
200}
201
202'@phan-var-force CommonObject $object';
203
204// Security check
205$socid = 0;
206if ($user->socid > 0) { // Protection if external user
207 //$socid = $user->socid;
208 accessforbidden('Access forbidden to external users');
209}
210
211// Fetch optionals attributes and labels
212$extrafields->fetch_name_optionals_label('all'); // We load all extrafields definitions for all objects
213//$extrafields->fetch_name_optionals_label($object->table_element_line);
214
215$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
216
217$search_component_params = array('');
218$search_component_params_hidden = trim(GETPOST('search_component_params_hidden', 'alphanohtml'));
219$search_component_params_input = trim(GETPOST('search_component_params_input', 'alphanohtml'));
220//var_dump($search_component_params_hidden);
221//var_dump($search_component_params_input);
222
223// If string is not an universal filter string, we try to convert it into universal filter syntax string
224$errorstr = '';
225forgeSQLFromUniversalSearchCriteria($search_component_params_input, $errorstr); // Try conversion UFS->SQL
226//var_dump($errorstr);
227if ($errorstr) {
228 $value = $search_component_params_input;
229
230 $value = preg_replace('/([a-z\.]+)\s*([!<>=]+|in|notin|like|notlike)\s*/', '\1:\2:', $value); // Clean string 'x < 10' into 'x:<:10' so we can then explode on space to get all AND tests to do
231 $value = preg_replace('/\s*\|\s*/', '|', $value);
232 //var_dump($value);
233
234 $crits = explode(' ', trim($value)); // the string after the name of the field. Explode on each AND
235 $res = '';
236
237 $i1 = 0; // count the nb of and criteria added (all fields / criteria)
238 foreach ($crits as $crit) { // Loop on each AND criteria
239 $crit = trim($crit);
240
241 $i2 = 0; // count the nb of valid criteria added for this first criteria
242 $newres = '';
243 $tmpcrits = explode('|', $crit);
244 $i3 = 0; // count the nb of valid criteria added for this current field
245 foreach ($tmpcrits as $tmpcrit) {
246 if ($tmpcrit !== '0' && empty($tmpcrit)) {
247 continue;
248 }
249 $tmpcrit = trim($tmpcrit);
250 //var_dump($tmpcrit);
251
252 $errorstr = '';
253 $parenthesislevel = 0;
254 $rescheckfilter = dolCheckFilters($tmpcrit, $errorstr, $parenthesislevel);
255 if ($rescheckfilter) {
256 while ($parenthesislevel > 0) {
257 $tmpcrit = preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $tmpcrit));
258 $parenthesislevel--;
259 }
260 }
261
262 $field = preg_replace('/(:[!<>=\s]+:|:in:|:notin:|:like:|:notlike:).*$/', '', $tmpcrit); // the name of the field
263 $tmpcrit = preg_replace('/^.*(:[!<>=\s]+:|:in:|:notin:|:like:|:notlike:)/', '\1', $tmpcrit); // the condition after the name of the field
264 //var_dump($field); var_dump($tmpcrit); var_dump($i3);
265
266 $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
267
268 $operator = '=';
269 $newcrit = preg_replace('/(:[!<>=\s]+:|:in:|:notin:|:like:|:notlike:)/', '', $tmpcrit);
270 //var_dump($newcrit);
271
272 $reg = array();
273 preg_match('/:([!<>=\s]+|in|notin|like|notlike):/', $tmpcrit, $reg);
274 if (!empty($reg[1])) {
275 $operator = $reg[1];
276 }
277 if ($newcrit != '') {
278 if (!preg_match('/^\'[^\']*\'$/', $newcrit)) {
279 $numnewcrit = price2num($newcrit);
280 $newres .= '('.$field.':'.$operator.':'.((float) $numnewcrit).')';
281 } else {
282 $newres .= '('.$field.':'.$operator.":".((string) $newcrit).')';
283 }
284 $i3++; // a criteria was added to string
285 }
286 }
287 $i2++; // a criteria for 1 more field was added to string
288
289 if ($newres) {
290 $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
291 }
292 $i1++;
293 }
294 $res = "(".$res.")";
295
296 //var_dump($res);exit;
297 $search_component_params_input = $res;
298}
299
300$arrayofandtagshidden = dolForgeExplodeAnd($search_component_params_hidden);
301$arrayofandtagsinput = dolForgeExplodeAnd($search_component_params_input);
302
303$search_component_params_hidden = implode(' AND ', array_merge($arrayofandtagshidden, $arrayofandtagsinput));
304//var_dump($search_component_params_hidden);
305
306$MAXUNIQUEVALFORGROUP = 20;
307$MAXMEASURESINBARGRAPH = 20;
308$SHOWLEGEND = (isset($SHOWLEGEND) ? $SHOWLEGEND : 1);
309
310$YYYY = substr($langs->trans("Year"), 0, 1).substr($langs->trans("Year"), 0, 1).substr($langs->trans("Year"), 0, 1).substr($langs->trans("Year"), 0, 1);
311$MM = substr($langs->trans("Month"), 0, 1).substr($langs->trans("Month"), 0, 1);
312$DD = substr($langs->trans("Day"), 0, 1).substr($langs->trans("Day"), 0, 1);
313$HH = substr($langs->trans("Hour"), 0, 1).substr($langs->trans("Hour"), 0, 1);
314$MI = substr($langs->trans("Minute"), 0, 1).substr($langs->trans("Minute"), 0, 1);
315$SS = substr($langs->trans("Second"), 0, 1).substr($langs->trans("Second"), 0, 1);
316
317$arrayofmesures = array();
318$arrayofxaxis = array();
319$arrayofgroupby = array();
320$arrayofyaxis = array();
321$arrayofvaluesforgroupby = array();
322
323$features = $object->element;
324if (!empty($object->element_for_permission)) {
325 $features = $object->element_for_permission;
326} else {
327 $features .= (empty($object->module) ? '' : '@'.$object->module);
328}
329
330restrictedArea($user, $features, 0, '');
331
332$error = 0;
333
334
335/*
336 * Actions
337 */
338
339// None
340
341
342
343/*
344 * View
345 */
346
347$form = new Form($db);
348$formother = new FormOther($db);
349
350if (!defined('USE_CUSTOM_REPORT_AS_INCLUDE')) {
351 llxHeader('', $langs->transnoentitiesnoconv('CustomReports'), '');
352
353 if (empty($head)) {
354 print dol_get_fiche_head($head, 'customreports', $title, -2, $picto);
355 } else {
356 print dol_get_fiche_head($head, 'customreports', $title, -1, $picto);
357 }
358}
359
360$newarrayoftype = array();
361foreach ($arrayoftype as $key => $val) {
362 if (dol_eval($val['enabled'], 1, 1, '1')) {
363 $newarrayoftype[$key] = $arrayoftype[$key];
364 }
365 if (!empty($val['langs'])) {
366 $langs->load($val['langs']);
367 }
368}
369
370$count = 0;
371$arrayofmesures = fillArrayOfMeasures($object, 't', $langs->trans($newarrayoftype[$objecttype]['label']), $arrayofmesures, 0, $count);
372$arrayofmesures = dol_sort_array($arrayofmesures, 'position', 'asc', 0, 0, 1);
373
374$count = 0;
375$arrayofxaxis = fillArrayOfXAxis($object, 't', $langs->trans($newarrayoftype[$objecttype]['label']), $arrayofxaxis, 0, $count);
376$arrayofxaxis = dol_sort_array($arrayofxaxis, 'position', 'asc', 0, 0, 1);
377
378$count = 0;
379$arrayofgroupby = fillArrayOfGroupBy($object, 't', $langs->trans($newarrayoftype[$objecttype]['label']), $arrayofgroupby, 0, $count);
380$arrayofgroupby = dol_sort_array($arrayofgroupby, 'position', 'asc', 0, 0, 1);
381
382$count = 0;
383$arrayoffilterfields = fillArrayOfFilterFields($object, 't', $langs->trans($newarrayoftype[$objecttype]['label']), $arrayoffilterfields, 0, $count);
384$arrayoffilterfields = dol_sort_array($arrayoffilterfields, 'position', 'asc', 0, 0, 1);
385
386
387// Check parameters
388if ($action == 'viewgraph') {
389 if (is_array($search_measures) && !count($search_measures)) {
390 setEventMessages($langs->trans("AtLeastOneMeasureIsRequired"), null, 'warnings');
391 } elseif ($mode == 'graph' && is_array($search_xaxis) && count($search_xaxis) > 1) {
392 setEventMessages($langs->trans("OnlyOneFieldForXAxisIsPossible"), null, 'warnings');
393 $search_xaxis = array(0 => $search_xaxis[0]);
394 }
395 if (is_array($search_groupby) && count($search_groupby) >= 2) {
396 setEventMessages($langs->trans("ErrorOnlyOneFieldForGroupByIsPossible"), null, 'warnings');
397 $search_groupby = array(0 => $search_groupby[0]);
398 }
399 if (is_array($search_xaxis) && !count($search_xaxis)) {
400 setEventMessages($langs->trans("AtLeastOneXAxisIsRequired"), null, 'warnings');
401 } elseif ($mode == 'graph' && $search_graph == 'bars' && count($search_measures) > $MAXMEASURESINBARGRAPH) {
402 $langs->load("errors");
403 setEventMessages($langs->trans("GraphInBarsAreLimitedToNMeasures", $MAXMEASURESINBARGRAPH), null, 'warnings');
404 $search_graph = 'lines';
405 }
406}
407
408// Get all possible values of fields when a 'group by' is set, and save this into $arrayofvaluesforgroupby
409// $arrayofvaluesforgroupby will be used to forge lael of each grouped series
410if (is_array($search_groupby) && count($search_groupby)) {
411 $fieldtocount = '';
412 foreach ($search_groupby as $gkey => $gval) {
413 $gvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $gval);
414
415 if (preg_match('/\-year$/', $search_groupby[$gkey])) {
416 $tmpval = preg_replace('/\-year$/', '', $search_groupby[$gkey]);
417 $fieldtocount .= 'DATE_FORMAT('.$tmpval.", '%Y')";
418 } elseif (preg_match('/\-month$/', $search_groupby[$gkey])) {
419 $tmpval = preg_replace('/\-month$/', '', $search_groupby[$gkey]);
420 $fieldtocount .= 'DATE_FORMAT('.$tmpval.", '%Y-%m')";
421 } elseif (preg_match('/\-day$/', $search_groupby[$gkey])) {
422 $tmpval = preg_replace('/\-day$/', '', $search_groupby[$gkey]);
423 $fieldtocount .= 'DATE_FORMAT('.$tmpval.", '%Y-%m-%d')";
424 } else {
425 $fieldtocount = $search_groupby[$gkey];
426 }
427
428 $sql = "SELECT DISTINCT ".$fieldtocount." as val";
429
430 if (strpos($fieldtocount, 'te') === 0) {
431 $tabletouse = $object->table_element;
432 $tablealiastouse = 'te';
433 if (!empty($arrayofgroupby[$gval])) {
434 $tmpval = explode('.', $gval);
435 $tabletouse = $arrayofgroupby[$gval]['table'];
436 $tablealiastouse = $tmpval[0];
437 }
438 //var_dump($tablealiastouse);exit;
439
440 //$sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element."_extrafields as te";
441 $sql .= " FROM ".MAIN_DB_PREFIX.$tabletouse."_extrafields as ".$tablealiastouse;
442 } else {
443 $tabletouse = $object->table_element;
444 $tablealiastouse = 't';
445 if (!empty($arrayofgroupby[$gval])) {
446 $tmpval = explode('.', $gval);
447 $tabletouse = $arrayofgroupby[$gval]['table'];
448 $tablealiastouse = $tmpval[0];
449 }
450 $sql .= " FROM ".MAIN_DB_PREFIX.$tabletouse." as ".$tablealiastouse;
451 }
452
453 // Add a where here keeping only the criteria on $tabletouse
454 /* TODO
455 if ($search_component_params_hidden) {
456 $errormessage = '';
457 $sql .= forgeSQLFromUniversalSearchCriteria($search_component_params_hidden, $errormessage);
458 }
459 */
460
461 $sql .= " LIMIT ".((int) ($MAXUNIQUEVALFORGROUP + 1));
462
463 //print $sql;
464 $resql = $db->query($sql);
465 if (!$resql) {
466 dol_print_error($db);
467 }
468
469 while ($obj = $db->fetch_object($resql)) {
470 if (is_null($obj->val)) {
471 $keytouse = '__NULL__';
472 $valuetranslated = $langs->transnoentitiesnoconv("NotDefined");
473 } elseif ($obj->val === '') {
474 $keytouse = '';
475 $valuetranslated = $langs->transnoentitiesnoconv("Empty");
476 } else {
477 $keytouse = (string) $obj->val;
478 $valuetranslated = $obj->val;
479 }
480
481 $regs = array();
482 if (!empty($object->fields[$gvalwithoutprefix]['arrayofkeyval'])) {
483 $valuetranslated = $object->fields[$gvalwithoutprefix]['arrayofkeyval'][$obj->val];
484 if (is_null($valuetranslated)) {
485 $valuetranslated = $langs->transnoentitiesnoconv("UndefinedKey");
486 }
487 $valuetranslated = $langs->trans($valuetranslated);
488 } elseif (preg_match('/integer:([^:]+):([^:]+)$/', $object->fields[$gvalwithoutprefix]['type'], $regs)) {
489 $classname = $regs[1];
490 $classpath = $regs[2];
491 dol_include_once($classpath);
492 if (class_exists($classname)) {
493 $tmpobject = new $classname($db);
494 '@phan-var-force CommonObject $tmpobject';
495 $tmpobject->fetch($obj->val);
496 foreach ($tmpobject->fields as $fieldkey => $field) {
497 if ($field['showoncombobox']) {
498 $valuetranslated = $tmpobject->$fieldkey;
499 //if ($valuetranslated == '-') $valuetranslated = $langs->transnoentitiesnoconv("Unknown")
500 break;
501 }
502 }
503 //$valuetranslated = $tmpobject->ref.'eee';
504 }
505 }
506
507 $arrayofvaluesforgroupby['g_'.$gkey][$keytouse] = $valuetranslated;
508 }
509 // Add also the possible NULL value if field is a parent field that is not a strict join
510 $tmpfield = explode('.', $gval);
511 if ($tmpfield[0] != 't' || (is_array($object->fields[$tmpfield[1]]) && empty($object->fields[$tmpfield[1]]['notnull']))) {
512 dol_syslog("The group by field ".$gval." may be null (because field is null or it is a left join), so we add __NULL__ entry in list of possible values");
513 //var_dump($gval); var_dump($object->fields);
514 $arrayofvaluesforgroupby['g_'.$gkey]['__NULL__'] = $langs->transnoentitiesnoconv("NotDefined");
515 }
516
517 if (is_array($arrayofvaluesforgroupby['g_'.$gkey])) {
518 asort($arrayofvaluesforgroupby['g_'.$gkey]);
519 }
520
521 // Add a protection/error to refuse the request if number of differentr values for the group by is higher than $MAXUNIQUEVALFORGROUP
522 if (is_array($arrayofvaluesforgroupby['g_'.$gkey]) && count($arrayofvaluesforgroupby['g_'.$gkey]) > $MAXUNIQUEVALFORGROUP) {
523 $langs->load("errors");
524
525 if (strpos($fieldtocount, 'te') === 0) { // This is a field of an extrafield
526 //if (!empty($extrafields->attributes[$object->table_element]['langfile'][$gvalwithoutprefix])) {
527 // $langs->load($extrafields->attributes[$object->table_element]['langfile'][$gvalwithoutprefix]);
528 //}
529 $keyforlabeloffield = $extrafields->attributes[$object->table_element]['label'][$gvalwithoutprefix];
530 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield);
531 } elseif (strpos($fieldtocount, 't__') === 0) { // This is a field of a foreign key
532 $reg = array();
533 if (preg_match('/^(.*)\.(.*)/', $gvalwithoutprefix, $reg)) {
534 /*
535 $gvalwithoutprefix = preg_replace('/\..*$/', '', $gvalwithoutprefix);
536 $gvalwithoutprefix = preg_replace('/^t__/', '', $gvalwithoutprefix);
537 $keyforlabeloffield = $object->fields[$gvalwithoutprefix]['label'];
538 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield).'-'.$reg[2];
539 */
540 $labeloffield = $arrayofgroupby[$fieldtocount]['labelnohtml'];
541 } else {
542 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield);
543 }
544 } else { // This is a common field
545 $reg = array();
546 if (preg_match('/^(.*)\-(year|month|day)/', $gvalwithoutprefix, $reg)) {
547 $gvalwithoutprefix = preg_replace('/\-(year|month|day)/', '', $gvalwithoutprefix);
548 $keyforlabeloffield = $object->fields[$gvalwithoutprefix]['label'];
549 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield).'-'.$reg[2];
550 } else {
551 $keyforlabeloffield = $object->fields[$gvalwithoutprefix]['label'];
552 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield);
553 }
554 }
555 //var_dump($object->fields);
556 setEventMessages($langs->trans("ErrorTooManyDifferentValueForSelectedGroupBy", $MAXUNIQUEVALFORGROUP, $labeloffield), null, 'warnings');
557 $search_groupby = array();
558 }
559
560 $db->free($resql);
561 }
562}
563//var_dump($arrayofvaluesforgroupby);exit;
564
565
566$tmparray = dol_getdate(dol_now());
567$endyear = $tmparray['year'];
568$endmonth = $tmparray['mon'];
569$datelastday = dol_get_last_day($endyear, $endmonth, 1);
570$startyear = $endyear - 2;
571
572$param = '';
573
574
575if (!defined('MAIN_CUSTOM_REPORT_KEEP_GRAPH_ONLY')) {
576 print '<form method="post" action="'.$_SERVER['PHP_SELF'].'" autocomplete="off">';
577 print '<input type="hidden" name="token" value="'.newToken().'">';
578 print '<input type="hidden" name="action" value="viewgraph">';
579 print '<input type="hidden" name="tabfamily" value="'.$tabfamily.'">';
580
581 $viewmode = '';
582
583 $viewmode .= '<div class="divadvancedsearchfield">';
584 $arrayofgraphs = array('bars' => 'Bars', 'lines' => 'Lines'); // also 'pies'
585 $viewmode .= '<div class="inline-block opacitymedium"><span class="fas fa-chart-area paddingright" title="'.$langs->trans("Graph").'"></span>'.$langs->trans("Graph").'</div> ';
586 $viewmode .= $form->selectarray('search_graph', $arrayofgraphs, $search_graph, 0, 0, 0, '', 1, 0, 0, '', 'graphtype width100');
587 $viewmode .= '</div>';
588
589 $num = 0;
590 $massactionbutton = '';
591 $nav = '';
592 $newcardbutton = '';
593 $limit = 0;
594
595 print_barre_liste('', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, -1, 'object_action', 0, $nav.'<span class="marginleftonly"></span>'.$newcardbutton, '', $limit, 1, 0, 1, $viewmode);
596
597
598 foreach ($newarrayoftype as $tmpkey => $tmpval) {
599 $newarrayoftype[$tmpkey]['label'] = img_picto('', $tmpval['picto'], 'class="pictofixedwidth"').$langs->trans($tmpval['label']);
600 }
601
602 print '<div class="liste_titre liste_titre_bydiv liste_titre_bydiv_inlineblock centpercent">';
603
604 // Select object
605 print '<div class="divadvancedsearchfield center floatnone">';
606 print '<div class="inline-block"><span class="opacitymedium">'.$langs->trans("StatisticsOn").'</span></div> ';
607 print $form->selectarray('objecttype', $newarrayoftype, $objecttype, 0, 0, 0, '', 1, 0, 0, '', 'minwidth200', 1, '', 0, 1);
608 if (empty($conf->use_javascript_ajax)) {
609 print '<input type="submit" class="button buttongen button-save nomargintop" name="changeobjecttype" value="'.$langs->trans("Refresh").'">';
610 } else {
611 print '<!-- js code to reload page with good object type -->
612 <script nonce="'.getNonce().'" type="text/javascript">
613 jQuery(document).ready(function() {
614 jQuery("#objecttype").change(function() {
615 console.log("Reload for "+jQuery("#objecttype").val());
616 location.href = "'.$_SERVER["PHP_SELF"].'?objecttype="+jQuery("#objecttype").val()+"'.($tabfamily ? '&tabfamily='.urlencode($tabfamily) : '').(GETPOSTINT('show_search_component_params_hidden') ? '&show_search_component_params_hidden='.((int) GETPOSTINT('show_search_component_params_hidden')) : '').'";
617 });
618 });
619 </script>';
620 }
621 print '</div><div class="clearboth"></div>';
622
623 // Filter (you can use param &show_search_component_params_hidden=1 for debug)
624 if (!empty($object)) {
625 print '<div class="divadvancedsearchfield">';
626 print $form->searchComponent(array($object->element => $object->fields), $search_component_params, array(), $search_component_params_hidden, $arrayoffilterfields);
627 print '</div>';
628 }
629
630 // YAxis (add measures into array)
631 $count = 0;
632 //var_dump($arrayofmesures);
633 print '<div class="divadvancedsearchfield clearboth">';
634 print '<div class="inline-block"><span class="fas fa-ruler-combined paddingright pictofixedwidth" title="'.dol_escape_htmltag($langs->trans("Measures")).'"></span><span class="fas fa-caret-left caretleftaxis" title="'.dol_escape_htmltag($langs->trans("Measures")).'"></span></div>';
635 $simplearrayofmesures = array();
636 foreach ($arrayofmesures as $key => $val) {
637 $simplearrayofmesures[$key] = $arrayofmesures[$key]['label'];
638 }
639 print $form->multiselectarray('search_measures', $simplearrayofmesures, $search_measures, 0, 0, 'minwidth300', 1, 0, '', '', $langs->trans("Measures")); // Fill the array $arrayofmeasures with possible fields
640 print '</div>';
641
642 // XAxis
643 $count = 0;
644 print '<div class="divadvancedsearchfield">';
645 print '<div class="inline-block"><span class="fas fa-ruler-combined paddingright pictofixedwidth" title="'.dol_escape_htmltag($langs->trans("XAxis")).'"></span><span class="fas fa-caret-down caretdownaxis" title="'.dol_escape_htmltag($langs->trans("XAxis")).'"></span></div>';
646 //var_dump($arrayofxaxis);
647 print $formother->selectXAxisField($object, $search_xaxis, $arrayofxaxis, $langs->trans("XAxis"), 'minwidth300 maxwidth400'); // Fill the array $arrayofxaxis with possible fields
648 print '</div>';
649
650 // Group by
651 $count = 0;
652 print '<div class="divadvancedsearchfield">';
653 print '<div class="inline-block opacitymedium"><span class="fas fa-ruler-horizontal paddingright pictofixedwidth" title="'.dol_escape_htmltag($langs->trans("GroupBy")).'"></span></div>';
654 print $formother->selectGroupByField($object, $search_groupby, $arrayofgroupby, 'minwidth250 maxwidth300', $langs->trans("GroupBy")); // Fill the array $arrayofgroupby with possible fields
655 print '</div>';
656
657
658 if ($mode == 'grid') {
659 // YAxis
660 print '<div class="divadvancedsearchfield">';
661 foreach ($object->fields as $key => $val) {
662 if (empty($val['measure']) && (!isset($val['enabled']) || dol_eval($val['enabled'], 1, 1, '1'))) {
663 if (in_array($key, array('id', 'rowid', 'entity', 'last_main_doc', 'extraparams'))) {
664 continue;
665 }
666 if (preg_match('/^fk_/', $key)) {
667 continue;
668 }
669 if (in_array($val['type'], array('html', 'text'))) {
670 continue;
671 }
672 if (in_array($val['type'], array('timestamp', 'date', 'datetime'))) {
673 $arrayofyaxis['t.'.$key.'-year'] = array(
674 'label' => $langs->trans($val['label']).' ('.$YYYY.')',
675 'position' => $val['position'],
676 'table' => $object->table_element
677 );
678 $arrayofyaxis['t.'.$key.'-month'] = array(
679 'label' => $langs->trans($val['label']).' ('.$YYYY.'-'.$MM.')',
680 'position' => $val['position'],
681 'table' => $object->table_element
682 );
683 $arrayofyaxis['t.'.$key.'-day'] = array(
684 'label' => $langs->trans($val['label']).' ('.$YYYY.'-'.$MM.'-'.$DD.')',
685 'position' => $val['position'],
686 'table' => $object->table_element
687 );
688 } else {
689 $arrayofyaxis['t.'.$key] = array(
690 'label' => $val['label'],
691 'position' => (int) $val['position'],
692 'table' => $object->table_element
693 );
694 }
695 }
696 }
697 // Add measure from extrafields
698 if ($object->isextrafieldmanaged) {
699 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) {
700 if (!empty($extrafields->attributes[$object->table_element]['totalizable'][$key]) && (!isset($extrafields->attributes[$object->table_element]['enabled'][$key]) || dol_eval((string) $extrafields->attributes[$object->table_element]['enabled'][$key], 1, 1, '1'))) {
701 $arrayofyaxis['te.'.$key] = array(
702 'label' => $extrafields->attributes[$object->table_element]['label'][$key],
703 'position' => (int) $extrafields->attributes[$object->table_element]['pos'][$key],
704 'table' => $object->table_element
705 );
706 }
707 }
708 }
709 $arrayofyaxis = dol_sort_array($arrayofyaxis, 'position');
710 $arrayofyaxislabel = array();
711 foreach ($arrayofyaxis as $key => $val) {
712 $arrayofyaxislabel[$key] = $val['label'];
713 }
714 print '<div class="inline-block opacitymedium"><span class="fas fa-ruler-vertical paddingright" title="'.$langs->trans("YAxis").'"></span>'.$langs->trans("YAxis").'</div> ';
715 print $form->multiselectarray('search_yaxis', $arrayofyaxislabel, $search_yaxis, 0, 0, 'minwidth100', 1);
716 print '</div>';
717 }
718
719 if ($mode == 'graph') {
720 //
721 }
722
723 print '<div class="divadvancedsearchfield">';
724 print '<input type="submit" class="button buttongen button-save nomargintop" value="'.$langs->trans("Refresh").'">';
725 print '</div>';
726 print '</div>';
727 print '</form>';
728}
729
730// Generate the SQL request
731$sql = '';
732if (!empty($search_measures) && !empty($search_xaxis)) {
733 $errormessage = '';
734
735 $fieldid = 'rowid';
736
737 $sql = "SELECT ";
738 foreach ($search_xaxis as $key => $val) {
739 if (preg_match('/\-year$/', $val)) {
740 $tmpval = preg_replace('/\-year$/', '', $val);
741 $sql .= "DATE_FORMAT(".$tmpval.", '%Y') as x_".$key.', ';
742 } elseif (preg_match('/\-month$/', $val)) {
743 $tmpval = preg_replace('/\-month$/', '', $val);
744 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m') as x_".$key.', ';
745 } elseif (preg_match('/\-day$/', $val)) {
746 $tmpval = preg_replace('/\-day$/', '', $val);
747 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m-%d') as x_".$key.', ';
748 } else {
749 $sql .= $val." as x_".$key.", ";
750 }
751 }
752 if (!empty($search_groupby)) {
753 foreach ($search_groupby as $key => $val) {
754 if (preg_match('/\-year$/', $val)) {
755 $tmpval = preg_replace('/\-year$/', '', $val);
756 $sql .= "DATE_FORMAT(".$tmpval.", '%Y') as g_".$key.', ';
757 } elseif (preg_match('/\-month$/', $val)) {
758 $tmpval = preg_replace('/\-month$/', '', $val);
759 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m') as g_".$key.', ';
760 } elseif (preg_match('/\-day$/', $val)) {
761 $tmpval = preg_replace('/\-day$/', '', $val);
762 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m-%d') as g_".$key.', ';
763 } else {
764 $sql .= $val." as g_".$key.", ";
765 }
766 }
767 }
768 foreach ($search_measures as $key => $val) {
769 if ($val == 't.count') {
770 $sql .= "COUNT(t.".$fieldid.") as y_".$key.', ';
771 } elseif (preg_match('/\-sum$/', $val)) {
772 $tmpval = preg_replace('/\-sum$/', '', $val);
773 $sql .= "SUM(".$db->ifsql($tmpval.' IS NULL', '0', $tmpval).") as y_".$key.", ";
774 } elseif (preg_match('/\-average$/', $val)) {
775 $tmpval = preg_replace('/\-average$/', '', $val);
776 $sql .= "AVG(".$db->ifsql($tmpval.' IS NULL', '0', $tmpval).") as y_".$key.", ";
777 } elseif (preg_match('/\-min$/', $val)) {
778 $tmpval = preg_replace('/\-min$/', '', $val);
779 $sql .= "MIN(".$db->ifsql($tmpval.' IS NULL', '0', $tmpval).") as y_".$key.", ";
780 } elseif (preg_match('/\-max$/', $val)) {
781 $tmpval = preg_replace('/\-max$/', '', $val);
782 $sql .= "MAX(".$db->ifsql($tmpval.' IS NULL', '0', $tmpval).") as y_".$key.", ";
783 }
784 }
785 $sql = preg_replace('/,\s*$/', '', $sql);
786 $sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t";
787 // Add measure from extrafields
788 if ($object->isextrafieldmanaged) {
789 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as te ON te.fk_object = t.".$fieldid;
790 }
791 // Add table for link on multientity
792 if ($object->ismultientitymanaged) { // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
793 if ($object->ismultientitymanaged == 1) {
794 // No table to add here
795 } else {
796 $tmparray = explode('@', $object->ismultientitymanaged);
797 $sql .= " INNER JOIN ".MAIN_DB_PREFIX.$tmparray[1]." as parenttableforentity ON t.".$tmparray[0]." = parenttableforentity.rowid";
798 $sql .= " AND parenttableforentity.entity IN (".getEntity($tmparray[1]).")";
799 }
800 }
801
802 // Init the list of tables added. We include by default always the main table.
803 $listoftablesalreadyadded = array($object->table_element => $object->table_element);
804
805 // Add LEFT JOIN for all parent tables mentioned into the Xaxis
806 //var_dump($arrayofxaxis); var_dump($search_xaxis);
807 foreach ($search_xaxis as $key => $val) {
808 if (!empty($arrayofxaxis[$val])) {
809 $tmpval = explode('.', $val);
810 //var_dump($arrayofgroupby);
811 $tmpforloop = dolExplodeIntoArray($arrayofxaxis[$val]['tablefromt'], ',');
812 foreach ($tmpforloop as $tmptable => $tmptablealias) {
813 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
814 $tmpforexplode = explode('__', $tmptablealias);
815 $endpart = end($tmpforexplode);
816 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
817
818 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
819 $listoftablesalreadyadded[$tmptable] = $tmptable;
820
821 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
822 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
823 $listoftablesalreadyadded[$tmptable] = $tmptable;
824 }
825 }
826 }
827 } else {
828 $errormessage = 'Found a key into search_xaxis not found into arrayofxaxis';
829 }
830 }
831
832 // Add LEFT JOIN for all parent tables mentioned into the Group by
833 //var_dump($arrayofgroupby); var_dump($search_groupby);
834 foreach ($search_groupby as $key => $val) {
835 if (!empty($arrayofgroupby[$val])) {
836 $tmpval = explode('.', $val);
837 //var_dump($arrayofgroupby[$val]); var_dump($tmpval);
838 $tmpforloop = dolExplodeIntoArray($arrayofgroupby[$val]['tablefromt'], ',');
839 foreach ($tmpforloop as $tmptable => $tmptablealias) {
840 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
841 $tmpforexplode = explode('__', $tmptablealias);
842 $endpart = end($tmpforexplode);
843 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
844
845 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
846 $listoftablesalreadyadded[$tmptable] = $tmptable;
847
848 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
849 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
850 $listoftablesalreadyadded[$tmptable] = $tmptable;
851 }
852 }
853 }
854 } else {
855 $errormessage = 'Found a key into search_groupby not found into arrayofgroupby';
856 }
857 }
858
859 // Add LEFT JOIN for all parent tables mentioned into the Yaxis
860 //var_dump($arrayofgroupby); var_dump($search_groupby);
861 foreach ($search_measures as $key => $val) {
862 if (!empty($arrayofmesures[$val])) {
863 $tmpval = explode('.', $val);
864 //var_dump($arrayofgroupby);
865 $tmpforloop = dolExplodeIntoArray($arrayofmesures[$val]['tablefromt'], ',');
866 foreach ($tmpforloop as $tmptable => $tmptablealias) {
867 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
868 $tmpforexplode = explode('__', $tmptablealias);
869 $endpart = end($tmpforexplode);
870 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
871
872 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
873 $listoftablesalreadyadded[$tmptable] = $tmptable;
874
875 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
876 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
877 $listoftablesalreadyadded[$tmptable] = $tmptable;
878 }
879 }
880 }
881 } else {
882 $errormessage = 'Found a key into search_measures not found into arrayofmesures';
883 }
884 }
885
886 // Add LEFT JOIN for all tables mentioned into filter
887 if (!empty($search_component_params_hidden)) {
888 // Get all fields used into the filter
889 preg_match_all('/\b(t[\w]*_[\w]*)\.(\w+(-\w+)?)/', $search_component_params_hidden, $matches);
890 $fieldsUsedInFilter = array_unique($matches[0]);
891
892 // Remove fields used before to avoid double join
893 $fieldsToRemove = array_merge($search_measures, $search_groupby, $search_xaxis);
894 $fieldsUsedInFilter = array_diff($fieldsUsedInFilter, $fieldsToRemove);
895
896 foreach ($fieldsUsedInFilter as $key => $val) {
897 if (!empty($arrayoffilterfields[$val])) {
898 $tmpval = explode('.', $val);
899 $tmpforloop = dolExplodeIntoArray($arrayoffilterfields[$val]['tablefromt'], ',');
900 foreach ($tmpforloop as $tmptable => $tmptablealias) {
901 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
902 $tmpforexplode = explode('__', $tmptablealias);
903 $endpart = end($tmpforexplode);
904 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
905
906 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
907 $listoftablesalreadyadded[$tmptable] = $tmptable;
908
909 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
910 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
911 $listoftablesalreadyadded[$tmptable] = $tmptable;
912 }
913 }
914 }
915 } else {
916 $errormessage = 'Found a key into search_filterfields not found into arrayoffilterfields';
917 }
918 }
919 }
920
921 $sql .= " WHERE 1 = 1";
922 if ($object->ismultientitymanaged == 1) { // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
923 $sql .= " AND t.entity IN (".getEntity($object->element).")";
924 }
925 // Add the where here
926 $sqlfilters = $search_component_params_hidden;
927 if ($sqlfilters) {
928 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage, 0, 0, 1);
929
930 // Replace date values by $db->idate(dol_mktime(...))
931 $sql = preg_replace_callback(
932 "/(\w+)\.(\w+)\s*(=|!=|<>|<|>|<=|>=)\s*'(\d{4})-(\d{2})-(\d{2})'/",
937 function (array $matches): string {
938 global $db;
939 $column = $matches[1] . '.' . $matches[2];
940 $operator = $matches[3];
941 $year = (int) $matches[4];
942 $month = (int) $matches[5];
943 $day = (int) $matches[6];
944
945 $startOfDay = $db->idate(dol_mktime(0, 0, 0, $month, $day, $year));
946 $endOfDay = $db->idate(dol_mktime(23, 59, 59, $month, $day, $year));
947
948 switch ($operator) {
949 case "=":
950 return "($column >= '$startOfDay' AND $column <= '$endOfDay')";
951 case "!=":
952 case "<>":
953 return "NOT ($column >= '$startOfDay' AND $column <= '$endOfDay')";
954 case "<":
955 return "$column < '$startOfDay'";
956 case ">":
957 return "$column > '$endOfDay'";
958 case "<=":
959 return "$column <= '$endOfDay'";
960 case ">=":
961 return "$column >= '$startOfDay'";
962 default:
963 return "";
964 }
965 },
966 $sql
967 );
968 }
969 $sql .= " GROUP BY ";
970 foreach ($search_xaxis as $key => $val) {
971 if (preg_match('/\-year$/', $val)) {
972 $tmpval = preg_replace('/\-year$/', '', $val);
973 $sql .= "DATE_FORMAT(".$tmpval.", '%Y'), ";
974 } elseif (preg_match('/\-month$/', $val)) {
975 $tmpval = preg_replace('/\-month$/', '', $val);
976 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m'), ";
977 } elseif (preg_match('/\-day$/', $val)) {
978 $tmpval = preg_replace('/\-day$/', '', $val);
979 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m-%d'), ";
980 } else {
981 $sql .= $val.", ";
982 }
983 }
984 if (!empty($search_groupby)) {
985 foreach ($search_groupby as $key => $val) {
986 if (preg_match('/\-year$/', $val)) {
987 $tmpval = preg_replace('/\-year$/', '', $val);
988 $sql .= "DATE_FORMAT(".$tmpval.", '%Y'), ";
989 } elseif (preg_match('/\-month$/', $val)) {
990 $tmpval = preg_replace('/\-month$/', '', $val);
991 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m'), ";
992 } elseif (preg_match('/\-day$/', $val)) {
993 $tmpval = preg_replace('/\-day$/', '', $val);
994 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m-%d'), ";
995 } else {
996 $sql .= $val.', ';
997 }
998 }
999 }
1000 $sql = preg_replace('/,\s*$/', '', $sql);
1001 $sql .= ' ORDER BY ';
1002 foreach ($search_xaxis as $key => $val) {
1003 if (preg_match('/\-year$/', $val)) {
1004 $tmpval = preg_replace('/\-year$/', '', $val);
1005 $sql .= "DATE_FORMAT(".$tmpval.", '%Y'), ";
1006 } elseif (preg_match('/\-month$/', $val)) {
1007 $tmpval = preg_replace('/\-month$/', '', $val);
1008 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m'), ";
1009 } elseif (preg_match('/\-day$/', $val)) {
1010 $tmpval = preg_replace('/\-day$/', '', $val);
1011 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m-%d'), ";
1012 } else {
1013 $sql .= $val.', ';
1014 }
1015 }
1016 if (!empty($search_groupby)) {
1017 foreach ($search_groupby as $key => $val) {
1018 if (preg_match('/\-year$/', $val)) {
1019 $tmpval = preg_replace('/\-year$/', '', $val);
1020 $sql .= "DATE_FORMAT(".$tmpval.", '%Y'), ";
1021 } elseif (preg_match('/\-month$/', $val)) {
1022 $tmpval = preg_replace('/\-month$/', '', $val);
1023 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m'), ";
1024 } elseif (preg_match('/\-day$/', $val)) {
1025 $tmpval = preg_replace('/\-day$/', '', $val);
1026 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m-%d'), ";
1027 } else {
1028 $sql .= $val.', ';
1029 }
1030 }
1031 }
1032 $sql = preg_replace('/,\s*$/', '', $sql);
1033
1034 // Can overwrite the SQL with a custom SQL string (when used as an include)
1035 if (!empty($customsql)) {
1036 $sql = $customsql;
1037 }
1038}
1039//print $sql;
1040
1041if ($errormessage) {
1042 print '<div class="warning">';
1043 print dol_escape_htmltag($errormessage);
1044 //print '<br>'.dol_escape_htmltag('SQL is '.$sql);
1045 print '</div>';
1046 $sql = '';
1047}
1048
1049$legend = array();
1050foreach ($search_measures as $key => $val) {
1051 $legend[] = $langs->trans($arrayofmesures[$val]['label']);
1052}
1053
1054$useagroupby = (is_array($search_groupby) && count($search_groupby));
1055//var_dump($useagroupby);
1056//var_dump($arrayofvaluesforgroupby);
1057
1058// Execute the SQL request
1059$totalnbofrecord = 0;
1060$data = array();
1061if ($sql) {
1062 $resql = $db->query($sql);
1063 if (!$resql) {
1064 print '<div class="warning">';
1065 print dol_escape_htmltag($db->lasterror());
1066 //print '<br>'.dol_escape_htmltag('SQL is '.$sql);
1067 print '</div>';
1068 } else {
1069 $ifetch = 0;
1070 $xi = 0;
1071 $oldlabeltouse = '';
1072 while ($obj = $db->fetch_object($resql)) {
1073 $ifetch++;
1074 if ($useagroupby) {
1075 $xval = $search_xaxis[0];
1076 $fieldforxkey = 'x_0';
1077 $xlabel = $obj->$fieldforxkey;
1078 $xvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $xval);
1079
1080 // Define $xlabel
1081 if (!empty($object->fields[$xvalwithoutprefix]['arrayofkeyval'])) {
1082 $xlabel = $object->fields[$xvalwithoutprefix]['arrayofkeyval'][$obj->$fieldforxkey];
1083 }
1084 $labeltouse = (($xlabel || $xlabel == '0') ? dol_trunc($xlabel, 20, 'middle') : ($xlabel === '' ? $langs->transnoentitiesnoconv("Empty") : $langs->transnoentitiesnoconv("NotDefined")));
1085
1086 if ($oldlabeltouse !== '' && ($labeltouse != $oldlabeltouse)) {
1087 $xi++; // Increase $xi
1088 }
1089 //var_dump($labeltouse.' '.$oldlabeltouse.' '.$xi);
1090 $oldlabeltouse = $labeltouse;
1091
1092 /* Example of value for $arrayofvaluesforgroupby
1093 * array (size=1)
1094 * 'g_0' =>
1095 * array (size=6)
1096 * 0 => string '0' (length=1)
1097 * '' => string 'Empty' (length=5)
1098 * '__NULL__' => string 'Not defined' (length=11)
1099 * 'done' => string 'done' (length=4)
1100 * 'processing' => string 'processing' (length=10)
1101 * 'undeployed' => string 'undeployed' (length=10)
1102 */
1103 foreach ($search_measures as $key => $val) {
1104 $gi = 0;
1105 foreach ($search_groupby as $gkey) {
1106 //var_dump('*** Fetch #'.$ifetch.' for labeltouse='.$labeltouse.' measure number '.$key.' and group g_'.$gi);
1107 //var_dump($arrayofvaluesforgroupby);
1108 foreach ($arrayofvaluesforgroupby['g_'.$gi] as $gvaluepossiblekey => $gvaluepossiblelabel) {
1109 $ykeysuffix = $gvaluepossiblelabel;
1110 $gvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $gval);
1111
1112 $fieldfory = 'y_'.$key;
1113 $fieldforg = 'g_'.$gi;
1114 $fieldforybis = 'y_'.$key.'_'.$ykeysuffix;
1115 //var_dump('gvaluepossiblekey='.$gvaluepossiblekey.' gvaluepossiblelabel='.$gvaluepossiblelabel.' ykeysuffix='.$ykeysuffix.' gval='.$gval.' gvalwithoutsuffix='.$gvalwithoutprefix);
1116 //var_dump('fieldforg='.$fieldforg.' obj->$fieldforg='.$obj->$fieldforg.' fieldfory='.$fieldfory.' obj->$fieldfory='.$obj->$fieldfory.' fieldforybis='.$fieldforybis);
1117
1118 if (!array_key_exists($xi, $data)) {
1119 $data[$xi] = array();
1120 }
1121
1122 if (!array_key_exists('label', $data[$xi])) {
1123 $data[$xi] = array();
1124 $data[$xi]['label'] = $labeltouse;
1125 }
1126
1127 $objfieldforg = $obj->$fieldforg;
1128 if (is_null($objfieldforg)) {
1129 $objfieldforg = '__NULL__';
1130 }
1131
1132 if ($gvaluepossiblekey == '0') { // $gvaluepossiblekey can have type int or string. So we create a special if, used when value is '0'
1133 //var_dump($objfieldforg.' == \'0\' -> '.($objfieldforg == '0'));
1134 if ($objfieldforg == '0') {
1135 // The record we fetch is for this group
1136 $data[$xi][$fieldforybis] = $obj->$fieldfory;
1137 } elseif (!isset($data[$xi][$fieldforybis])) {
1138 // The record we fetch is not for this group
1139 $data[$xi][$fieldforybis] = '0';
1140 }
1141 } else {
1142 //var_dump((string) $objfieldforg.' === '.(string) $gvaluepossiblekey.' -> '.((string) $objfieldforg === (string) $gvaluepossiblekey));
1143 if ((string) $objfieldforg === (string) $gvaluepossiblekey) {
1144 // The record we fetch is for this group
1145 $data[$xi][$fieldforybis] = $obj->$fieldfory;
1146 } elseif (!isset($data[$xi][$fieldforybis])) {
1147 // The record we fetch is not for this group
1148 $data[$xi][$fieldforybis] = '0';
1149 }
1150 }
1151 }
1152 //var_dump($data[$xi]);
1153 $gi++;
1154 }
1155 }
1156 } else { // No group by
1157 $xval = $search_xaxis[0];
1158 $fieldforxkey = 'x_0';
1159 $xlabel = $obj->$fieldforxkey;
1160 $xvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $xval);
1161
1162 // Define $xlabel
1163 if (!empty($object->fields[$xvalwithoutprefix]['arrayofkeyval'])) {
1164 $xlabel = $object->fields[$xvalwithoutprefix]['arrayofkeyval'][$obj->$fieldforxkey];
1165 }
1166
1167 $labeltouse = (($xlabel || $xlabel == '0') ? dol_trunc($xlabel, 20, 'middle') : ($xlabel === '' ? $langs->transnoentitiesnoconv("Empty") : $langs->transnoentitiesnoconv("NotDefined")));
1168 $xarrayforallseries = array('label' => $labeltouse);
1169 foreach ($search_measures as $key => $val) {
1170 $fieldfory = 'y_'.$key;
1171 $xarrayforallseries[$fieldfory] = $obj->$fieldfory;
1172 }
1173 $data[$xi] = $xarrayforallseries;
1174 $xi++;
1175 }
1176 }
1177
1178 $totalnbofrecord = count($data);
1179 }
1180}
1181//var_dump($data);
1182
1183print '<!-- Section to show the result -->'."\n";
1184print '<div class="customreportsoutput'.($totalnbofrecord ? '' : ' customreportsoutputnotdata').'">';
1185
1186
1187if ($mode == 'grid') {
1188 // TODO
1189}
1190
1191if ($mode == 'graph') {
1192 $WIDTH = '80%';
1193 $HEIGHT = (empty($_SESSION['dol_screenheight']) ? 400 : $_SESSION['dol_screenheight'] - 500);
1194
1195 // Show graph
1196 $px1 = new DolGraph();
1197 $mesg = $px1->isGraphKo();
1198 if (!$mesg) {
1199 //var_dump($legend);
1200 //var_dump($data);
1201 $px1->SetData($data);
1202 unset($data);
1203
1204 $arrayoftypes = array();
1205 foreach ($search_measures as $key => $val) {
1206 $arrayoftypes[] = $search_graph;
1207 }
1208
1209 $px1->SetLegend($legend);
1210 $px1->setShowLegend($SHOWLEGEND);
1211 $px1->SetMinValue($px1->GetFloorMinValue());
1212 $px1->SetMaxValue($px1->GetCeilMaxValue());
1213 $px1->SetWidth($WIDTH);
1214 $px1->SetHeight($HEIGHT);
1215 $px1->SetYLabel($langs->trans("Y"));
1216 $px1->SetShading(3);
1217 $px1->SetHorizTickIncrement(1);
1218 $px1->SetCssPrefix("cssboxes");
1219 $px1->SetType($arrayoftypes);
1220 $px1->mode = 'depth';
1221 $px1->SetTitle('');
1222
1223 $dir = $conf->user->dir_temp;
1224 dol_mkdir($dir);
1225 // $customreportkey may be defined when using customreports.php as an include
1226 $filenamekey = $dir.'/customreport_'.$object->element.(empty($customreportkey) ? '' : $customreportkey).'.png';
1227 $fileurlkey = DOL_URL_ROOT.'/viewimage.php?modulepart=user&file=customreport_'.$object->element.(empty($customreportkey) ? '' : $customreportkey).'.png';
1228
1229 $px1->draw($filenamekey, $fileurlkey);
1230
1231 $texttoshow = $langs->trans("NoRecordFound");
1232 if (!GETPOSTISSET('search_measures') || !GETPOSTISSET('search_xaxis')) {
1233 $texttoshow = $langs->trans("SelectYourGraphOptionsFirst");
1234 }
1235
1236 print $px1->show($totalnbofrecord ? 0 : $texttoshow);
1237 }
1238}
1239
1240if ($sql && !defined('MAIN_CUSTOM_REPORT_KEEP_GRAPH_ONLY')) {
1241 // Show admin info
1242 print '<br>'.info_admin($langs->trans("SQLUsedForExport").':<br> '.$sql, 0, 0, '1', '', 'TechnicalInformation');
1243}
1244
1245print '<div>';
1246
1247if (!defined('USE_CUSTOM_REPORT_AS_INCLUDE')) {
1248 print dol_get_fiche_end();
1249
1250 llxFooter();
1251 // End of page
1252
1253 $db->close();
1254}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
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:70
Class to build graphs.
Class to manage standard extra fields.
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.
fillArrayOfGroupBy($object, $tablealias, $labelofobject, &$arrayofgroupby, $level=0, &$count=0, &$tablepath='')
Fill arrayofgrupby for an object.
fillArrayOfFilterFields($object, $tablealias, $labelofobject, &$arrayoffields, $level=0, &$count=0, &$tablepath='')
Fill array of possible filter fields for an object.
fillArrayOfMeasures($object, $tablealias, $labelofobject, &$arrayofmesures, $level=0, &$count=0, &$tablepath='')
Fill arrayofmesures for an object.
fillArrayOfXAxis($object, $tablealias, $labelofobject, &$arrayofxaxis, $level=0, &$count=0, &$tablepath='')
Fill arrayofmesures for an object.
dol_get_last_day($year, $month=12, $gm=false)
Return GMT time for last day of a month or year.
Definition date.lib.php:615
llxFooter()
Footer empty.
Definition document.php:107
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
dolCheckFilters($sqlfilters, &$error='', &$parenthesislevel=0)
Return if a $sqlfilters parameter has a valid balance of parenthesis.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
dolForgeExplodeAnd($sqlfilters)
Explode an universal search string with AND parts.
dolExplodeIntoArray($string, $delimiter=';', $kv='=')
Split a string with 2 keys into key array.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
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_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dol_now($mode='auto')
Return date for now.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.