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