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]+\./i', '', $gval);
408 $gvalsanitized = preg_replace('/[^a-z0-9\._\-]+/i', '', $gval);
409
410 if (preg_match('/\-year$/', $gvalsanitized)) {
411 $tmpval = preg_replace('/\-year$/', '', $gvalsanitized);
412 $fieldtocount .= 'DATE_FORMAT('.$tmpval.", '%Y')";
413 } elseif (preg_match('/\-month$/', $gvalsanitized)) {
414 $tmpval = preg_replace('/\-month$/', '', $gvalsanitized);
415 $fieldtocount .= 'DATE_FORMAT('.$tmpval.", '%Y-%m')";
416 } elseif (preg_match('/\-day$/', $gvalsanitized)) {
417 $tmpval = preg_replace('/\-day$/', '', $gvalsanitized);
418 $fieldtocount .= 'DATE_FORMAT('.$tmpval.", '%Y-%m-%d')";
419 } else {
420 $fieldtocount = $gvalsanitized;
421 }
422
423 $sql = "SELECT DISTINCT ".$fieldtocount." as val";
424
425 if (strpos($fieldtocount, 'te') === 0) {
426 $tabletouse = $object->table_element;
427 $tablealiastouse = 'te';
428 if (!empty($arrayofgroupby[$gval])) {
429 $tmpval = explode('.', $gval);
430 $tabletouse = $arrayofgroupby[$gval]['table'];
431 $tablealiastouse = $tmpval[0];
432 }
433 //var_dump($tablealiastouse);exit;
434
435 //$sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element."_extrafields as te";
436 $sql .= " FROM ".MAIN_DB_PREFIX.$tabletouse."_extrafields as ".$tablealiastouse;
437 } else {
438 $tabletouse = $object->table_element;
439 $tablealiastouse = 't';
440 if (!empty($arrayofgroupby[$gval])) {
441 $tmpval = explode('.', $gval);
442 $tabletouse = $arrayofgroupby[$gval]['table'];
443 $tablealiastouse = $tmpval[0];
444 }
445 $sql .= " FROM ".MAIN_DB_PREFIX.$tabletouse." as ".$tablealiastouse;
446 }
447
448 // Add a where here keeping only the criteria on $tabletouse
449 /* TODO
450 if ($search_component_params_hidden) {
451 $errormessage = '';
452 $sql .= forgeSQLFromUniversalSearchCriteria($search_component_params_hidden, $errormessage);
453 }
454 */
455
456 $sql .= " LIMIT ".((int) ($MAXUNIQUEVALFORGROUP + 1));
457
458 //print $sql;
459 $resql = $db->query($sql);
460 if (!$resql) {
461 dol_print_error($db);
462 }
463
464 while ($obj = $db->fetch_object($resql)) {
465 if (is_null($obj->val)) {
466 $keytouse = '__NULL__';
467 $valuetranslated = $langs->transnoentitiesnoconv("NotDefined");
468 } elseif ($obj->val === '') {
469 $keytouse = '';
470 $valuetranslated = $langs->transnoentitiesnoconv("Empty");
471 } else {
472 $keytouse = (string) $obj->val;
473 $valuetranslated = $obj->val;
474 }
475
476 $regs = array();
477 if (!empty($object->fields[$gvalwithoutprefix]['arrayofkeyval'])) {
478 $valuetranslated = $object->fields[$gvalwithoutprefix]['arrayofkeyval'][$obj->val];
479 if (is_null($valuetranslated)) {
480 $valuetranslated = $langs->transnoentitiesnoconv("UndefinedKey");
481 }
482 $valuetranslated = $langs->trans($valuetranslated);
483 } elseif (preg_match('/integer:([^:]+):([^:]+)$/', $object->fields[$gvalwithoutprefix]['type'], $regs)) {
484 $classname = $regs[1];
485 $classpath = $regs[2];
486 dol_include_once($classpath);
487 if (class_exists($classname)) {
488 $tmpobject = new $classname($db);
489 $tmpobject->fetch($obj->val);
490 foreach ($tmpobject->fields as $fieldkey => $field) {
491 if ($field['showoncombobox']) {
492 $valuetranslated = $tmpobject->$fieldkey;
493 //if ($valuetranslated == '-') $valuetranslated = $langs->transnoentitiesnoconv("Unknown")
494 break;
495 }
496 }
497 //$valuetranslated = $tmpobject->ref.'eee';
498 }
499 }
500
501 $arrayofvaluesforgroupby['g_'.$gkey][$keytouse] = $valuetranslated;
502 }
503 // Add also the possible NULL value if field is a parent field that is not a strict join
504 $tmpfield = explode('.', $gval);
505 if ($tmpfield[0] != 't' || (is_array($object->fields[$tmpfield[1]]) && empty($object->fields[$tmpfield[1]]['notnull']))) {
506 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");
507 //var_dump($gval); var_dump($object->fields);
508 $arrayofvaluesforgroupby['g_'.$gkey]['__NULL__'] = $langs->transnoentitiesnoconv("NotDefined");
509 }
510
511 if (is_array($arrayofvaluesforgroupby['g_'.$gkey])) {
512 asort($arrayofvaluesforgroupby['g_'.$gkey]);
513 }
514
515 // Add a protection/error to refuse the request if number of differentr values for the group by is higher than $MAXUNIQUEVALFORGROUP
516 if (is_array($arrayofvaluesforgroupby['g_'.$gkey]) && count($arrayofvaluesforgroupby['g_'.$gkey]) > $MAXUNIQUEVALFORGROUP) {
517 $langs->load("errors");
518
519 if (strpos($fieldtocount, 'te') === 0) { // This is a field of an extrafield
520 //if (!empty($extrafields->attributes[$object->table_element]['langfile'][$gvalwithoutprefix])) {
521 // $langs->load($extrafields->attributes[$object->table_element]['langfile'][$gvalwithoutprefix]);
522 //}
523 $keyforlabeloffield = $extrafields->attributes[$object->table_element]['label'][$gvalwithoutprefix];
524 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield);
525 } elseif (strpos($fieldtocount, 't__') === 0) { // This is a field of a foreign key
526 $reg = array();
527 if (preg_match('/^(.*)\.(.*)/', $gvalwithoutprefix, $reg)) {
528 /*
529 $gvalwithoutprefix = preg_replace('/\..*$/', '', $gvalwithoutprefix);
530 $gvalwithoutprefix = preg_replace('/^t__/', '', $gvalwithoutprefix);
531 $keyforlabeloffield = $object->fields[$gvalwithoutprefix]['label'];
532 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield).'-'.$reg[2];
533 */
534 $labeloffield = $arrayofgroupby[$fieldtocount]['labelnohtml'];
535 } else {
536 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield);
537 }
538 } else { // This is a common field
539 $reg = array();
540 if (preg_match('/^(.*)\-(year|month|day)/', $gvalwithoutprefix, $reg)) {
541 $gvalwithoutprefix = preg_replace('/\-(year|month|day)/', '', $gvalwithoutprefix);
542 $keyforlabeloffield = $object->fields[$gvalwithoutprefix]['label'];
543 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield).'-'.$reg[2];
544 } else {
545 $keyforlabeloffield = $object->fields[$gvalwithoutprefix]['label'];
546 $labeloffield = $langs->transnoentitiesnoconv($keyforlabeloffield);
547 }
548 }
549 //var_dump($object->fields);
550 setEventMessages($langs->trans("ErrorTooManyDifferentValueForSelectedGroupBy", $MAXUNIQUEVALFORGROUP, $labeloffield), null, 'warnings');
551 $search_groupby = array();
552 }
553
554 $db->free($resql);
555 }
556}
557//var_dump($arrayofvaluesforgroupby);exit;
558
559
560$tmparray = dol_getdate(dol_now());
561$endyear = $tmparray['year'];
562$endmonth = $tmparray['mon'];
563$datelastday = dol_get_last_day($endyear, $endmonth, 1);
564$startyear = $endyear - 2;
565
566$param = '';
567
568
569if (!defined('MAIN_CUSTOM_REPORT_KEEP_GRAPH_ONLY')) {
570 print '<form method="post" action="'.$_SERVER['PHP_SELF'].'" autocomplete="off">';
571 print '<input type="hidden" name="token" value="'.newToken().'">';
572 print '<input type="hidden" name="action" value="viewgraph">';
573 print '<input type="hidden" name="tabfamily" value="'.$tabfamily.'">';
574
575 $viewmode = '';
576
577 $viewmode .= '<div class="divadvancedsearchfield">';
578 $arrayofgraphs = array('bars' => 'Bars', 'lines' => 'Lines'); // also 'pies'
579 $viewmode .= '<div class="inline-block opacitymedium"><span class="fas fa-chart-area paddingright" title="'.$langs->trans("Graph").'"></span>'.$langs->trans("Graph").'</div> ';
580 $viewmode .= $form->selectarray('search_graph', $arrayofgraphs, $search_graph, 0, 0, 0, '', 1, 0, 0, '', 'graphtype width100');
581 $viewmode .= '</div>';
582
583 $num = 0;
584 $massactionbutton = '';
585 $nav = '';
586 $newcardbutton = '';
587 $limit = 0;
588
589 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);
590
591
592 foreach ($newarrayoftype as $tmpkey => $tmpval) {
593 $newarrayoftype[$tmpkey]['label'] = img_picto('', $tmpval['picto'], 'class="pictofixedwidth"').$langs->trans($tmpval['label']);
594 }
595
596 print '<div class="liste_titre liste_titre_bydiv liste_titre_bydiv_inlineblock centpercent">';
597
598 // Select object
599 print '<div class="divadvancedsearchfield center floatnone">';
600 print '<div class="inline-block"><span class="opacitymedium">'.$langs->trans("StatisticsOn").'</span></div> ';
601 print $form->selectarray('objecttype', $newarrayoftype, $objecttype, 0, 0, 0, '', 1, 0, 0, '', 'minwidth200', 1, '', 0, 1);
602 if (empty($conf->use_javascript_ajax)) {
603 print '<input type="submit" class="button buttongen button-save nomargintop" name="changeobjecttype" value="'.$langs->trans("Refresh").'">';
604 } else {
605 print '<!-- js code to reload page with good object type -->
606 <script nonce="'.getNonce().'" type="text/javascript">
607 jQuery(document).ready(function() {
608 jQuery("#objecttype").change(function() {
609 console.log("Reload for "+jQuery("#objecttype").val());
610 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')) : '').'";
611 });
612 });
613 </script>';
614 }
615 print '</div><div class="clearboth"></div>';
616
617 // Filter (you can use param &show_search_component_params_hidden=1 for debug)
618 if (!empty($object)) {
619 print '<div class="divadvancedsearchfield">';
620 print $form->searchComponent(array($object->element => $object->fields), $search_component_params, array(), $search_component_params_hidden);
621 print '</div>';
622 }
623
624 // YAxis (add measures into array)
625 $count = 0;
626 //var_dump($arrayofmesures);
627 print '<div class="divadvancedsearchfield clearboth">';
628 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>';
629 $simplearrayofmesures = array();
630 foreach ($arrayofmesures as $key => $val) {
631 $simplearrayofmesures[$key] = $arrayofmesures[$key]['label'];
632 }
633 print $form->multiselectarray('search_measures', $simplearrayofmesures, $search_measures, 0, 0, 'minwidth300', 1, 0, '', '', $langs->trans("Measures")); // Fill the array $arrayofmeasures with possible fields
634 print '</div>';
635
636 // XAxis
637 $count = 0;
638 print '<div class="divadvancedsearchfield">';
639 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>';
640 //var_dump($arrayofxaxis);
641 print $formother->selectXAxisField($object, $search_xaxis, $arrayofxaxis, $langs->trans("XAxis"), 'minwidth300 maxwidth400'); // Fill the array $arrayofxaxis with possible fields
642 print '</div>';
643
644 // Group by
645 $count = 0;
646 print '<div class="divadvancedsearchfield">';
647 print '<div class="inline-block opacitymedium"><span class="fas fa-ruler-horizontal paddingright pictofixedwidth" title="'.dol_escape_htmltag($langs->trans("GroupBy")).'"></span></div>';
648 print $formother->selectGroupByField($object, $search_groupby, $arrayofgroupby, 'minwidth250 maxwidth300', $langs->trans("GroupBy")); // Fill the array $arrayofgroupby with possible fields
649 print '</div>';
650
651
652 if ($mode == 'grid') {
653 // YAxis
654 print '<div class="divadvancedsearchfield">';
655 foreach ($object->fields as $key => $val) {
656 if (empty($val['measure']) && (!isset($val['enabled']) || dol_eval($val['enabled'], 1, 1, '1'))) {
657 if (in_array($key, array('id', 'rowid', 'entity', 'last_main_doc', 'extraparams'))) {
658 continue;
659 }
660 if (preg_match('/^fk_/', $key)) {
661 continue;
662 }
663 if (in_array($val['type'], array('html', 'text'))) {
664 continue;
665 }
666 if (in_array($val['type'], array('timestamp', 'date', 'datetime'))) {
667 $arrayofyaxis['t.'.$key.'-year'] = array(
668 'label' => $langs->trans($val['label']).' ('.$YYYY.')',
669 'position' => $val['position'],
670 'table' => $object->table_element
671 );
672 $arrayofyaxis['t.'.$key.'-month'] = array(
673 'label' => $langs->trans($val['label']).' ('.$YYYY.'-'.$MM.')',
674 'position' => $val['position'],
675 'table' => $object->table_element
676 );
677 $arrayofyaxis['t.'.$key.'-day'] = array(
678 'label' => $langs->trans($val['label']).' ('.$YYYY.'-'.$MM.'-'.$DD.')',
679 'position' => $val['position'],
680 'table' => $object->table_element
681 );
682 } else {
683 $arrayofyaxis['t.'.$key] = array(
684 'label' => $val['label'],
685 'position' => (int) $val['position'],
686 'table' => $object->table_element
687 );
688 }
689 }
690 }
691 // Add measure from extrafields
692 if ($object->isextrafieldmanaged) {
693 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) {
694 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'))) {
695 $arrayofyaxis['te.'.$key] = array(
696 'label' => $extrafields->attributes[$object->table_element]['label'][$key],
697 'position' => (int) $extrafields->attributes[$object->table_element]['pos'][$key],
698 'table' => $object->table_element
699 );
700 }
701 }
702 }
703 $arrayofyaxis = dol_sort_array($arrayofyaxis, 'position');
704 $arrayofyaxislabel = array();
705 foreach ($arrayofyaxis as $key => $val) {
706 $arrayofyaxislabel[$key] = $val['label'];
707 }
708 print '<div class="inline-block opacitymedium"><span class="fas fa-ruler-vertical paddingright" title="'.$langs->trans("YAxis").'"></span>'.$langs->trans("YAxis").'</div> ';
709 print $form->multiselectarray('search_yaxis', $arrayofyaxislabel, $search_yaxis, 0, 0, 'minwidth100', 1);
710 print '</div>';
711 }
712
713 if ($mode == 'graph') {
714 //
715 }
716
717 print '<div class="divadvancedsearchfield">';
718 print '<input type="submit" class="button buttongen button-save nomargintop" value="'.$langs->trans("Refresh").'">';
719 print '</div>';
720 print '</div>';
721 print '</form>';
722}
723
724// Generate the SQL request
725$sql = '';
726if (!empty($search_measures) && !empty($search_xaxis)) {
727 $errormessage = '';
728
729 $fieldid = 'rowid';
730
731 $sql = "SELECT ";
732 foreach ($search_xaxis as $key => $val) {
733 if (preg_match('/\-year$/', $val)) {
734 $tmpval = preg_replace('/\-year$/', '', $val);
735 $sql .= "DATE_FORMAT(".$tmpval.", '%Y') as x_".$key.', ';
736 } elseif (preg_match('/\-month$/', $val)) {
737 $tmpval = preg_replace('/\-month$/', '', $val);
738 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m') as x_".$key.', ';
739 } elseif (preg_match('/\-day$/', $val)) {
740 $tmpval = preg_replace('/\-day$/', '', $val);
741 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m-%d') as x_".$key.', ';
742 } else {
743 $sql .= $val." as x_".$key.", ";
744 }
745 }
746 if (!empty($search_groupby)) {
747 foreach ($search_groupby as $key => $val) {
748 if (preg_match('/\-year$/', $val)) {
749 $tmpval = preg_replace('/\-year$/', '', $val);
750 $sql .= "DATE_FORMAT(".$tmpval.", '%Y') as g_".$key.', ';
751 } elseif (preg_match('/\-month$/', $val)) {
752 $tmpval = preg_replace('/\-month$/', '', $val);
753 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m') as g_".$key.', ';
754 } elseif (preg_match('/\-day$/', $val)) {
755 $tmpval = preg_replace('/\-day$/', '', $val);
756 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m-%d') as g_".$key.', ';
757 } else {
758 $sql .= $val." as g_".$key.", ";
759 }
760 }
761 }
762 foreach ($search_measures as $key => $val) {
763 if ($val == 't.count') {
764 $sql .= "COUNT(t.".$fieldid.") as y_".$key.', ';
765 } elseif (preg_match('/\-sum$/', $val)) {
766 $tmpval = preg_replace('/\-sum$/', '', $val);
767 $sql .= "SUM(".$db->ifsql($tmpval.' IS NULL', '0', $tmpval).") as y_".$key.", ";
768 } elseif (preg_match('/\-average$/', $val)) {
769 $tmpval = preg_replace('/\-average$/', '', $val);
770 $sql .= "AVG(".$db->ifsql($tmpval.' IS NULL', '0', $tmpval).") as y_".$key.", ";
771 } elseif (preg_match('/\-min$/', $val)) {
772 $tmpval = preg_replace('/\-min$/', '', $val);
773 $sql .= "MIN(".$db->ifsql($tmpval.' IS NULL', '0', $tmpval).") as y_".$key.", ";
774 } elseif (preg_match('/\-max$/', $val)) {
775 $tmpval = preg_replace('/\-max$/', '', $val);
776 $sql .= "MAX(".$db->ifsql($tmpval.' IS NULL', '0', $tmpval).") as y_".$key.", ";
777 }
778 }
779 $sql = preg_replace('/,\s*$/', '', $sql);
780 $sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t";
781 // Add measure from extrafields
782 if ($object->isextrafieldmanaged) {
783 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as te ON te.fk_object = t.".$fieldid;
784 }
785 // Add table for link on multientity
786 if ($object->ismultientitymanaged) { // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
787 if ($object->ismultientitymanaged == 1) {
788 // No table to add here
789 } else {
790 $tmparray = explode('@', $object->ismultientitymanaged);
791 $sql .= " INNER JOIN ".MAIN_DB_PREFIX.$tmparray[1]." as parenttableforentity ON t.".$tmparray[0]." = parenttableforentity.rowid";
792 $sql .= " AND parenttableforentity.entity IN (".getEntity($tmparray[1]).")";
793 }
794 }
795
796 // Init the list of tables added. We include by default always the main table.
797 $listoftablesalreadyadded = array($object->table_element => $object->table_element);
798
799 // Add LEFT JOIN for all parent tables mentioned into the Xaxis
800 //var_dump($arrayofxaxis); var_dump($search_xaxis);
801 foreach ($search_xaxis as $key => $val) {
802 if (!empty($arrayofxaxis[$val])) {
803 $tmpval = explode('.', $val);
804 //var_dump($arrayofgroupby);
805 $tmpforloop = dolExplodeIntoArray($arrayofxaxis[$val]['tablefromt'], ',');
806 foreach ($tmpforloop as $tmptable => $tmptablealias) {
807 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
808 $tmpforexplode = explode('__', $tmptablealias);
809 $endpart = end($tmpforexplode);
810 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
811
812 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
813 $listoftablesalreadyadded[$tmptable] = $tmptable;
814
815 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
816 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
817 $listoftablesalreadyadded[$tmptable] = $tmptable;
818 }
819 }
820 }
821 } else {
822 $errormessage = 'Found a key into search_xaxis not found into arrayofxaxis';
823 }
824 }
825
826 // Add LEFT JOIN for all parent tables mentioned into the Group by
827 //var_dump($arrayofgroupby); var_dump($search_groupby);
828 foreach ($search_groupby as $key => $val) {
829 if (!empty($arrayofgroupby[$val])) {
830 $tmpval = explode('.', $val);
831 //var_dump($arrayofgroupby[$val]); var_dump($tmpval);
832 $tmpforloop = dolExplodeIntoArray($arrayofgroupby[$val]['tablefromt'], ',');
833 foreach ($tmpforloop as $tmptable => $tmptablealias) {
834 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
835 $tmpforexplode = explode('__', $tmptablealias);
836 $endpart = end($tmpforexplode);
837 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
838
839 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
840 $listoftablesalreadyadded[$tmptable] = $tmptable;
841
842 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
843 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
844 $listoftablesalreadyadded[$tmptable] = $tmptable;
845 }
846 }
847 }
848 } else {
849 $errormessage = 'Found a key into search_groupby not found into arrayofgroupby';
850 }
851 }
852
853 // Add LEFT JOIN for all parent tables mentioned into the Yaxis
854 //var_dump($arrayofgroupby); var_dump($search_groupby);
855 foreach ($search_measures as $key => $val) {
856 if (!empty($arrayofmesures[$val])) {
857 $tmpval = explode('.', $val);
858 //var_dump($arrayofgroupby);
859 $tmpforloop = dolExplodeIntoArray($arrayofmesures[$val]['tablefromt'], ',');
860 foreach ($tmpforloop as $tmptable => $tmptablealias) {
861 if (! in_array($tmptable, $listoftablesalreadyadded)) { // We do not add join for main table and tables already added
862 $tmpforexplode = explode('__', $tmptablealias);
863 $endpart = end($tmpforexplode);
864 $parenttableandfield = preg_replace('/__'.$endpart.'$/', '', $tmptablealias).'.'.$endpart;
865
866 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable." as ".$db->sanitize($tmptablealias)." ON ".$db->sanitize($parenttableandfield)." = ".$db->sanitize($tmptablealias).".rowid";
867 $listoftablesalreadyadded[$tmptable] = $tmptable;
868
869 if (preg_match('/^te/', $tmpval[0]) && preg_replace('/^t_/', 'te_', $tmptablealias) == $tmpval[0]) {
870 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$tmptable."_extrafields as ".$db->sanitize($tmpval[0])." ON ".$db->sanitize($tmpval[0]).".fk_object = ".$db->sanitize($tmptablealias).".rowid";
871 $listoftablesalreadyadded[$tmptable] = $tmptable;
872 }
873 }
874 }
875 } else {
876 $errormessage = 'Found a key into search_measures not found into arrayofmesures';
877 }
878 }
879
880 $sql .= " WHERE 1 = 1";
881 if ($object->ismultientitymanaged == 1) { // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
882 $sql .= " AND t.entity IN (".getEntity($object->element).")";
883 }
884 // Add the where here
885 $sqlfilters = $search_component_params_hidden;
886 if ($sqlfilters) {
887 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage, 0, 0, 1);
888 }
889 $sql .= " GROUP BY ";
890 foreach ($search_xaxis as $key => $val) {
891 if (preg_match('/\-year$/', $val)) {
892 $tmpval = preg_replace('/\-year$/', '', $val);
893 $sql .= "DATE_FORMAT(".$tmpval.", '%Y'), ";
894 } elseif (preg_match('/\-month$/', $val)) {
895 $tmpval = preg_replace('/\-month$/', '', $val);
896 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m'), ";
897 } elseif (preg_match('/\-day$/', $val)) {
898 $tmpval = preg_replace('/\-day$/', '', $val);
899 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m-%d'), ";
900 } else {
901 $sql .= $val.", ";
902 }
903 }
904 if (!empty($search_groupby)) {
905 foreach ($search_groupby as $key => $val) {
906 if (preg_match('/\-year$/', $val)) {
907 $tmpval = preg_replace('/\-year$/', '', $val);
908 $sql .= "DATE_FORMAT(".$tmpval.", '%Y'), ";
909 } elseif (preg_match('/\-month$/', $val)) {
910 $tmpval = preg_replace('/\-month$/', '', $val);
911 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m'), ";
912 } elseif (preg_match('/\-day$/', $val)) {
913 $tmpval = preg_replace('/\-day$/', '', $val);
914 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m-%d'), ";
915 } else {
916 $sql .= $val.', ';
917 }
918 }
919 }
920 $sql = preg_replace('/,\s*$/', '', $sql);
921 $sql .= ' ORDER BY ';
922 foreach ($search_xaxis as $key => $val) {
923 if (preg_match('/\-year$/', $val)) {
924 $tmpval = preg_replace('/\-year$/', '', $val);
925 $sql .= "DATE_FORMAT(".$tmpval.", '%Y'), ";
926 } elseif (preg_match('/\-month$/', $val)) {
927 $tmpval = preg_replace('/\-month$/', '', $val);
928 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m'), ";
929 } elseif (preg_match('/\-day$/', $val)) {
930 $tmpval = preg_replace('/\-day$/', '', $val);
931 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m-%d'), ";
932 } else {
933 $sql .= $val.', ';
934 }
935 }
936 if (!empty($search_groupby)) {
937 foreach ($search_groupby as $key => $val) {
938 if (preg_match('/\-year$/', $val)) {
939 $tmpval = preg_replace('/\-year$/', '', $val);
940 $sql .= "DATE_FORMAT(".$tmpval.", '%Y'), ";
941 } elseif (preg_match('/\-month$/', $val)) {
942 $tmpval = preg_replace('/\-month$/', '', $val);
943 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m'), ";
944 } elseif (preg_match('/\-day$/', $val)) {
945 $tmpval = preg_replace('/\-day$/', '', $val);
946 $sql .= "DATE_FORMAT(".$tmpval.", '%Y-%m-%d'), ";
947 } else {
948 $sql .= $val.', ';
949 }
950 }
951 }
952 $sql = preg_replace('/,\s*$/', '', $sql);
953
954 // Can overwrite the SQL with a custom SQL string (when used as an include)
955 if (!empty($customsql)) {
956 $sql = $customsql;
957 }
958}
959//print $sql;
960
961if ($errormessage) {
962 print '<div class="warning">';
963 print dol_escape_htmltag($errormessage);
964 //print '<br>'.dol_escape_htmltag('SQL is '.$sql);
965 print '</div>';
966 $sql = '';
967}
968
969$legend = array();
970foreach ($search_measures as $key => $val) {
971 $legend[] = $langs->trans($arrayofmesures[$val]['label']);
972}
973
974$useagroupby = (is_array($search_groupby) && count($search_groupby));
975//var_dump($useagroupby);
976//var_dump($arrayofvaluesforgroupby);
977
978// Execute the SQL request
979$totalnbofrecord = 0;
980$data = array();
981if ($sql) {
982 $resql = $db->query($sql);
983 if (!$resql) {
984 print '<div class="warning">';
985 print dol_escape_htmltag($db->lasterror());
986 //print '<br>'.dol_escape_htmltag('SQL is '.$sql);
987 print '</div>';
988 } else {
989 $ifetch = 0;
990 $xi = 0;
991 $oldlabeltouse = '';
992 while ($obj = $db->fetch_object($resql)) {
993 $ifetch++;
994 if ($useagroupby) {
995 $xval = $search_xaxis[0];
996 $fieldforxkey = 'x_0';
997 $xlabel = $obj->$fieldforxkey;
998 $xvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $xval);
999
1000 // Define $xlabel
1001 if (!empty($object->fields[$xvalwithoutprefix]['arrayofkeyval'])) {
1002 $xlabel = $object->fields[$xvalwithoutprefix]['arrayofkeyval'][$obj->$fieldforxkey];
1003 }
1004 $labeltouse = (($xlabel || $xlabel == '0') ? dol_trunc($xlabel, 20, 'middle') : ($xlabel === '' ? $langs->transnoentitiesnoconv("Empty") : $langs->transnoentitiesnoconv("NotDefined")));
1005
1006 if ($oldlabeltouse !== '' && ($labeltouse != $oldlabeltouse)) {
1007 $xi++; // Increase $xi
1008 }
1009 //var_dump($labeltouse.' '.$oldlabeltouse.' '.$xi);
1010 $oldlabeltouse = $labeltouse;
1011
1012 /* Example of value for $arrayofvaluesforgroupby
1013 * array (size=1)
1014 * 'g_0' =>
1015 * array (size=6)
1016 * 0 => string '0' (length=1)
1017 * '' => string 'Empty' (length=5)
1018 * '__NULL__' => string 'Not defined' (length=11)
1019 * 'done' => string 'done' (length=4)
1020 * 'processing' => string 'processing' (length=10)
1021 * 'undeployed' => string 'undeployed' (length=10)
1022 */
1023 foreach ($search_measures as $key => $val) {
1024 $gi = 0;
1025 foreach ($search_groupby as $gkey) {
1026 //var_dump('*** Fetch #'.$ifetch.' for labeltouse='.$labeltouse.' measure number '.$key.' and group g_'.$gi);
1027 //var_dump($arrayofvaluesforgroupby);
1028 foreach ($arrayofvaluesforgroupby['g_'.$gi] as $gvaluepossiblekey => $gvaluepossiblelabel) {
1029 $ykeysuffix = $gvaluepossiblelabel;
1030 $gvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $gval);
1031
1032 $fieldfory = 'y_'.$key;
1033 $fieldforg = 'g_'.$gi;
1034 $fieldforybis = 'y_'.$key.'_'.$ykeysuffix;
1035 //var_dump('gvaluepossiblekey='.$gvaluepossiblekey.' gvaluepossiblelabel='.$gvaluepossiblelabel.' ykeysuffix='.$ykeysuffix.' gval='.$gval.' gvalwithoutsuffix='.$gvalwithoutprefix);
1036 //var_dump('fieldforg='.$fieldforg.' obj->$fieldforg='.$obj->$fieldforg.' fieldfory='.$fieldfory.' obj->$fieldfory='.$obj->$fieldfory.' fieldforybis='.$fieldforybis);
1037
1038 if (!is_array($data[$xi])) {
1039 $data[$xi] = array();
1040 }
1041
1042 if (!array_key_exists('label', $data[$xi])) {
1043 $data[$xi] = array();
1044 $data[$xi]['label'] = $labeltouse;
1045 }
1046
1047 $objfieldforg = $obj->$fieldforg;
1048 if (is_null($objfieldforg)) {
1049 $objfieldforg = '__NULL__';
1050 }
1051
1052 if ($gvaluepossiblekey == '0') { // $gvaluepossiblekey can have type int or string. So we create a special if, used when value is '0'
1053 //var_dump($objfieldforg.' == \'0\' -> '.($objfieldforg == '0'));
1054 if ($objfieldforg == '0') {
1055 // The record we fetch is for this group
1056 $data[$xi][$fieldforybis] = $obj->$fieldfory;
1057 } elseif (!isset($data[$xi][$fieldforybis])) {
1058 // The record we fetch is not for this group
1059 $data[$xi][$fieldforybis] = '0';
1060 }
1061 } else {
1062 //var_dump((string) $objfieldforg.' === '.(string) $gvaluepossiblekey.' -> '.((string) $objfieldforg === (string) $gvaluepossiblekey));
1063 if ((string) $objfieldforg === (string) $gvaluepossiblekey) {
1064 // The record we fetch is for this group
1065 $data[$xi][$fieldforybis] = $obj->$fieldfory;
1066 } elseif (!isset($data[$xi][$fieldforybis])) {
1067 // The record we fetch is not for this group
1068 $data[$xi][$fieldforybis] = '0';
1069 }
1070 }
1071 }
1072 //var_dump($data[$xi]);
1073 $gi++;
1074 }
1075 }
1076 } else { // No group by
1077 $xval = $search_xaxis[0];
1078 $fieldforxkey = 'x_0';
1079 $xlabel = $obj->$fieldforxkey;
1080 $xvalwithoutprefix = preg_replace('/^[a-z]+\./', '', $xval);
1081
1082 // Define $xlabel
1083 if (!empty($object->fields[$xvalwithoutprefix]['arrayofkeyval'])) {
1084 $xlabel = $object->fields[$xvalwithoutprefix]['arrayofkeyval'][$obj->$fieldforxkey];
1085 }
1086
1087 $labeltouse = (($xlabel || $xlabel == '0') ? dol_trunc($xlabel, 20, 'middle') : ($xlabel === '' ? $langs->transnoentitiesnoconv("Empty") : $langs->transnoentitiesnoconv("NotDefined")));
1088 $xarrayforallseries = array('label' => $labeltouse);
1089 foreach ($search_measures as $key => $val) {
1090 $fieldfory = 'y_'.$key;
1091 $xarrayforallseries[$fieldfory] = $obj->$fieldfory;
1092 }
1093 $data[$xi] = $xarrayforallseries;
1094 $xi++;
1095 }
1096 }
1097
1098 $totalnbofrecord = count($data);
1099 }
1100}
1101//var_dump($data);
1102
1103print '<!-- Section to show the result -->'."\n";
1104print '<div class="customreportsoutput'.($totalnbofrecord ? '' : ' customreportsoutputnotdata').'">';
1105
1106
1107if ($mode == 'grid') {
1108 // TODO
1109}
1110
1111if ($mode == 'graph') {
1112 $WIDTH = '80%';
1113 $HEIGHT = (empty($_SESSION['dol_screenheight']) ? 400 : $_SESSION['dol_screenheight'] - 500);
1114
1115 // Show graph
1116 $px1 = new DolGraph();
1117 $mesg = $px1->isGraphKo();
1118 if (!$mesg) {
1119 //var_dump($legend);
1120 //var_dump($data);
1121 $px1->SetData($data);
1122 unset($data);
1123
1124 $arrayoftypes = array();
1125 foreach ($search_measures as $key => $val) {
1126 $arrayoftypes[] = $search_graph;
1127 }
1128
1129 $px1->SetLegend($legend);
1130 $px1->setShowLegend($SHOWLEGEND);
1131 $px1->SetMinValue($px1->GetFloorMinValue());
1132 $px1->SetMaxValue($px1->GetCeilMaxValue());
1133 $px1->SetWidth($WIDTH);
1134 $px1->SetHeight($HEIGHT);
1135 $px1->SetYLabel($langs->trans("Y"));
1136 $px1->SetShading(3);
1137 $px1->SetHorizTickIncrement(1);
1138 $px1->SetCssPrefix("cssboxes");
1139 $px1->SetType($arrayoftypes);
1140 $px1->mode = 'depth';
1141 $px1->SetTitle('');
1142
1143 $dir = $conf->user->dir_temp;
1144 dol_mkdir($dir);
1145 // $customreportkey may be defined when using customreports.php as an include
1146 if (!empty($object->element)) {
1147 $filenamekey = $dir.'/customreport_'.$object->element.(empty($customreportkey) ? '' : $customreportkey).'.png';
1148 $fileurlkey = DOL_URL_ROOT.'/viewimage.php?modulepart=user&file=customreport_'.$object->element.(empty($customreportkey) ? '' : $customreportkey).'.png';
1149 }
1150
1151 if (isset($filenamekey) && isset($fileurlkey)) {
1152 $px1->draw($filenamekey, $fileurlkey);
1153 }
1154
1155 $texttoshow = $langs->trans("NoRecordFound");
1156 if (!GETPOSTISSET('search_measures') || !GETPOSTISSET('search_xaxis')) {
1157 $texttoshow = $langs->trans("SelectYourGraphOptionsFirst");
1158 }
1159
1160 print $px1->show($totalnbofrecord ? 0 : $texttoshow);
1161 }
1162}
1163
1164if ($sql && !defined('MAIN_CUSTOM_REPORT_KEEP_GRAPH_ONLY')) {
1165 // Show admin info
1166 print '<br>'.info_admin($langs->trans("SQLUsedForExport").':<br> '.$sql, 0, 0, 1, '', 'TechnicalInformation');
1167}
1168
1169print '<div>';
1170
1171if (!defined('USE_CUSTOM_REPORT_AS_INCLUDE')) {
1172 print dol_get_fiche_end();
1173
1174 llxFooter();
1175 // End of page
1176
1177 $db->close();
1178}
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.