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