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