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