dolibarr  19.0.0-dev
html.form.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 2002-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
5  * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6  * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
7  * Copyright (C) 2005-2017 Regis Houssin <regis.houssin@inodbox.com>
8  * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
9  * Copyright (C) 2006 Marc Barilley/Ocebo <marc@ocebo.com>
10  * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerker@telenet.be>
11  * Copyright (C) 2007 Patrick Raguin <patrick.raguin@gmail.com>
12  * Copyright (C) 2010 Juanjo Menent <jmenent@2byte.es>
13  * Copyright (C) 2010-2021 Philippe Grand <philippe.grand@atoo-net.com>
14  * Copyright (C) 2011 Herve Prot <herve.prot@symeos.com>
15  * Copyright (C) 2012-2016 Marcos García <marcosgdf@gmail.com>
16  * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
17  * Copyright (C) 2012-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
18  * Copyright (C) 2014-2020 Alexandre Spangaro <aspangaro@open-dsi.fr>
19  * Copyright (C) 2018-2022 Ferran Marcet <fmarcet@2byte.es>
20  * Copyright (C) 2018-2021 Frédéric France <frederic.france@netlogic.fr>
21  * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
22  * Copyright (C) 2018 Christophe Battarel <christophe@altairis.fr>
23  * Copyright (C) 2018 Josep Lluis Amador <joseplluis@lliuretic.cat>
24  * Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
25  *
26  * This program is free software; you can redistribute it and/or modify
27  * it under the terms of the GNU General Public License as published by
28  * the Free Software Foundation; either version 3 of the License, or
29  * (at your option) any later version.
30  *
31  * This program is distributed in the hope that it will be useful,
32  * but WITHOUT ANY WARRANTY; without even the implied warranty of
33  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34  * GNU General Public License for more details.
35  *
36  * You should have received a copy of the GNU General Public License
37  * along with this program. If not, see <https://www.gnu.org/licenses/>.
38  */
39 
53 class Form
54 {
58  public $db;
59 
63  public $error = '';
64 
68  public $errors = array();
69 
70  // Some properties used to return data by some methods
71  public $result;
72  public $num;
73 
74  // Cache arrays
75  public $cache_types_paiements = array();
76  public $cache_conditions_paiements = array();
77  public $cache_transport_mode = array();
78  public $cache_availability = array();
79  public $cache_demand_reason = array();
80  public $cache_types_fees = array();
81  public $cache_vatrates = array();
82 
83 
89  public function __construct($db)
90  {
91  $this->db = $db;
92  }
93 
110  public function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata = 'string', $moreparam = '', $fieldrequired = 0, $notabletag = 0, $paramid = 'id', $help = '')
111  {
112  global $conf, $langs;
113 
114  $ret = '';
115 
116  // TODO change for compatibility
117  if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;/', $typeofdata)) {
118  if (!empty($perm)) {
119  $tmp = explode(':', $typeofdata);
120  $ret .= '<div class="editkey_' . $tmp[0] . (!empty($tmp[1]) ? ' ' . $tmp[1] : '') . '" id="' . $htmlname . '">';
121  if ($fieldrequired) {
122  $ret .= '<span class="fieldrequired">';
123  }
124  if ($help) {
125  $ret .= $this->textwithpicto($langs->trans($text), $help);
126  } else {
127  $ret .= $langs->trans($text);
128  }
129  if ($fieldrequired) {
130  $ret .= '</span>';
131  }
132  $ret .= '</div>' . "\n";
133  } else {
134  if ($fieldrequired) {
135  $ret .= '<span class="fieldrequired">';
136  }
137  if ($help) {
138  $ret .= $this->textwithpicto($langs->trans($text), $help);
139  } else {
140  $ret .= $langs->trans($text);
141  }
142  if ($fieldrequired) {
143  $ret .= '</span>';
144  }
145  }
146  } else {
147  if (empty($notabletag) && $perm) {
148  $ret .= '<table class="nobordernopadding centpercent"><tr><td class="nowrap">';
149  }
150  if ($fieldrequired) {
151  $ret .= '<span class="fieldrequired">';
152  }
153  if ($help) {
154  $ret .= $this->textwithpicto($langs->trans($text), $help);
155  } else {
156  $ret .= $langs->trans($text);
157  }
158  if ($fieldrequired) {
159  $ret .= '</span>';
160  }
161  if (!empty($notabletag)) {
162  $ret .= ' ';
163  }
164  if (empty($notabletag) && $perm) {
165  $ret .= '</td>';
166  }
167  if (empty($notabletag) && $perm) {
168  $ret .= '<td class="right">';
169  }
170  if ($htmlname && GETPOST('action', 'aZ09') != 'edit' . $htmlname && $perm) {
171  $ret .= '<a class="editfielda reposition" href="' . $_SERVER["PHP_SELF"] . '?action=edit' . $htmlname . '&token=' . newToken() . '&' . $paramid . '=' . $object->id . $moreparam . '">' . img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1)) . '</a>';
172  }
173  if (!empty($notabletag) && $notabletag == 1) {
174  if ($text) {
175  $ret .= ' : ';
176  } else {
177  $ret .= ' ';
178  }
179  }
180  if (!empty($notabletag) && $notabletag == 3) {
181  $ret .= ' ';
182  }
183  if (empty($notabletag) && $perm) {
184  $ret .= '</td>';
185  }
186  if (empty($notabletag) && $perm) {
187  $ret .= '</tr></table>';
188  }
189  }
190 
191  return $ret;
192  }
193 
215  public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 1, $formatfunc = '', $paramid = 'id', $gm = 'auto', $moreoptions = array(), $editaction = '')
216  {
217  global $conf, $langs;
218 
219  $ret = '';
220 
221  // Check parameters
222  if (empty($typeofdata)) {
223  return 'ErrorBadParameter typeofdata is empty';
224  }
225  // Clean paramater $typeofdata
226  if ($typeofdata == 'datetime') {
227  $typeofdata = 'dayhour';
228  }
229  $reg = array();
230  if (preg_match('/^(\w+)\‍((\d+)\‍)$/', $typeofdata, $reg)) {
231  if ($reg[1] == 'varchar') {
232  $typeofdata = 'string';
233  } elseif ($reg[1] == 'int') {
234  $typeofdata = 'numeric';
235  } else {
236  return 'ErrorBadParameter ' . $typeofdata;
237  }
238  }
239 
240  // When option to edit inline is activated
241  if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;|day|datepicker|dayhour|datehourpicker/', $typeofdata)) { // TODO add jquery timepicker and support select
242  $ret .= $this->editInPlace($object, $value, $htmlname, $perm, $typeofdata, $editvalue, $extObject, $custommsg);
243  } else {
244  if ($editaction == '') {
245  $editaction = GETPOST('action', 'aZ09');
246  }
247  $editmode = ($editaction == 'edit' . $htmlname);
248  if ($editmode) {
249  $ret .= "\n";
250  $ret .= '<form method="post" action="' . $_SERVER["PHP_SELF"] . ($moreparam ? '?' . $moreparam : '') . '">';
251  $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
252  $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
253  $ret .= '<input type="hidden" name="' . $paramid . '" value="' . $object->id . '">';
254  if (empty($notabletag)) {
255  $ret .= '<table class="nobordernopadding centpercent">';
256  }
257  if (empty($notabletag)) {
258  $ret .= '<tr><td>';
259  }
260  if (preg_match('/^(string|safehtmlstring|email|phone|url)/', $typeofdata)) {
261  $tmp = explode(':', $typeofdata);
262  $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($editvalue ? $editvalue : $value) . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
263  } elseif (preg_match('/^(integer)/', $typeofdata)) {
264  $tmp = explode(':', $typeofdata);
265  $valuetoshow = price2num($editvalue ? $editvalue : $value, 0);
266  $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . $valuetoshow . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
267  } elseif (preg_match('/^(numeric|amount)/', $typeofdata)) {
268  $tmp = explode(':', $typeofdata);
269  $valuetoshow = price2num($editvalue ? $editvalue : $value);
270  $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($valuetoshow != '' ? price($valuetoshow) : '') . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
271  } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
272  $tmp = explode(':', $typeofdata);
273  $ret .= '<input type="checkbox" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($value ? $value : 'on') . '"' . ($value ? ' checked' : '') . (empty($tmp[1]) ? '' : $tmp[1]) . '/>';
274  } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) { // if wysiwyg is enabled $typeofdata = 'ckeditor'
275  $tmp = explode(':', $typeofdata);
276  $cols = (empty($tmp[2]) ? '' : $tmp[2]);
277  $morealt = '';
278  if (preg_match('/%/', $cols)) {
279  $morealt = ' style="width: ' . $cols . '"';
280  $cols = '';
281  }
282 
283  $valuetoshow = ($editvalue ? $editvalue : $value);
284  $ret .= '<textarea id="' . $htmlname . '" name="' . $htmlname . '" wrap="soft" rows="' . (empty($tmp[1]) ? '20' : $tmp[1]) . '"' . ($cols ? ' cols="' . $cols . '"' : 'class="quatrevingtpercent"') . $morealt . '" autofocus>';
285  // textarea convert automatically entities chars into simple chars.
286  // So we convert & into &amp; so a string like 'a &lt; <b>b</b><br>é<br>&lt;script&gt;alert('X');&lt;script&gt;' stay a correct html and is not converted by textarea component when wysiwig is off.
287  $valuetoshow = str_replace('&', '&amp;', $valuetoshow);
288  $ret .= dol_string_neverthesehtmltags($valuetoshow, array('textarea'));
289  $ret .= '</textarea>';
290  } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
291  $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
292  $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
293  $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
294  $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
295  } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
296  $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
297  $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
298  $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
299  $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
300  } elseif (preg_match('/^select;/', $typeofdata)) {
301  $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
302  $arraylist = array();
303  foreach ($arraydata as $val) {
304  $tmp = explode(':', $val);
305  $tmpkey = str_replace('|', ':', $tmp[0]);
306  $arraylist[$tmpkey] = $tmp[1];
307  }
308  $ret .= $this->selectarray($htmlname, $arraylist, $value);
309  } elseif (preg_match('/^link/', $typeofdata)) {
310  // TODO Not yet implemented. See code for extrafields
311  } elseif (preg_match('/^ckeditor/', $typeofdata)) {
312  $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser
313  require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
314  $doleditor = new DolEditor($htmlname, ($editvalue ? $editvalue : $value), (empty($tmp[2]) ? '' : $tmp[2]), (empty($tmp[3]) ? '100' : $tmp[3]), (empty($tmp[1]) ? 'dolibarr_notes' : $tmp[1]), 'In', (empty($tmp[5]) ? 0 : $tmp[5]), (isset($tmp[8]) ? ($tmp[8] ? true : false) : true), true, (empty($tmp[6]) ? '20' : $tmp[6]), (empty($tmp[7]) ? '100' : $tmp[7]));
315  $ret .= $doleditor->Create(1);
316  } elseif ($typeofdata == 'asis') {
317  $ret .= ($editvalue ? $editvalue : $value);
318  }
319  if (empty($notabletag)) {
320  $ret .= '</td>';
321  }
322 
323  // Button save-cancel
324  if (empty($notabletag)) {
325  $ret .= '<td>';
326  }
327  //else $ret.='<div class="clearboth"></div>';
328  $ret .= '<input type="submit" class="smallpaddingimp button' . (empty($notabletag) ? '' : ' ') . '" name="modify" value="' . $langs->trans("Modify") . '">';
329  if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) {
330  $ret .= '<br>' . "\n";
331  }
332  $ret .= '<input type="submit" class="smallpaddingimp button button-cancel' . (empty($notabletag) ? '' : ' ') . '" name="cancel" value="' . $langs->trans("Cancel") . '">';
333  if (empty($notabletag)) {
334  $ret .= '</td>';
335  }
336 
337  if (empty($notabletag)) {
338  $ret .= '</tr></table>' . "\n";
339  }
340  $ret .= '</form>' . "\n";
341  } else {
342  if (preg_match('/^email/', $typeofdata)) {
343  $ret .= dol_print_email($value, 0, 0, 0, 0, 1);
344  } elseif (preg_match('/^phone/', $typeofdata)) {
345  $ret .= dol_print_phone($value, '_blank', 32, 1);
346  } elseif (preg_match('/^url/', $typeofdata)) {
347  $ret .= dol_print_url($value, '_blank', 32, 1);
348  } elseif (preg_match('/^(amount|numeric)/', $typeofdata)) {
349  $ret .= ($value != '' ? price($value, '', $langs, 0, -1, -1, $conf->currency) : '');
350  } elseif (preg_match('/^checkbox/', $typeofdata)) {
351  $tmp = explode(':', $typeofdata);
352  $ret .= '<input type="checkbox" disabled id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($value ? ' checked' : '') . ($tmp[1] ? $tmp[1] : '') . '/>';
353  } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {
354  $ret .= dol_htmlentitiesbr($value);
355  } elseif (preg_match('/^safehtmlstring/', $typeofdata)) {
356  $ret .= dol_string_onlythesehtmltags($value);
357  } elseif (preg_match('/^restricthtml/', $typeofdata)) {
358  $ret .= dol_string_onlythesehtmltags($value);
359  } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
360  $ret .= '<span class="valuedate">' . dol_print_date($value, 'day', $gm) . '</span>';
361  } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
362  $ret .= '<span class="valuedate">' . dol_print_date($value, 'dayhour', $gm) . '</span>';
363  } elseif (preg_match('/^select;/', $typeofdata)) {
364  $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
365  $arraylist = array();
366  foreach ($arraydata as $val) {
367  $tmp = explode(':', $val);
368  $arraylist[$tmp[0]] = $tmp[1];
369  }
370  $ret .= $arraylist[$value];
371  if ($htmlname == 'fk_product_type') {
372  if ($value == 0) {
373  $ret = img_picto($langs->trans("Product"), 'product', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
374  } else {
375  $ret = img_picto($langs->trans("Service"), 'service', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
376  }
377  }
378  } elseif (preg_match('/^ckeditor/', $typeofdata)) {
379  $tmpcontent = dol_htmlentitiesbr($value);
380  if (!empty($conf->global->MAIN_DISABLE_NOTES_TAB)) {
381  $firstline = preg_replace('/<br>.*/', '', $tmpcontent);
382  $firstline = preg_replace('/[\n\r].*/', '', $firstline);
383  $tmpcontent = $firstline . ((strlen($firstline) != strlen($tmpcontent)) ? '...' : '');
384  }
385  // We dont use dol_escape_htmltag to get the html formating active, but this need we must also
386  // clean data from some dangerous html
387  $ret .= dol_string_onlythesehtmltags(dol_htmlentitiesbr($tmpcontent));
388  } else {
389  if (empty($moreoptions['valuealreadyhtmlescaped'])) {
390  $ret .= dol_escape_htmltag($value);
391  } else {
392  $ret .= $value; // $value must be already html escaped.
393  }
394  }
395 
396  // Custom format if parameter $formatfunc has been provided
397  if ($formatfunc && method_exists($object, $formatfunc)) {
398  $ret = $object->$formatfunc($ret);
399  }
400  }
401  }
402  return $ret;
403  }
404 
416  public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '')
417  {
418  global $conf, $langs, $extralanguages;
419 
420  $result = '';
421 
422  // List of extra languages
423  $arrayoflangcode = array();
424  if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) {
425  $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
426  }
427 
428  if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
429  if (!is_object($extralanguages)) {
430  include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
431  $extralanguages = new ExtraLanguages($this->db);
432  }
433  $extralanguages->fetch_name_extralanguages('societe');
434 
435  if (!is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) {
436  return ''; // No extralang field to show
437  }
438 
439  $result .= '<!-- Widget for translation -->' . "\n";
440  $result .= '<div class="inline-block paddingleft image-' . $object->element . '-' . $fieldname . '">';
441  $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', false, 0, 0, '', 'fa-15 editfieldlang');
442  $result .= $s;
443  $result .= '</div>';
444 
445  $result .= '<div class="inline-block hidden field-' . $object->element . '-' . $fieldname . '">';
446 
447  $resultforextrlang = '';
448  foreach ($arrayoflangcode as $langcode) {
449  $valuetoshow = GETPOSTISSET('field-' . $object->element . "-" . $fieldname . "-" . $langcode) ? GETPOST('field-' . $object->element . '-' . $fieldname . "-" . $langcode, $check) : '';
450  if (empty($valuetoshow)) {
451  $object->fetchValuesForExtraLanguages();
452  //var_dump($object->array_languages);
453  $valuetoshow = $object->array_languages[$fieldname][$langcode];
454  }
455 
456  $s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"');
457  $resultforextrlang .= $s;
458 
459  // TODO Use the showInputField() method of ExtraLanguages object
460  if ($typeofdata == 'textarea') {
461  $resultforextrlang .= '<textarea name="field-' . $object->element . "-" . $fieldname . "-" . $langcode . '" id="' . $fieldname . "-" . $langcode . '" class="' . $morecss . '" rows="' . ROWS_2 . '" wrap="soft">';
462  $resultforextrlang .= $valuetoshow;
463  $resultforextrlang .= '</textarea>';
464  } else {
465  $resultforextrlang .= '<input type="text" class="inputfieldforlang ' . ($morecss ? ' ' . $morecss : '') . '" name="field-' . $object->element . '-' . $fieldname . '-' . $langcode . '" value="' . $valuetoshow . '">';
466  }
467  }
468  $result .= $resultforextrlang;
469 
470  $result .= '</div>';
471  $result .= '<script nonce="' . getNonce() . '">$(".image-' . $object->element . '-' . $fieldname . '").click(function() { console.log("Toggle lang widget"); jQuery(".field-' . $object->element . '-' . $fieldname . '").toggle(); });</script>';
472  }
473 
474  return $result;
475  }
476 
490  protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null)
491  {
492  global $conf;
493 
494  $out = '';
495 
496  // Check parameters
497  if (preg_match('/^text/', $inputType)) {
498  $value = dol_nl2br($value);
499  } elseif (preg_match('/^numeric/', $inputType)) {
500  $value = price($value);
501  } elseif ($inputType == 'day' || $inputType == 'datepicker') {
502  $value = dol_print_date($value, 'day');
503  }
504 
505  if ($condition) {
506  $element = false;
507  $table_element = false;
508  $fk_element = false;
509  $loadmethod = false;
510  $savemethod = false;
511  $ext_element = false;
512  $button_only = false;
513  $inputOption = '';
514  $rows = '';
515  $cols = '';
516 
517  if (is_object($object)) {
518  $element = $object->element;
519  $table_element = $object->table_element;
520  $fk_element = $object->id;
521  }
522 
523  if (is_object($extObject)) {
524  $ext_element = $extObject->element;
525  }
526 
527  if (preg_match('/^(string|email|numeric)/', $inputType)) {
528  $tmp = explode(':', $inputType);
529  $inputType = $tmp[0];
530  if (!empty($tmp[1])) {
531  $inputOption = $tmp[1];
532  }
533  if (!empty($tmp[2])) {
534  $savemethod = $tmp[2];
535  }
536  $out .= '<input id="width_' . $htmlname . '" value="' . $inputOption . '" type="hidden"/>' . "\n";
537  } elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) {
538  $tmp = explode(':', $inputType);
539  $inputType = $tmp[0];
540  if (!empty($tmp[1])) {
541  $inputOption = $tmp[1];
542  }
543  if (!empty($tmp[2])) {
544  $savemethod = $tmp[2];
545  }
546 
547  $out .= '<input id="timestamp" type="hidden"/>' . "\n"; // Use for timestamp format
548  } elseif (preg_match('/^(select|autocomplete)/', $inputType)) {
549  $tmp = explode(':', $inputType);
550  $inputType = $tmp[0];
551  $loadmethod = $tmp[1];
552  if (!empty($tmp[2])) {
553  $savemethod = $tmp[2];
554  }
555  if (!empty($tmp[3])) {
556  $button_only = true;
557  }
558  } elseif (preg_match('/^textarea/', $inputType)) {
559  $tmp = explode(':', $inputType);
560  $inputType = $tmp[0];
561  $rows = (empty($tmp[1]) ? '8' : $tmp[1]);
562  $cols = (empty($tmp[2]) ? '80' : $tmp[2]);
563  } elseif (preg_match('/^ckeditor/', $inputType)) {
564  $tmp = explode(':', $inputType);
565  $inputType = $tmp[0];
566  $toolbar = $tmp[1];
567  if (!empty($tmp[2])) {
568  $width = $tmp[2];
569  }
570  if (!empty($tmp[3])) {
571  $heigth = $tmp[3];
572  }
573  if (!empty($tmp[4])) {
574  $savemethod = $tmp[4];
575  }
576 
577  if (isModEnabled('fckeditor')) {
578  $out .= '<input id="ckeditor_toolbar" value="' . $toolbar . '" type="hidden"/>' . "\n";
579  } else {
580  $inputType = 'textarea';
581  }
582  }
583 
584  $out .= '<input id="element_' . $htmlname . '" value="' . $element . '" type="hidden"/>' . "\n";
585  $out .= '<input id="table_element_' . $htmlname . '" value="' . $table_element . '" type="hidden"/>' . "\n";
586  $out .= '<input id="fk_element_' . $htmlname . '" value="' . $fk_element . '" type="hidden"/>' . "\n";
587  $out .= '<input id="loadmethod_' . $htmlname . '" value="' . $loadmethod . '" type="hidden"/>' . "\n";
588  if (!empty($savemethod)) {
589  $out .= '<input id="savemethod_' . $htmlname . '" value="' . $savemethod . '" type="hidden"/>' . "\n";
590  }
591  if (!empty($ext_element)) {
592  $out .= '<input id="ext_element_' . $htmlname . '" value="' . $ext_element . '" type="hidden"/>' . "\n";
593  }
594  if (!empty($custommsg)) {
595  if (is_array($custommsg)) {
596  if (!empty($custommsg['success'])) {
597  $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg['success'] . '" type="hidden"/>' . "\n";
598  }
599  if (!empty($custommsg['error'])) {
600  $out .= '<input id="errormsg_' . $htmlname . '" value="' . $custommsg['error'] . '" type="hidden"/>' . "\n";
601  }
602  } else {
603  $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg . '" type="hidden"/>' . "\n";
604  }
605  }
606  if ($inputType == 'textarea') {
607  $out .= '<input id="textarea_' . $htmlname . '_rows" value="' . $rows . '" type="hidden"/>' . "\n";
608  $out .= '<input id="textarea_' . $htmlname . '_cols" value="' . $cols . '" type="hidden"/>' . "\n";
609  }
610  $out .= '<span id="viewval_' . $htmlname . '" class="viewval_' . $inputType . ($button_only ? ' inactive' : ' active') . '">' . $value . '</span>' . "\n";
611  $out .= '<span id="editval_' . $htmlname . '" class="editval_' . $inputType . ($button_only ? ' inactive' : ' active') . ' hideobject">' . (!empty($editvalue) ? $editvalue : $value) . '</span>' . "\n";
612  } else {
613  $out = $value;
614  }
615 
616  return $out;
617  }
618 
637  public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
638  {
639  if ($incbefore) {
640  $text = $incbefore . $text;
641  }
642  if (!$htmltext) {
643  return $text;
644  }
645  $direction = (int) $direction; // For backward compatibility when $direction was set to '' instead of 0
646 
647  $tag = 'td';
648  if ($notabs == 2) {
649  $tag = 'div';
650  }
651  if ($notabs == 3) {
652  $tag = 'span';
653  }
654  // Sanitize tooltip
655  $htmltext = str_replace(array("\r", "\n"), '', $htmltext);
656 
657  $extrastyle = '';
658  if ($direction < 0) {
659  $extracss = ($extracss ? $extracss . ' ' : '') . ($notabs != 3 ? 'inline-block' : '');
660  $extrastyle = 'padding: 0px; padding-left: 3px;';
661  }
662  if ($direction > 0) {
663  $extracss = ($extracss ? $extracss . ' ' : '') . ($notabs != 3 ? 'inline-block' : '');
664  $extrastyle = 'padding: 0px; padding-right: 3px;';
665  }
666 
667  $classfortooltip = 'classfortooltip';
668 
669  $s = '';
670  $textfordialog = '';
671 
672  if ($tooltiptrigger == '') {
673  $htmltext = str_replace('"', '&quot;', $htmltext);
674  } else {
675  $classfortooltip = 'classfortooltiponclick';
676  $textfordialog .= '<div style="display: none;" id="idfortooltiponclick_' . $tooltiptrigger . '" class="classfortooltiponclicktext">' . $htmltext . '</div>';
677  }
678  if ($tooltipon == 2 || $tooltipon == 3) {
679  $paramfortooltipimg = ' class="' . $classfortooltip . ($notabs != 3 ? ' inline-block' : '') . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '"';
680  if ($tooltiptrigger == '') {
681  $paramfortooltipimg .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)) . '"'; // Attribut to put on img tag to store tooltip
682  } else {
683  $paramfortooltipimg .= ' dolid="' . $tooltiptrigger . '"';
684  }
685  } else {
686  $paramfortooltipimg = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribut to put on td text tag
687  }
688  if ($tooltipon == 1 || $tooltipon == 3) {
689  $paramfortooltiptd = ' class="' . ($tooltipon == 3 ? 'cursorpointer ' : '') . $classfortooltip . ' inline-block' . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '" ';
690  if ($tooltiptrigger == '') {
691  $paramfortooltiptd .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)) . '"'; // Attribut to put on td tag to store tooltip
692  } else {
693  $paramfortooltiptd .= ' dolid="' . $tooltiptrigger . '"';
694  }
695  } else {
696  $paramfortooltiptd = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribut to put on td text tag
697  }
698  if (empty($notabs)) {
699  $s .= '<table class="nobordernopadding"><tr style="height: auto;">';
700  } elseif ($notabs == 2) {
701  $s .= '<div class="inline-block' . ($forcenowrap ? ' nowrap' : '') . '">';
702  }
703  // Define value if value is before
704  if ($direction < 0) {
705  $s .= '<' . $tag . $paramfortooltipimg;
706  if ($tag == 'td') {
707  $s .= ' class="valigntop" width="14"';
708  }
709  $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
710  }
711  // Use another method to help avoid having a space in value in order to use this value with jquery
712  // Define label
713  if ((string) $text != '') {
714  $s .= '<' . $tag . $paramfortooltiptd . '>' . $text . '</' . $tag . '>';
715  }
716  // Define value if value is after
717  if ($direction > 0) {
718  $s .= '<' . $tag . $paramfortooltipimg;
719  if ($tag == 'td') {
720  $s .= ' class="valignmiddle" width="14"';
721  }
722  $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
723  }
724  if (empty($notabs)) {
725  $s .= '</tr></table>';
726  } elseif ($notabs == 2) {
727  $s .= '</div>';
728  }
729 
730  return $s;
731  }
732 
747  public function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
748  {
749  global $conf, $langs;
750 
751  //For backwards compatibility
752  if ($type == '0') {
753  $type = 'info';
754  } elseif ($type == '1') {
755  $type = 'help';
756  }
757 
758  if (preg_match('/onsmartphone$/', $tooltiptrigger) && empty($conf->dol_no_mouse_hover)) {
759  $tooltiptrigger = preg_replace('/^.*onsmartphone$/', '', $tooltiptrigger);
760  }
761 
762  $alt = '';
763  if ($tooltiptrigger) {
764  $alt = $langs->transnoentitiesnoconv("ClickToShowHelp");
765  }
766 
767  // If info or help with no javascript, show only text
768  if (empty($conf->use_javascript_ajax)) {
769  if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
770  return $text;
771  } else {
772  $alt = $htmltext;
773  $htmltext = '';
774  }
775  }
776 
777  // If info or help with smartphone, show only text (tooltip hover can't works)
778  if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) {
779  if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
780  return $text;
781  }
782  }
783  // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone)
784  //if (!empty($conf->dol_no_mouse_hover) && !empty($tooltiptrigger))
785  //{
786  //if ($type == 'info' || $type == 'help') return '<a href="'..'">'.$text.'</a>';
787  //}
788 
789  $img = '';
790  if ($type == 'info') {
791  $img = img_help(0, $alt);
792  } elseif ($type == 'help') {
793  $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
794  } elseif ($type == 'helpclickable') {
795  $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
796  } elseif ($type == 'superadmin') {
797  $img = img_picto($alt, 'redstar');
798  } elseif ($type == 'admin') {
799  $img = img_picto($alt, 'star');
800  } elseif ($type == 'warning') {
801  $img = img_warning($alt);
802  } elseif ($type != 'none') {
803  $img = img_picto($alt, $type); // $type can be an image path
804  }
805 
806  return $this->textwithtooltip($text, $htmltext, ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2), $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
807  }
808 
819  public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect')
820  {
821  global $conf, $langs, $hookmanager;
822 
823  $disabled = 0;
824  $ret = '<div class="centpercent center">';
825  $ret .= '<select class="flat' . (empty($conf->use_javascript_ajax) ? '' : ' hideobject') . ' ' . $name . ' ' . $name . 'select valignmiddle alignstart" id="' . $name . '" name="' . $name . '"' . ($disabled ? ' disabled="disabled"' : '') . '>';
826 
827  // Complete list with data from external modules. THe module can use $_SERVER['PHP_SELF'] to know on which page we are, or use the $parameters['currentcontext'] completed by executeHooks.
828  $parameters = array();
829  $reshook = $hookmanager->executeHooks('addMoreMassActions', $parameters); // Note that $action and $object may have been modified by hook
830  // check if there is a mass action
831  if (count($arrayofaction) == 0 && empty($hookmanager->resPrint)) {
832  return;
833  }
834  if (empty($reshook)) {
835  $ret .= '<option value="0"' . ($disabled ? ' disabled="disabled"' : '') . '>-- ' . $langs->trans("SelectAction") . ' --</option>';
836  foreach ($arrayofaction as $code => $label) {
837  $ret .= '<option value="' . $code . '"' . ($disabled ? ' disabled="disabled"' : '') . ' data-html="' . dol_escape_htmltag($label) . '">' . $label . '</option>';
838  }
839  }
840  $ret .= $hookmanager->resPrint;
841 
842  $ret .= '</select>';
843 
844  if (empty($conf->dol_optimize_smallscreen)) {
845  $ret .= ajax_combobox('.' . $name . 'select');
846  }
847 
848  // Warning: if you set submit button to disabled, post using 'Enter' will no more work if there is no another input submit. So we add a hidden button
849  $ret .= '<input type="submit" name="confirmmassactioninvisible" style="display: none" tabindex="-1">'; // Hidden button BEFORE so it is the one used when we submit with ENTER.
850  $ret .= '<input type="submit" disabled name="confirmmassaction"' . (empty($conf->use_javascript_ajax) ? '' : ' style="display: none"') . ' class="reposition button smallpaddingimp' . (empty($conf->use_javascript_ajax) ? '' : ' hideobject') . ' ' . $name . ' ' . $name . 'confirmed" value="' . dol_escape_htmltag($langs->trans("Confirm")) . '">';
851  $ret .= '</div>';
852 
853  if (!empty($conf->use_javascript_ajax)) {
854  $ret .= '<!-- JS CODE TO ENABLE mass action select -->
855  <script nonce="' . getNonce() . '">
856  function initCheckForSelect(mode, name, cssclass) /* mode is 0 during init of page or click all, 1 when we click on 1 checkboxi, "name" refers to the class of the massaction button, "cssclass" to the class of the checkfor select boxes */
857  {
858  atleastoneselected=0;
859  jQuery("."+cssclass).each(function( index ) {
860  /* console.log( index + ": " + $( this ).text() ); */
861  if ($(this).is(\':checked\')) atleastoneselected++;
862  });
863 
864  console.log("initCheckForSelect mode="+mode+" name="+name+" cssclass="+cssclass+" atleastoneselected="+atleastoneselected);
865 
866  if (atleastoneselected || ' . $alwaysvisible . ')
867  {
868  jQuery("."+name).show();
869  ' . ($selected ? 'if (atleastoneselected) { jQuery("."+name+"select").val("' . $selected . '").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', false); }' : '') . '
870  ' . ($selected ? 'if (! atleastoneselected) { jQuery("."+name+"select").val("0").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', true); } ' : '') . '
871  }
872  else
873  {
874  jQuery("."+name).hide();
875  jQuery("."+name+"other").hide();
876  }
877  }
878 
879  jQuery(document).ready(function () {
880  initCheckForSelect(0, "' . $name . '", "' . $cssclass . '");
881  jQuery(".' . $cssclass . '").click(function() {
882  initCheckForSelect(1, "' . $name . '", "' . $cssclass . '");
883  });
884  jQuery(".' . $name . 'select").change(function() {
885  var massaction = $( this ).val();
886  var urlform = $( this ).closest("form").attr("action").replace("#show_files","");
887  if (massaction == "builddoc")
888  {
889  urlform = urlform + "#show_files";
890  }
891  $( this ).closest("form").attr("action", urlform);
892  console.log("we select a mass action name=' . $name . ' massaction="+massaction+" - "+urlform);
893  /* Warning: if you set submit button to disabled, post using Enter will no more work if there is no other button */
894  if ($(this).val() != \'0\')
895  {
896  jQuery(".' . $name . 'confirmed").prop(\'disabled\', false);
897  jQuery(".' . $name . 'other").hide(); /* To disable if another div was open */
898  jQuery(".' . $name . '"+massaction).show();
899  }
900  else
901  {
902  jQuery(".' . $name . 'confirmed").prop(\'disabled\', true);
903  jQuery(".' . $name . 'other").hide(); /* To disable any div open */
904  }
905  });
906  });
907  </script>
908  ';
909  }
910 
911  return $ret;
912  }
913 
914  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
915 
932  public function select_country($selected = '', $htmlname = 'country_id', $htmloption = '', $maxlength = 0, $morecss = 'minwidth300', $usecodeaskey = '', $showempty = 1, $disablefavorites = 0, $addspecialentries = 0, $exclude_country_code = array(), $hideflags = 0)
933  {
934  // phpcs:enable
935  global $conf, $langs, $mysoc;
936 
937  $langs->load("dict");
938 
939  $out = '';
940  $countryArray = array();
941  $favorite = array();
942  $label = array();
943  $atleastonefavorite = 0;
944 
945  $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite, eec";
946  $sql .= " FROM " . $this->db->prefix() . "c_country";
947  $sql .= " WHERE active > 0";
948  //$sql.= " ORDER BY code ASC";
949 
950  dol_syslog(get_class($this) . "::select_country", LOG_DEBUG);
951  $resql = $this->db->query($sql);
952  if ($resql) {
953  $out .= '<select id="select' . $htmlname . '" class="flat maxwidth200onsmartphone selectcountry' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" ' . $htmloption . '>';
954  $num = $this->db->num_rows($resql);
955  $i = 0;
956  if ($num) {
957  while ($i < $num) {
958  $obj = $this->db->fetch_object($resql);
959 
960  $countryArray[$i]['rowid'] = $obj->rowid;
961  $countryArray[$i]['code_iso'] = $obj->code_iso;
962  $countryArray[$i]['code_iso3'] = $obj->code_iso3;
963  $countryArray[$i]['label'] = ($obj->code_iso && $langs->transnoentitiesnoconv("Country" . $obj->code_iso) != "Country" . $obj->code_iso ? $langs->transnoentitiesnoconv("Country" . $obj->code_iso) : ($obj->label != '-' ? $obj->label : ''));
964  $countryArray[$i]['favorite'] = $obj->favorite;
965  $countryArray[$i]['eec'] = $obj->eec;
966  $favorite[$i] = $obj->favorite;
967  $label[$i] = dol_string_unaccent($countryArray[$i]['label']);
968  $i++;
969  }
970 
971  if (empty($disablefavorites)) {
972  $array1_sort_order = SORT_DESC;
973  $array2_sort_order = SORT_ASC;
974  array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $countryArray);
975  } else {
976  $countryArray = dol_sort_array($countryArray, 'label');
977  }
978 
979  if ($showempty) {
980  if (is_numeric($showempty)) {
981  $out .= '<option value="">&nbsp;</option>' . "\n";
982  } else {
983  $out .= '<option value="-1">' . $langs->trans($showempty) . '</option>' . "\n";
984  }
985  }
986 
987  if ($addspecialentries) { // Add dedicated entries for groups of countries
988  //if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
989  $out .= '<option value="special_allnotme"' . ($selected == 'special_allnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
990  $out .= '<option value="special_eec"' . ($selected == 'special_eec' ? ' selected' : '') . '>' . $langs->trans("CountriesInEEC") . '</option>';
991  if ($mysoc->isInEEC()) {
992  $out .= '<option value="special_eecnotme"' . ($selected == 'special_eecnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
993  }
994  $out .= '<option value="special_noteec"' . ($selected == 'special_noteec' ? ' selected' : '') . '>' . $langs->trans("CountriesNotInEEC") . '</option>';
995  $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
996  }
997 
998  foreach ($countryArray as $row) {
999  //if (empty($showempty) && empty($row['rowid'])) continue;
1000  if (empty($row['rowid'])) {
1001  continue;
1002  }
1003  if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
1004  continue; // exclude some countries
1005  }
1006 
1007  if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
1008  $atleastonefavorite++;
1009  }
1010  if (empty($row['favorite']) && $atleastonefavorite) {
1011  $atleastonefavorite = 0;
1012  $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1013  }
1014 
1015  $labeltoshow = '';
1016  if ($row['label']) {
1017  $labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
1018  } else {
1019  $labeltoshow .= '&nbsp;';
1020  }
1021  if ($row['code_iso']) {
1022  $labeltoshow .= ' <span class="opacitymedium">(' . $row['code_iso'] . ')</span>';
1023  if (empty($hideflags)) {
1024  $tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
1025  $labeltoshow = $tmpflag . ' ' . $labeltoshow;
1026  }
1027  }
1028 
1029  if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
1030  $out .= '<option value="' . ($usecodeaskey ? ($usecodeaskey == 'code2' ? $row['code_iso'] : $row['code_iso3']) : $row['rowid']) . '" selected data-html="' . dol_escape_htmltag($labeltoshow) . '" data-eec="' . ((int) $row['eec']) . '">';
1031  } else {
1032  $out .= '<option value="' . ($usecodeaskey ? ($usecodeaskey == 'code2' ? $row['code_iso'] : $row['code_iso3']) : $row['rowid']) . '" data-html="' . dol_escape_htmltag($labeltoshow) . '" data-eec="' . ((int) $row['eec']) . '">';
1033  }
1034  $out .= $labeltoshow;
1035  $out .= '</option>' . "\n";
1036  }
1037  }
1038  $out .= '</select>';
1039  } else {
1040  dol_print_error($this->db);
1041  }
1042 
1043  // Make select dynamic
1044  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1045  $out .= ajax_combobox('select' . $htmlname, array(), 0, 0, 'resolve');
1046 
1047  return $out;
1048  }
1049 
1050  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1051 
1065  public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array(), $disableautocomplete = 0)
1066  {
1067  // phpcs:enable
1068  global $conf, $langs;
1069 
1070  $langs->load("dict");
1071 
1072  $out = '';
1073  $moreattrib = '';
1074  $incotermArray = array();
1075 
1076  $sql = "SELECT rowid, code";
1077  $sql .= " FROM " . $this->db->prefix() . "c_incoterms";
1078  $sql .= " WHERE active > 0";
1079  $sql .= " ORDER BY code ASC";
1080 
1081  dol_syslog(get_class($this) . "::select_incoterm", LOG_DEBUG);
1082  $resql = $this->db->query($sql);
1083  if ($resql) {
1084  if ($conf->use_javascript_ajax && !$forcecombo) {
1085  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1086  $out .= ajax_combobox($htmlname, $events);
1087  }
1088 
1089  if (!empty($page)) {
1090  $out .= '<form method="post" action="' . $page . '">';
1091  $out .= '<input type="hidden" name="action" value="set_incoterms">';
1092  $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
1093  }
1094 
1095  $out .= '<select id="' . $htmlname . '" class="flat selectincoterm width75" name="' . $htmlname . '" ' . $htmloption . '>';
1096  $out .= '<option value="0">&nbsp;</option>';
1097  $num = $this->db->num_rows($resql);
1098  $i = 0;
1099  if ($num) {
1100  while ($i < $num) {
1101  $obj = $this->db->fetch_object($resql);
1102  $incotermArray[$i]['rowid'] = $obj->rowid;
1103  $incotermArray[$i]['code'] = $obj->code;
1104  $i++;
1105  }
1106 
1107  foreach ($incotermArray as $row) {
1108  if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1109  $out .= '<option value="' . $row['rowid'] . '" selected>';
1110  } else {
1111  $out .= '<option value="' . $row['rowid'] . '">';
1112  }
1113 
1114  if ($row['code']) {
1115  $out .= $row['code'];
1116  }
1117 
1118  $out .= '</option>';
1119  }
1120  }
1121  $out .= '</select>';
1122 
1123  if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
1124  $out .= ajax_multiautocompleter('location_incoterms', array(), DOL_URL_ROOT . '/core/ajax/locationincoterms.php') . "\n";
1125  $moreattrib .= ' autocomplete="off"';
1126  }
1127  $out .= '<input id="location_incoterms" class="maxwidthonsmartphone type="text" name="location_incoterms" value="' . $location_incoterms . '">' . "\n";
1128 
1129  if (!empty($page)) {
1130  $out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="' . $langs->trans("Modify") . '"></form>';
1131  }
1132  } else {
1133  dol_print_error($this->db);
1134  }
1135 
1136  return $out;
1137  }
1138 
1139  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1140 
1152  public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0)
1153  {
1154  // phpcs:enable
1155  global $langs, $conf;
1156 
1157  // If product & services are enabled or both disabled.
1158  if ($forceall == 1 || (empty($forceall) && isModEnabled("product") && isModEnabled("service"))
1159  || (empty($forceall) && !isModEnabled('product') && !isModEnabled('service'))) {
1160  if (empty($hidetext)) {
1161  print $langs->trans("Type") . ': ';
1162  }
1163  print '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
1164  if ($showempty) {
1165  print '<option value="-1"';
1166  if ($selected == -1) {
1167  print ' selected';
1168  }
1169  print '>&nbsp;</option>';
1170  }
1171 
1172  print '<option value="0"';
1173  if (0 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'product')) {
1174  print ' selected';
1175  }
1176  print '>' . $langs->trans("Product");
1177 
1178  print '<option value="1"';
1179  if (1 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'service')) {
1180  print ' selected';
1181  }
1182  print '>' . $langs->trans("Service");
1183 
1184  print '</select>';
1185  print ajax_combobox('select_' . $htmlname);
1186  //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1187  }
1188  if ((empty($forceall) && !isModEnabled('product') && isModEnabled("service")) || $forceall == 3) {
1189  print $langs->trans("Service");
1190  print '<input type="hidden" name="' . $htmlname . '" value="1">';
1191  }
1192  if ((empty($forceall) && isModEnabled("product") && !isModEnabled('service')) || $forceall == 2) {
1193  print $langs->trans("Product");
1194  print '<input type="hidden" name="' . $htmlname . '" value="0">';
1195  }
1196  if ($forceall < 0) { // This should happened only for contracts when both predefined product and service are disabled.
1197  print '<input type="hidden" name="' . $htmlname . '" value="1">'; // By default we set on service for contract. If CONTRACT_SUPPORT_PRODUCTS is set, forceall should be 1 not -1
1198  }
1199  }
1200 
1201  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1202 
1208  public function load_cache_types_fees()
1209  {
1210  // phpcs:enable
1211  global $langs;
1212 
1213  $num = count($this->cache_types_fees);
1214  if ($num > 0) {
1215  return 0; // Cache already loaded
1216  }
1217 
1218  dol_syslog(__METHOD__, LOG_DEBUG);
1219 
1220  $langs->load("trips");
1221 
1222  $sql = "SELECT c.code, c.label";
1223  $sql .= " FROM " . $this->db->prefix() . "c_type_fees as c";
1224  $sql .= " WHERE active > 0";
1225 
1226  $resql = $this->db->query($sql);
1227  if ($resql) {
1228  $num = $this->db->num_rows($resql);
1229  $i = 0;
1230 
1231  while ($i < $num) {
1232  $obj = $this->db->fetch_object($resql);
1233 
1234  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1235  $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1236  $this->cache_types_fees[$obj->code] = $label;
1237  $i++;
1238  }
1239 
1240  asort($this->cache_types_fees);
1241 
1242  return $num;
1243  } else {
1244  dol_print_error($this->db);
1245  return -1;
1246  }
1247  }
1248 
1249  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1250 
1259  public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1260  {
1261  // phpcs:enable
1262  global $user, $langs;
1263 
1264  dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
1265 
1266  $this->load_cache_types_fees();
1267 
1268  print '<select id="select_' . $htmlname . '" class="flat" name="' . $htmlname . '">';
1269  if ($showempty) {
1270  print '<option value="-1"';
1271  if ($selected == -1) {
1272  print ' selected';
1273  }
1274  print '>&nbsp;</option>';
1275  }
1276 
1277  foreach ($this->cache_types_fees as $key => $value) {
1278  print '<option value="' . $key . '"';
1279  if ($key == $selected) {
1280  print ' selected';
1281  }
1282  print '>';
1283  print $value;
1284  print '</option>';
1285  }
1286 
1287  print '</select>';
1288  if ($user->admin) {
1289  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1290  }
1291  }
1292 
1293 
1294  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1295 
1317  public function select_company($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $limit = 0, $morecss = 'minwidth100', $moreparam = '', $selected_input_value = '', $hidelabel = 1, $ajaxoptions = array(), $multiple = false, $excludeids = array(), $showcode = 0)
1318  {
1319  // phpcs:enable
1320  global $conf, $user, $langs;
1321 
1322  $out = '';
1323 
1324  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && !$forcecombo) {
1325  if (is_null($ajaxoptions)) {
1326  $ajaxoptions = array();
1327  }
1328 
1329  require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1330 
1331  // No immediate load of all database
1332  $placeholder = '';
1333  if ($selected && empty($selected_input_value)) {
1334  require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1335  $societetmp = new Societe($this->db);
1336  $societetmp->fetch($selected);
1337  $selected_input_value = $societetmp->name;
1338  unset($societetmp);
1339  }
1340 
1341  // mode 1
1342  $urloption = 'htmlname=' . urlencode(str_replace('.', '_', $htmlname)) . '&outjson=1&filter=' . urlencode($filter) . (empty($excludeids) ? '' : '&excludeids=' . join(',', $excludeids)) . ($showtype ? '&showtype=' . urlencode($showtype) : '') . ($showcode ? '&showcode=' . urlencode($showcode) : '');
1343 
1344  $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1345  if (empty($hidelabel)) {
1346  print $langs->trans("RefOrLabel") . ' : ';
1347  } elseif ($hidelabel > 1) {
1348  $placeholder = $langs->trans("RefOrLabel");
1349  if ($hidelabel == 2) {
1350  $out .= img_picto($langs->trans("Search"), 'search');
1351  }
1352  }
1353  $out .= '<input type="text" class="' . $morecss . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '') . ' ' . (!empty($conf->global->THIRDPARTY_SEARCH_AUTOFOCUS) ? 'autofocus' : '') . ' />';
1354  if ($hidelabel == 3) {
1355  $out .= img_picto($langs->trans("Search"), 'search');
1356  }
1357 
1358  $out .= ajax_event($htmlname, $events);
1359 
1360  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/societe/ajax/company.php', $urloption, $conf->global->COMPANY_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
1361  } else {
1362  // Immediate load of all database
1363  $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids, $showcode);
1364  }
1365 
1366  return $out;
1367  }
1368 
1369  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1370 
1394  public function select_thirdparty_list($selected = '', $htmlname = 'socid', $filter = '', $showempty = '', $showtype = 0, $forcecombo = 0, $events = array(), $filterkey = '', $outputmode = 0, $limit = 0, $morecss = 'minwidth100', $moreparam = '', $multiple = false, $excludeids = array(), $showcode = 0)
1395  {
1396  // phpcs:enable
1397  global $conf, $user, $langs;
1398  global $hookmanager;
1399 
1400  $out = '';
1401  $num = 0;
1402  $outarray = array();
1403 
1404  if ($selected === '') {
1405  $selected = array();
1406  } elseif (!is_array($selected)) {
1407  $selected = array($selected);
1408  }
1409 
1410  // Clean $filter that may contains sql conditions so sql code
1411  if (function_exists('testSqlAndScriptInject')) {
1412  if (testSqlAndScriptInject($filter, 3) > 0) {
1413  $filter = '';
1414  return 'SQLInjectionTryDetected';
1415  }
1416  }
1417 
1418  if (preg_match('/[\‍(\‍)]/', $filter)) {
1419  // If there is one parenthesis inside the criteria, we assume it is an Universal Filter Syntax.
1420  $errormsg = '';
1421  $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
1422 
1423  // Redo clean $filter that may contains sql conditions so sql code
1424  if (function_exists('testSqlAndScriptInject')) {
1425  if (testSqlAndScriptInject($filter, 3) > 0) {
1426  $filter = '';
1427  return 'SQLInjectionTryDetected';
1428  }
1429  }
1430  } else {
1431  // If not, we do nothing. We already no that there is no parenthesis
1432  // TODO Disallow this case in a future.
1433  dol_syslog("Warning, select_thirdparty_list was called with a filter criteria not using the Universal Search Syntax.", LOG_WARNING);
1434  }
1435 
1436  // We search companies
1437  $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
1438  if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1439  $sql .= ", s.address, s.zip, s.town";
1440  $sql .= ", dictp.code as country_code";
1441  }
1442  $sql .= " FROM " . $this->db->prefix() . "societe as s";
1443  if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1444  $sql .= " LEFT JOIN " . $this->db->prefix() . "c_country as dictp ON dictp.rowid = s.fk_pays";
1445  }
1446  if (empty($user->rights->societe->client->voir) && !$user->socid) {
1447  $sql .= ", " . $this->db->prefix() . "societe_commerciaux as sc";
1448  }
1449  $sql .= " WHERE s.entity IN (" . getEntity('societe') . ")";
1450  if (!empty($user->socid)) {
1451  $sql .= " AND s.rowid = " . ((int) $user->socid);
1452  }
1453  if ($filter) {
1454  // $filter is safe because, if it contains '(' or ')', it has been sanitized by testSqlAndScriptInject() and forgeSQLFromUniversalSearchCriteria()
1455  // if not, by testSqlAndScriptInject() only.
1456  $sql .= " AND (" . $filter . ")";
1457  }
1458  if (empty($user->rights->societe->client->voir) && !$user->socid) {
1459  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id);
1460  }
1461  if (!empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) {
1462  $sql .= " AND s.status <> 0";
1463  }
1464  if (!empty($excludeids)) {
1465  $sql .= " AND s.rowid NOT IN (" . $this->db->sanitize(join(',', $excludeids)) . ")";
1466  }
1467  // Add where from hooks
1468  $parameters = array();
1469  $reshook = $hookmanager->executeHooks('selectThirdpartyListWhere', $parameters); // Note that $action and $object may have been modified by hook
1470  $sql .= $hookmanager->resPrint;
1471  // Add criteria
1472  if ($filterkey && $filterkey != '') {
1473  $sql .= " AND (";
1474  $prefix = empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
1475  // For natural search
1476  $scrit = explode(' ', $filterkey);
1477  $i = 0;
1478  if (count($scrit) > 1) {
1479  $sql .= "(";
1480  }
1481  foreach ($scrit as $crit) {
1482  if ($i > 0) {
1483  $sql .= " AND ";
1484  }
1485  $sql .= "(s.nom LIKE '" . $this->db->escape($prefix . $crit) . "%')";
1486  $i++;
1487  }
1488  if (count($scrit) > 1) {
1489  $sql .= ")";
1490  }
1491  if (isModEnabled('barcode')) {
1492  $sql .= " OR s.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
1493  }
1494  $sql .= " OR s.code_client LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.code_fournisseur LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
1495  $sql .= " OR s.name_alias LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.tva_intra LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
1496  $sql .= ")";
1497  }
1498  $sql .= $this->db->order("nom", "ASC");
1499  $sql .= $this->db->plimit($limit, 0);
1500 
1501  // Build output string
1502  dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
1503  $resql = $this->db->query($sql);
1504  if ($resql) {
1505  if (!$forcecombo) {
1506  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1507  $out .= ajax_combobox($htmlname, $events, getDolGlobalString("COMPANY_USE_SEARCH_TO_SELECT"));
1508  }
1509 
1510  // Construct $out and $outarray
1511  $out .= '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($moreparam ? ' ' . $moreparam : '') . ' name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . '>' . "\n";
1512 
1513  $textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
1514  if (!empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT)) {
1515  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
1516  //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
1517  if ($showempty && !is_numeric($showempty)) {
1518  $textifempty = $langs->trans($showempty);
1519  } else {
1520  $textifempty .= $langs->trans("All");
1521  }
1522  }
1523  if ($showempty) {
1524  $out .= '<option value="-1" data-html="' . dol_escape_htmltag('<span class="opacitymedium">' . ($textifempty ? $textifempty : '&nbsp;') . '</span>') . '">' . $textifempty . '</option>' . "\n";
1525  }
1526 
1527  $companytemp = new Societe($this->db);
1528 
1529  $num = $this->db->num_rows($resql);
1530  $i = 0;
1531  if ($num) {
1532  while ($i < $num) {
1533  $obj = $this->db->fetch_object($resql);
1534  $label = '';
1535  if ($showcode || !empty($conf->global->SOCIETE_ADD_REF_IN_LIST)) {
1536  if (($obj->client) && (!empty($obj->code_client))) {
1537  $label = $obj->code_client . ' - ';
1538  }
1539  if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
1540  $label .= $obj->code_fournisseur . ' - ';
1541  }
1542  $label .= ' ' . $obj->name;
1543  } else {
1544  $label = $obj->name;
1545  }
1546 
1547  if (!empty($obj->name_alias)) {
1548  $label .= ' (' . $obj->name_alias . ')';
1549  }
1550 
1551  if (!empty($conf->global->SOCIETE_SHOW_VAT_IN_LIST) && !empty($obj->tva_intra)) {
1552  $label .= ' - '.$obj->tva_intra;
1553  }
1554 
1555  $labelhtml = $label;
1556 
1557  if ($showtype) {
1558  $companytemp->id = $obj->rowid;
1559  $companytemp->client = $obj->client;
1560  $companytemp->fournisseur = $obj->fournisseur;
1561  $tmptype = $companytemp->getTypeUrl(1, '', 0, 'span');
1562  if ($tmptype) {
1563  $labelhtml .= ' ' . $tmptype;
1564  }
1565 
1566  if ($obj->client || $obj->fournisseur) {
1567  $label .= ' (';
1568  }
1569  if ($obj->client == 1 || $obj->client == 3) {
1570  $label .= $langs->trans("Customer");
1571  }
1572  if ($obj->client == 2 || $obj->client == 3) {
1573  $label .= ($obj->client == 3 ? ', ' : '') . $langs->trans("Prospect");
1574  }
1575  if ($obj->fournisseur) {
1576  $label .= ($obj->client ? ', ' : '') . $langs->trans("Supplier");
1577  }
1578  if ($obj->client || $obj->fournisseur) {
1579  $label .= ')';
1580  }
1581  }
1582 
1583  if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1584  $s = ($obj->address ? ' - ' . $obj->address : '') . ($obj->zip ? ' - ' . $obj->zip : '') . ($obj->town ? ' ' . $obj->town : '');
1585  if (!empty($obj->country_code)) {
1586  $s .= ', ' . $langs->trans('Country' . $obj->country_code);
1587  }
1588  $label .= $s;
1589  $labelhtml .= $s;
1590  }
1591 
1592  if (empty($outputmode)) {
1593  if (in_array($obj->rowid, $selected)) {
1594  $out .= '<option value="' . $obj->rowid . '" selected data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
1595  } else {
1596  $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
1597  }
1598  } else {
1599  array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label, 'labelhtml' => $labelhtml));
1600  }
1601 
1602  $i++;
1603  if (($i % 10) == 0) {
1604  $out .= "\n";
1605  }
1606  }
1607  }
1608  $out .= '</select>' . "\n";
1609  } else {
1610  dol_print_error($this->db);
1611  }
1612 
1613  $this->result = array('nbofthirdparties' => $num);
1614 
1615  if ($outputmode) {
1616  return $outarray;
1617  }
1618  return $out;
1619  }
1620 
1621 
1622  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1623 
1634  public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
1635  {
1636  // phpcs:enable
1637  global $langs, $conf;
1638 
1639  // On recherche les remises
1640  $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1641  $sql .= " re.description, re.fk_facture_source";
1642  $sql .= " FROM " . $this->db->prefix() . "societe_remise_except as re";
1643  $sql .= " WHERE re.fk_soc = " . (int) $socid;
1644  $sql .= " AND re.entity = " . $conf->entity;
1645  if ($filter) {
1646  $sql .= " AND " . $filter;
1647  }
1648  $sql .= " ORDER BY re.description ASC";
1649 
1650  dol_syslog(get_class($this) . "::select_remises", LOG_DEBUG);
1651  $resql = $this->db->query($sql);
1652  if ($resql) {
1653  print '<select id="select_' . $htmlname . '" class="flat maxwidthonsmartphone" name="' . $htmlname . '">';
1654  $num = $this->db->num_rows($resql);
1655 
1656  $qualifiedlines = $num;
1657 
1658  $i = 0;
1659  if ($num) {
1660  print '<option value="0">&nbsp;</option>';
1661  while ($i < $num) {
1662  $obj = $this->db->fetch_object($resql);
1663  $desc = dol_trunc($obj->description, 40);
1664  if (preg_match('/\‍(CREDIT_NOTE\‍)/', $desc)) {
1665  $desc = preg_replace('/\‍(CREDIT_NOTE\‍)/', $langs->trans("CreditNote"), $desc);
1666  }
1667  if (preg_match('/\‍(DEPOSIT\‍)/', $desc)) {
1668  $desc = preg_replace('/\‍(DEPOSIT\‍)/', $langs->trans("Deposit"), $desc);
1669  }
1670  if (preg_match('/\‍(EXCESS RECEIVED\‍)/', $desc)) {
1671  $desc = preg_replace('/\‍(EXCESS RECEIVED\‍)/', $langs->trans("ExcessReceived"), $desc);
1672  }
1673  if (preg_match('/\‍(EXCESS PAID\‍)/', $desc)) {
1674  $desc = preg_replace('/\‍(EXCESS PAID\‍)/', $langs->trans("ExcessPaid"), $desc);
1675  }
1676 
1677  $selectstring = '';
1678  if ($selected > 0 && $selected == $obj->rowid) {
1679  $selectstring = ' selected';
1680  }
1681 
1682  $disabled = '';
1683  if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
1684  $qualifiedlines--;
1685  $disabled = ' disabled';
1686  }
1687 
1688  if (!empty($conf->global->MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST) && !empty($obj->fk_facture_source)) {
1689  $tmpfac = new Facture($this->db);
1690  if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
1691  $desc = $desc . ' - ' . $tmpfac->ref;
1692  }
1693  }
1694 
1695  print '<option value="' . $obj->rowid . '"' . $selectstring . $disabled . '>' . $desc . ' (' . price($obj->amount_ht) . ' ' . $langs->trans("HT") . ' - ' . price($obj->amount_ttc) . ' ' . $langs->trans("TTC") . ')</option>';
1696  $i++;
1697  }
1698  }
1699  print '</select>';
1700  print ajax_combobox('select_' . $htmlname);
1701 
1702  return $qualifiedlines;
1703  } else {
1704  dol_print_error($this->db);
1705  return -1;
1706  }
1707  }
1708 
1709  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1710 
1731  public function select_contacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $showsoc = 0, $forcecombo = 0, $events = array(), $options_only = false, $moreparam = '', $htmlid = '')
1732  {
1733  // phpcs:enable
1734  print $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $morecss, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid);
1735  return $this->num;
1736  }
1737 
1762  public function selectcontacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $options_only = false, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false, $disableifempty = 0)
1763  {
1764  global $conf, $langs, $hookmanager, $action;
1765 
1766  $langs->load('companies');
1767 
1768  if (empty($htmlid)) {
1769  $htmlid = $htmlname;
1770  }
1771  $num = 0;
1772 
1773  if ($selected === '') {
1774  $selected = array();
1775  } elseif (!is_array($selected)) {
1776  $selected = array($selected);
1777  }
1778  $out = '';
1779 
1780  if (!is_object($hookmanager)) {
1781  include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
1782  $hookmanager = new HookManager($this->db);
1783  }
1784 
1785  // We search third parties
1786  $sql = "SELECT sp.rowid, sp.lastname, sp.statut, sp.firstname, sp.poste, sp.email, sp.phone, sp.phone_perso, sp.phone_mobile, sp.town AS contact_town";
1787  if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1788  $sql .= ", s.nom as company, s.town AS company_town";
1789  }
1790  $sql .= " FROM " . $this->db->prefix() . "socpeople as sp";
1791  if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1792  $sql .= " LEFT OUTER JOIN " . $this->db->prefix() . "societe as s ON s.rowid=sp.fk_soc";
1793  }
1794  $sql .= " WHERE sp.entity IN (" . getEntity('contact') . ")";
1795  if ($socid > 0 || $socid == -1) {
1796  $sql .= " AND sp.fk_soc = " . ((int) $socid);
1797  }
1798  if (!empty($conf->global->CONTACT_HIDE_INACTIVE_IN_COMBOBOX)) {
1799  $sql .= " AND sp.statut <> 0";
1800  }
1801  // Add where from hooks
1802  $parameters = array();
1803  $reshook = $hookmanager->executeHooks('selectContactListWhere', $parameters); // Note that $action and $object may have been modified by hook
1804  $sql .= $hookmanager->resPrint;
1805  $sql .= " ORDER BY sp.lastname ASC";
1806 
1807  dol_syslog(get_class($this) . "::selectcontacts", LOG_DEBUG);
1808  $resql = $this->db->query($sql);
1809  if ($resql) {
1810  $num = $this->db->num_rows($resql);
1811 
1812  if ($htmlname != 'none' && !$options_only) {
1813  $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlid . '" name="' . $htmlname . (($num || empty($disableifempty)) ? '' : ' disabled') . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . (!empty($moreparam) ? $moreparam : '') . '>';
1814  }
1815 
1816  if ($showempty && !is_numeric($showempty)) {
1817  $textforempty = $showempty;
1818  $out .= '<option class="optiongrey" value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>' . $textforempty . '</option>';
1819  } else {
1820  if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) {
1821  $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>&nbsp;</option>';
1822  }
1823  if ($showempty == 2) {
1824  $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>-- ' . $langs->trans("Internal") . ' --</option>';
1825  }
1826  }
1827 
1828  $i = 0;
1829  if ($num) {
1830  include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
1831  $contactstatic = new Contact($this->db);
1832 
1833  while ($i < $num) {
1834  $obj = $this->db->fetch_object($resql);
1835 
1836  // Set email (or phones) and town extended infos
1837  $extendedInfos = '';
1838  if (!empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1839  $extendedInfos = array();
1840  $email = trim($obj->email);
1841  if (!empty($email)) {
1842  $extendedInfos[] = $email;
1843  } else {
1844  $phone = trim($obj->phone);
1845  $phone_perso = trim($obj->phone_perso);
1846  $phone_mobile = trim($obj->phone_mobile);
1847  if (!empty($phone)) {
1848  $extendedInfos[] = $phone;
1849  }
1850  if (!empty($phone_perso)) {
1851  $extendedInfos[] = $phone_perso;
1852  }
1853  if (!empty($phone_mobile)) {
1854  $extendedInfos[] = $phone_mobile;
1855  }
1856  }
1857  $contact_town = trim($obj->contact_town);
1858  $company_town = trim($obj->company_town);
1859  if (!empty($contact_town)) {
1860  $extendedInfos[] = $contact_town;
1861  } elseif (!empty($company_town)) {
1862  $extendedInfos[] = $company_town;
1863  }
1864  $extendedInfos = implode(' - ', $extendedInfos);
1865  if (!empty($extendedInfos)) {
1866  $extendedInfos = ' - ' . $extendedInfos;
1867  }
1868  }
1869 
1870  $contactstatic->id = $obj->rowid;
1871  $contactstatic->lastname = $obj->lastname;
1872  $contactstatic->firstname = $obj->firstname;
1873  if ($obj->statut == 1) {
1874  if ($htmlname != 'none') {
1875  $disabled = 0;
1876  if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
1877  $disabled = 1;
1878  }
1879  if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
1880  $disabled = 1;
1881  }
1882  if (!empty($selected) && in_array($obj->rowid, $selected)) {
1883  $out .= '<option value="' . $obj->rowid . '"';
1884  if ($disabled) {
1885  $out .= ' disabled';
1886  }
1887  $out .= ' selected>';
1888  $out .= $contactstatic->getFullName($langs) . $extendedInfos;
1889  if ($showfunction && $obj->poste) {
1890  $out .= ' (' . $obj->poste . ')';
1891  }
1892  if (($showsoc > 0) && $obj->company) {
1893  $out .= ' - (' . $obj->company . ')';
1894  }
1895  $out .= '</option>';
1896  } else {
1897  $out .= '<option value="' . $obj->rowid . '"';
1898  if ($disabled) {
1899  $out .= ' disabled';
1900  }
1901  $out .= '>';
1902  $out .= $contactstatic->getFullName($langs) . $extendedInfos;
1903  if ($showfunction && $obj->poste) {
1904  $out .= ' (' . $obj->poste . ')';
1905  }
1906  if (($showsoc > 0) && $obj->company) {
1907  $out .= ' - (' . $obj->company . ')';
1908  }
1909  $out .= '</option>';
1910  }
1911  } else {
1912  if (in_array($obj->rowid, $selected)) {
1913  $out .= $contactstatic->getFullName($langs) . $extendedInfos;
1914  if ($showfunction && $obj->poste) {
1915  $out .= ' (' . $obj->poste . ')';
1916  }
1917  if (($showsoc > 0) && $obj->company) {
1918  $out .= ' - (' . $obj->company . ')';
1919  }
1920  }
1921  }
1922  }
1923  $i++;
1924  }
1925  } else {
1926  $labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
1927  $out .= '<option class="disabled" value="-1"' . (($showempty == 2 || $multiple) ? '' : ' selected') . ' disabled="disabled">';
1928  $out .= $labeltoshow;
1929  $out .= '</option>';
1930  }
1931 
1932  $parameters = array(
1933  'socid' => $socid,
1934  'htmlname' => $htmlname,
1935  'resql' => $resql,
1936  'out' => &$out,
1937  'showfunction' => $showfunction,
1938  'showsoc' => $showsoc,
1939  );
1940 
1941  $reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1942 
1943  if ($htmlname != 'none' && !$options_only) {
1944  $out .= '</select>';
1945  }
1946 
1947  if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
1948  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1949  $out .= ajax_combobox($htmlid, $events, getDolGlobalString("CONTACT_USE_SEARCH_TO_SELECT"));
1950  }
1951 
1952  $this->num = $num;
1953  return $out;
1954  } else {
1955  dol_print_error($this->db);
1956  return -1;
1957  }
1958  }
1959 
1960  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1961 
1977  public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0')
1978  {
1979  // phpcs:enable
1980  print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
1981  }
1982 
1983  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1984 
2009  public function select_dolusers($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $show_every = 0, $enableonlytext = '', $morecss = '', $notdisabled = 0, $outputmode = 0, $multiple = false, $forcecombo = 0)
2010  {
2011  // phpcs:enable
2012  global $conf, $user, $langs, $hookmanager;
2013  global $action;
2014 
2015  // If no preselected user defined, we take current user
2016  if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && empty($conf->global->SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE)) {
2017  $selected = $user->id;
2018  }
2019 
2020  if ($selected === '') {
2021  $selected = array();
2022  } elseif (!is_array($selected)) {
2023  $selected = array($selected);
2024  }
2025 
2026  $excludeUsers = null;
2027  $includeUsers = null;
2028 
2029  // Exclude some users
2030  if (is_array($exclude)) {
2031  $excludeUsers = implode(",", $exclude);
2032  }
2033  // Include some uses
2034  if (is_array($include)) {
2035  $includeUsers = implode(",", $include);
2036  } elseif ($include == 'hierarchy') {
2037  // Build list includeUsers to have only hierarchy
2038  $includeUsers = implode(",", $user->getAllChildIds(0));
2039  } elseif ($include == 'hierarchyme') {
2040  // Build list includeUsers to have only hierarchy and current user
2041  $includeUsers = implode(",", $user->getAllChildIds(1));
2042  }
2043 
2044  $out = '';
2045  $outarray = array();
2046  $outarray2 = array();
2047 
2048  // Forge request to select users
2049  $sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.photo";
2050  if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
2051  $sql .= ", e.label";
2052  }
2053  $sql .= " FROM " . $this->db->prefix() . "user as u";
2054  if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
2055  $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid = u.entity";
2056  if ($force_entity) {
2057  $sql .= " WHERE u.entity IN (0, " . $this->db->sanitize($force_entity) . ")";
2058  } else {
2059  $sql .= " WHERE u.entity IS NOT NULL";
2060  }
2061  } else {
2062  if (isModEnabled('multicompany') && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2063  $sql .= " LEFT JOIN " . $this->db->prefix() . "usergroup_user as ug";
2064  $sql .= " ON ug.fk_user = u.rowid";
2065  $sql .= " WHERE ug.entity = " . (int) $conf->entity;
2066  } else {
2067  $sql .= " WHERE u.entity IN (0, " . ((int) $conf->entity) . ")";
2068  }
2069  }
2070  if (!empty($user->socid)) {
2071  $sql .= " AND u.fk_soc = " . ((int) $user->socid);
2072  }
2073  if (is_array($exclude) && $excludeUsers) {
2074  $sql .= " AND u.rowid NOT IN (" . $this->db->sanitize($excludeUsers) . ")";
2075  }
2076  if ($includeUsers) {
2077  $sql .= " AND u.rowid IN (" . $this->db->sanitize($includeUsers) . ")";
2078  }
2079  if (!empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX) || $notdisabled) {
2080  $sql .= " AND u.statut <> 0";
2081  }
2082  if (!empty($morefilter)) {
2083  $sql .= " " . $morefilter;
2084  }
2085 
2086  //Add hook to filter on user (for exemple on usergroup define in custom modules)
2087  $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
2088  if (!empty($reshook)) {
2089  $sql .= $hookmanager->resPrint;
2090  }
2091 
2092  if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) { // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
2093  $sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
2094  } else {
2095  $sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
2096  }
2097 
2098  dol_syslog(get_class($this) . "::select_dolusers", LOG_DEBUG);
2099 
2100  $resql = $this->db->query($sql);
2101  if ($resql) {
2102  $num = $this->db->num_rows($resql);
2103  $i = 0;
2104  if ($num) {
2105  // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
2106  $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : ' minwidth200') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
2107  if ($show_empty && !$multiple) {
2108  $textforempty = ' ';
2109  if (!empty($conf->use_javascript_ajax)) {
2110  $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
2111  }
2112  if (!is_numeric($show_empty)) {
2113  $textforempty = $show_empty;
2114  }
2115  $out .= '<option class="optiongrey" value="' . ($show_empty < 0 ? $show_empty : -1) . '"' . ((empty($selected) || in_array(-1, $selected)) ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
2116  }
2117  if ($show_every) {
2118  $out .= '<option value="-2"' . ((in_array(-2, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("Everybody") . ' --</option>' . "\n";
2119  }
2120 
2121  $userstatic = new User($this->db);
2122 
2123  while ($i < $num) {
2124  $obj = $this->db->fetch_object($resql);
2125 
2126  $userstatic->id = $obj->rowid;
2127  $userstatic->lastname = $obj->lastname;
2128  $userstatic->firstname = $obj->firstname;
2129  $userstatic->photo = $obj->photo;
2130  $userstatic->statut = $obj->status;
2131  $userstatic->entity = $obj->entity;
2132  $userstatic->admin = $obj->admin;
2133 
2134  $disableline = '';
2135  if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
2136  $disableline = ($enableonlytext ? $enableonlytext : '1');
2137  }
2138 
2139  $labeltoshow = '';
2140  $labeltoshowhtml = '';
2141 
2142  // $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2143  $fullNameMode = 0;
2144  if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) {
2145  $fullNameMode = 1; //Firstname+lastname
2146  }
2147  $labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2148  $labeltoshowhtml .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2149  if (empty($obj->firstname) && empty($obj->lastname)) {
2150  $labeltoshow .= $obj->login;
2151  $labeltoshowhtml .= $obj->login;
2152  }
2153 
2154  // Complete name with a more info string like: ' (info1 - info2 - ...)'
2155  $moreinfo = '';
2156  $moreinfohtml = '';
2157  if (!empty($conf->global->MAIN_SHOW_LOGIN)) {
2158  $moreinfo .= ($moreinfo ? ' - ' : ' (');
2159  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(');
2160  $moreinfo .= $obj->login;
2161  $moreinfohtml .= $obj->login;
2162  }
2163  if ($showstatus >= 0) {
2164  if ($obj->status == 1 && $showstatus == 1) {
2165  $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Enabled');
2166  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Enabled');
2167  }
2168  if ($obj->status == 0 && $showstatus == 1) {
2169  $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Disabled');
2170  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Disabled');
2171  }
2172  }
2173  if (isModEnabled('multicompany') && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1 && $user->admin && !$user->entity) {
2174  if (!$obj->entity) {
2175  $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans("AllEntities");
2176  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans("AllEntities");
2177  } else {
2178  if ($obj->entity != $conf->entity) {
2179  $moreinfo .= ($moreinfo ? ' - ' : ' (') . ($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2180  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . ($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2181  }
2182  }
2183  }
2184  $moreinfo .= ($moreinfo ? ')' : '');
2185  $moreinfohtml .= ($moreinfohtml ? ')</span>' : '');
2186  if ($disableline && $disableline != '1') {
2187  // Add text from $enableonlytext parameter
2188  $moreinfo .= ' - ' . $disableline;
2189  $moreinfohtml .= ' - ' . $disableline;
2190  }
2191  $labeltoshow .= $moreinfo;
2192  $labeltoshowhtml .= $moreinfohtml;
2193 
2194  $out .= '<option value="' . $obj->rowid . '"';
2195  if ($disableline) {
2196  $out .= ' disabled';
2197  }
2198  if ((is_object($selected) && $selected->id == $obj->rowid) || (!is_object($selected) && in_array($obj->rowid, $selected))) {
2199  $out .= ' selected';
2200  }
2201  $out .= ' data-html="';
2202  $outhtml = $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1) . ' ';
2203  if ($showstatus >= 0 && $obj->status == 0) {
2204  $outhtml .= '<strike class="opacitymediumxxx">';
2205  }
2206  $outhtml .= $labeltoshowhtml;
2207  if ($showstatus >= 0 && $obj->status == 0) {
2208  $outhtml .= '</strike>';
2209  }
2210  $out .= dol_escape_htmltag($outhtml);
2211  $out .= '">';
2212  $out .= $labeltoshow;
2213  $out .= '</option>';
2214 
2215  $outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength) . $moreinfo;
2216  $outarray2[$userstatic->id] = array(
2217  'id'=>$userstatic->id,
2218  'label'=>$labeltoshow,
2219  'labelhtml'=>$labeltoshowhtml,
2220  'color'=>'',
2221  'picto'=>''
2222  );
2223 
2224  $i++;
2225  }
2226  } else {
2227  $out .= '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '" disabled>';
2228  $out .= '<option value="">' . $langs->trans("None") . '</option>';
2229  }
2230  $out .= '</select>';
2231 
2232  if ($num && !$forcecombo) {
2233  // Enhance with select2
2234  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2235  $out .= ajax_combobox($htmlname);
2236  }
2237  } else {
2238  dol_print_error($this->db);
2239  }
2240 
2241  if ($outputmode == 2) {
2242  return $outarray2;
2243  } elseif ($outputmode) {
2244  return $outarray;
2245  }
2246 
2247  return $out;
2248  }
2249 
2250 
2251  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2252 
2275  public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array())
2276  {
2277  // phpcs:enable
2278  global $conf, $user, $langs;
2279 
2280  $userstatic = new User($this->db);
2281  $out = '';
2282 
2283 
2284  $assignedtouser = array();
2285  if (!empty($_SESSION['assignedtouser'])) {
2286  $assignedtouser = json_decode($_SESSION['assignedtouser'], true);
2287  }
2288  $nbassignetouser = count($assignedtouser);
2289 
2290  //if ($nbassignetouser && $action != 'view') $out .= '<br>';
2291  if ($nbassignetouser) {
2292  $out .= '<ul class="attendees">';
2293  }
2294  $i = 0;
2295  $ownerid = 0;
2296  foreach ($assignedtouser as $key => $value) {
2297  if ($value['id'] == $ownerid) {
2298  continue;
2299  }
2300 
2301  $out .= '<li>';
2302  $userstatic->fetch($value['id']);
2303  $out .= $userstatic->getNomUrl(-1);
2304  if ($i == 0) {
2305  $ownerid = $value['id'];
2306  $out .= ' (' . $langs->trans("Owner") . ')';
2307  }
2308  if ($nbassignetouser > 1 && $action != 'view') {
2309  $out .= ' <input type="image" style="border: 0px;" src="' . img_picto($langs->trans("Remove"), 'delete', '', 0, 1) . '" value="' . $userstatic->id . '" class="removedassigned reposition" id="removedassigned_' . $userstatic->id . '" name="removedassigned_' . $userstatic->id . '">';
2310  }
2311  // Show my availability
2312  if ($showproperties) {
2313  if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
2314  $out .= '<div class="myavailability inline-block">';
2315  $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;<span class="opacitymedium">' . $langs->trans("Availability") . ':</span> </span><input id="transparency" class="paddingrightonly" ' . ($action == 'view' ? 'disabled' : '') . ' type="checkbox" name="transparency"' . ($listofuserid[$ownerid]['transparency'] ? ' checked' : '') . '><label for="transparency">' . $langs->trans("Busy") . '</label>';
2316  $out .= '</div>';
2317  }
2318  }
2319  //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
2320  //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
2321 
2322  $out .= '</li>';
2323  $i++;
2324  }
2325  if ($nbassignetouser) {
2326  $out .= '</ul>';
2327  }
2328 
2329  // Method with no ajax
2330  if ($action != 'view') {
2331  $out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
2332  $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
2333  $out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
2334  $out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
2335  $out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#' . $action . 'assignedtouser").attr("disabled", false); }';
2336  $out .= ' else { jQuery("#' . $action . 'assignedtouser").attr("disabled", true); }';
2337  $out .= '});';
2338  $out .= '})</script>';
2339  $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
2340  $out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp reposition" id="' . $action . 'assignedtouser" name="' . $action . 'assignedtouser" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
2341  $out .= '<br>';
2342  }
2343 
2344  return $out;
2345  }
2346 
2347 
2348  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2349 
2377  public function select_produits($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 0, $price_level = 0, $status = 1, $finished = 2, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '', $selected_combinations = null, $nooutput = 0, $status_purchase = -1)
2378  {
2379  // phpcs:enable
2380  global $langs, $conf;
2381 
2382  $out = '';
2383 
2384  // check parameters
2385  $price_level = (!empty($price_level) ? $price_level : 0);
2386  if (is_null($ajaxoptions)) {
2387  $ajaxoptions = array();
2388  }
2389 
2390  if (strval($filtertype) === '' && (isModEnabled("product") || isModEnabled("service"))) {
2391  if (isModEnabled("product") && !isModEnabled('service')) {
2392  $filtertype = '0';
2393  } elseif (!isModEnabled('product') && isModEnabled("service")) {
2394  $filtertype = '1';
2395  }
2396  }
2397 
2398  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2399  $placeholder = '';
2400 
2401  if ($selected && empty($selected_input_value)) {
2402  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
2403  $producttmpselect = new Product($this->db);
2404  $producttmpselect->fetch($selected);
2405  $selected_input_value = $producttmpselect->ref;
2406  unset($producttmpselect);
2407  }
2408  // handle case where product or service module is disabled + no filter specified
2409  if ($filtertype == '') {
2410  if (!isModEnabled('product')) { // when product module is disabled, show services only
2411  $filtertype = 1;
2412  } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
2413  $filtertype = 0;
2414  }
2415  }
2416  // mode=1 means customers products
2417  $urloption = ($socid > 0 ? 'socid=' . $socid . '&' : '') . 'htmlname=' . $htmlname . '&outjson=1&price_level=' . $price_level . '&type=' . $filtertype . '&mode=1&status=' . $status . '&status_purchase=' . $status_purchase . '&finished=' . $finished . '&hidepriceinlabel=' . $hidepriceinlabel . '&warehousestatus=' . $warehouseStatus;
2418  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
2419 
2420  if (isModEnabled('variants') && is_array($selected_combinations)) {
2421  // Code to automatically insert with javascript the select of attributes under the select of product
2422  // when a parent of variant has been selected.
2423  $out .= '
2424  <!-- script to auto show attributes select tags if a variant was selected -->
2425  <script nonce="' . getNonce() . '">
2426  // auto show attributes fields
2427  selected = ' . json_encode($selected_combinations) . ';
2428  combvalues = {};
2429 
2430  jQuery(document).ready(function () {
2431 
2432  jQuery("input[name=\'prod_entry_mode\']").change(function () {
2433  if (jQuery(this).val() == \'free\') {
2434  jQuery(\'div#attributes_box\').empty();
2435  }
2436  });
2437 
2438  jQuery("input#' . $htmlname . '").change(function () {
2439 
2440  if (!jQuery(this).val()) {
2441  jQuery(\'div#attributes_box\').empty();
2442  return;
2443  }
2444 
2445  console.log("A change has started. We get variants fields to inject html select");
2446 
2447  jQuery.getJSON("' . DOL_URL_ROOT . '/variants/ajax/getCombinations.php", {
2448  id: jQuery(this).val()
2449  }, function (data) {
2450  jQuery(\'div#attributes_box\').empty();
2451 
2452  jQuery.each(data, function (key, val) {
2453 
2454  combvalues[val.id] = val.values;
2455 
2456  var span = jQuery(document.createElement(\'div\')).css({
2457  \'display\': \'table-row\'
2458  });
2459 
2460  span.append(
2461  jQuery(document.createElement(\'div\')).text(val.label).css({
2462  \'font-weight\': \'bold\',
2463  \'display\': \'table-cell\'
2464  })
2465  );
2466 
2467  var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
2468  \'margin-left\': \'15px\',
2469  \'white-space\': \'pre\'
2470  }).append(
2471  jQuery(document.createElement(\'option\')).val(\'\')
2472  );
2473 
2474  jQuery.each(combvalues[val.id], function (key, val) {
2475  var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
2476 
2477  if (selected[val.fk_product_attribute] == val.id) {
2478  tag.attr(\'selected\', \'selected\');
2479  }
2480 
2481  html.append(tag);
2482  });
2483 
2484  span.append(html);
2485  jQuery(\'div#attributes_box\').append(span);
2486  });
2487  })
2488  });
2489 
2490  ' . ($selected ? 'jQuery("input#' . $htmlname . '").change();' : '') . '
2491  });
2492  </script>
2493  ';
2494  }
2495 
2496  if (empty($hidelabel)) {
2497  $out .= $langs->trans("RefOrLabel") . ' : ';
2498  } elseif ($hidelabel > 1) {
2499  $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
2500  if ($hidelabel == 2) {
2501  $out .= img_picto($langs->trans("Search"), 'search');
2502  }
2503  }
2504  $out .= '<input type="text" class="minwidth100' . ($morecss ? ' ' . $morecss : '') . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '') . ' />';
2505  if ($hidelabel == 3) {
2506  $out .= img_picto($langs->trans("Search"), 'search');
2507  }
2508  } else {
2509  $out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus, $status_purchase);
2510  }
2511 
2512  if (empty($nooutput)) {
2513  print $out;
2514  } else {
2515  return $out;
2516  }
2517  }
2518 
2519  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2520 
2536  public function select_bom($selected = '', $htmlname = 'bom_id', $limit = 0, $status = 1, $type = 0, $showempty = '1', $morecss = '', $nooutput = '', $forcecombo = 0, $TProducts = [])
2537  {
2538  // phpcs:enable
2539  global $conf, $user, $langs, $db;
2540 
2541  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
2542 
2543  $error = 0;
2544  $out = '';
2545 
2546  if (!$forcecombo) {
2547  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2548  $events = array();
2549  $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
2550  }
2551 
2552  $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
2553 
2554  $sql = 'SELECT b.rowid, b.ref, b.label, b.fk_product';
2555  $sql .= ' FROM ' . MAIN_DB_PREFIX . 'bom_bom as b';
2556  $sql .= ' WHERE b.entity IN (' . getEntity('bom') . ')';
2557  if (!empty($status)) $sql .= ' AND status = ' . (int) $status;
2558  if (!empty($type)) $sql .= ' AND bomtype = ' . (int) $type;
2559  if (!empty($TProducts)) $sql .= ' AND fk_product IN (' . $this->db->sanitize(implode(',', $TProducts)) . ')';
2560  if (!empty($limit)) $sql .= ' LIMIT ' . (int) $limit;
2561  $resql = $db->query($sql);
2562  if ($resql) {
2563  if ($showempty) {
2564  $out .= '<option value="-1"';
2565  if (empty($selected)) $out .= ' selected';
2566  $out .= '>&nbsp;</option>';
2567  }
2568  while ($obj = $db->fetch_object($resql)) {
2569  $product = new Product($db);
2570  $res = $product->fetch($obj->fk_product);
2571  $out .= '<option value="' . $obj->rowid . '"';
2572  if ($obj->rowid == $selected) $out .= 'selected';
2573  $out .= '>' . $obj->ref . ' - ' . $product->label . ' - ' . $obj->label . '</option>';
2574  }
2575  } else {
2576  $error++;
2577  dol_print_error($db);
2578  }
2579  $out .= '</select>';
2580  if (empty($nooutput)) {
2581  print $out;
2582  } else {
2583  return $out;
2584  }
2585  }
2586 
2587  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2588 
2614  public function select_produits_list($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 20, $price_level = 0, $filterkey = '', $status = 1, $finished = 2, $outputmode = 0, $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '', $status_purchase = -1)
2615  {
2616  // phpcs:enable
2617  global $langs, $conf;
2618  global $hookmanager;
2619 
2620  $out = '';
2621  $outarray = array();
2622 
2623  // Units
2624  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2625  $langs->load('other');
2626  }
2627 
2628  $warehouseStatusArray = array();
2629  if (!empty($warehouseStatus)) {
2630  require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
2631  if (preg_match('/warehouseclosed/', $warehouseStatus)) {
2632  $warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
2633  }
2634  if (preg_match('/warehouseopen/', $warehouseStatus)) {
2635  $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
2636  }
2637  if (preg_match('/warehouseinternal/', $warehouseStatus)) {
2638  $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
2639  }
2640  }
2641 
2642  $selectFields = " p.rowid, p.ref, p.label, p.description, p.barcode, p.fk_country, p.fk_product_type, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.default_vat_code, p.duration, p.fk_price_expression";
2643  if (count($warehouseStatusArray)) {
2644  $selectFieldsGrouped = ", sum(" . $this->db->ifsql("e.statut IS NULL", "0", "ps.reel") . ") as stock"; // e.statut is null if there is no record in stock
2645  } else {
2646  $selectFieldsGrouped = ", " . $this->db->ifsql("p.stock IS NULL", 0, "p.stock") . " AS stock";
2647  }
2648 
2649  $sql = "SELECT ";
2650 
2651  // Add select from hooks
2652  $parameters = array();
2653  $reshook = $hookmanager->executeHooks('selectProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
2654  if (empty($reshook)) {
2655  $sql .= $selectFields.$selectFieldsGrouped.$hookmanager->resPrint;
2656  } else {
2657  $sql .= $hookmanager->resPrint;
2658  }
2659 
2660  if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2661  //Product category
2662  $sql .= ", (SELECT " . $this->db->prefix() . "categorie_product.fk_categorie
2663  FROM " . $this->db->prefix() . "categorie_product
2664  WHERE " . $this->db->prefix() . "categorie_product.fk_product=p.rowid
2665  LIMIT 1
2666  ) AS categorie_product_id ";
2667  }
2668 
2669  //Price by customer
2670  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2671  $sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
2672  $sql .= ' pcp.price_base_type as custprice_base_type, pcp.tva_tx as custtva_tx, pcp.default_vat_code as custdefault_vat_code, pcp.ref_customer as custref';
2673  $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custdefault_vat_code, custref";
2674  }
2675  // Units
2676  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2677  $sql .= ", u.label as unit_long, u.short_label as unit_short, p.weight, p.weight_units, p.length, p.length_units, p.width, p.width_units, p.height, p.height_units, p.surface, p.surface_units, p.volume, p.volume_units";
2678  $selectFields .= ', unit_long, unit_short, p.weight, p.weight_units, p.length, p.length_units, p.width, p.width_units, p.height, p.height_units, p.surface, p.surface_units, p.volume, p.volume_units';
2679  }
2680 
2681  // Multilang : we add translation
2682  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2683  $sql .= ", pl.label as label_translated";
2684  $sql .= ", pl.description as description_translated";
2685  $selectFields .= ", label_translated";
2686  $selectFields .= ", description_translated";
2687  }
2688  // Price by quantity
2689  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2690  $sql .= ", (SELECT pp.rowid FROM " . $this->db->prefix() . "product_price as pp WHERE pp.fk_product = p.rowid";
2691  if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2692  $sql .= " AND price_level = " . ((int) $price_level);
2693  }
2694  $sql .= " ORDER BY date_price";
2695  $sql .= " DESC LIMIT 1) as price_rowid";
2696  $sql .= ", (SELECT pp.price_by_qty FROM " . $this->db->prefix() . "product_price as pp WHERE pp.fk_product = p.rowid"; // price_by_qty is 1 if some prices by qty exists in subtable
2697  if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2698  $sql .= " AND price_level = " . ((int) $price_level);
2699  }
2700  $sql .= " ORDER BY date_price";
2701  $sql .= " DESC LIMIT 1) as price_by_qty";
2702  $selectFields .= ", price_rowid, price_by_qty";
2703  }
2704 
2705  $sql .= " FROM ".$this->db->prefix()."product as p";
2706  // Add from (left join) from hooks
2707  $parameters = array();
2708  $reshook = $hookmanager->executeHooks('selectProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
2709  $sql .= $hookmanager->resPrint;
2710 
2711  if (count($warehouseStatusArray)) {
2712  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_stock as ps on ps.fk_product = p.rowid";
2713  $sql .= " LEFT JOIN " . $this->db->prefix() . "entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (" . getEntity('stock') . ")";
2714  $sql .= ' AND e.statut IN (' . $this->db->sanitize($this->db->escape(implode(',', $warehouseStatusArray))) . ')'; // Return line if product is inside the selected stock. If not, an empty line will be returned so we will count 0.
2715  }
2716 
2717  // include search in supplier ref
2718  if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2719  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
2720  }
2721 
2722  //Price by customer
2723  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2724  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_customer_price as pcp ON pcp.fk_soc=" . ((int) $socid) . " AND pcp.fk_product=p.rowid";
2725  }
2726  // Units
2727  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2728  $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units u ON u.rowid = p.fk_unit";
2729  }
2730  // Multilang : we add translation
2731  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2732  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_lang as pl ON pl.fk_product = p.rowid ";
2733  if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && !empty($socid)) {
2734  require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
2735  $soc = new Societe($this->db);
2736  $result = $soc->fetch($socid);
2737  if ($result > 0 && !empty($soc->default_lang)) {
2738  $sql .= " AND pl.lang = '" . $this->db->escape($soc->default_lang) . "'";
2739  } else {
2740  $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
2741  }
2742  } else {
2743  $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
2744  }
2745  }
2746 
2747  if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2748  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_attribute_combination pac ON pac.fk_product_child = p.rowid";
2749  }
2750 
2751  $sql .= ' WHERE p.entity IN (' . getEntity('product') . ')';
2752 
2753  if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2754  $sql .= " AND pac.rowid IS NULL";
2755  }
2756 
2757  if ($finished == 0) {
2758  $sql .= " AND p.finished = " . ((int) $finished);
2759  } elseif ($finished == 1) {
2760  $sql .= " AND p.finished = " . ((int) $finished);
2761  if ($status >= 0) {
2762  $sql .= " AND p.tosell = " . ((int) $status);
2763  }
2764  } elseif ($status >= 0) {
2765  $sql .= " AND p.tosell = " . ((int) $status);
2766  }
2767  if ($status_purchase >= 0) {
2768  $sql .= " AND p.tobuy = " . ((int) $status_purchase);
2769  }
2770  // Filter by product type
2771  if (strval($filtertype) != '') {
2772  $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
2773  } elseif (!isModEnabled('product')) { // when product module is disabled, show services only
2774  $sql .= " AND p.fk_product_type = 1";
2775  } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
2776  $sql .= " AND p.fk_product_type = 0";
2777  }
2778  // Add where from hooks
2779  $parameters = array();
2780  $reshook = $hookmanager->executeHooks('selectProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
2781  $sql .= $hookmanager->resPrint;
2782  // Add criteria on ref/label
2783  if ($filterkey != '') {
2784  $sql .= ' AND (';
2785  $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
2786  // For natural search
2787  $scrit = explode(' ', $filterkey);
2788  $i = 0;
2789  if (count($scrit) > 1) {
2790  $sql .= "(";
2791  }
2792  foreach ($scrit as $crit) {
2793  if ($i > 0) {
2794  $sql .= " AND ";
2795  }
2796  $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
2797  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2798  $sql .= " OR pl.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
2799  }
2800  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2801  $sql .= " OR pcp.ref_customer LIKE '" . $this->db->escape($prefix . $crit) . "%'";
2802  }
2803  if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION)) {
2804  $sql .= " OR p.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
2805  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2806  $sql .= " OR pl.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
2807  }
2808  }
2809  if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2810  $sql .= " OR pfp.ref_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
2811  }
2812  $sql .= ")";
2813  $i++;
2814  }
2815  if (count($scrit) > 1) {
2816  $sql .= ")";
2817  }
2818  if (isModEnabled('barcode')) {
2819  $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2820  }
2821  $sql .= ')';
2822  }
2823  if (count($warehouseStatusArray)) {
2824  $sql .= " GROUP BY " . $selectFields;
2825  }
2826 
2827  //Sort by category
2828  if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2829  $sql .= " ORDER BY categorie_product_id ";
2830  //ASC OR DESC order
2831  ($conf->global->PRODUCT_SORT_BY_CATEGORY == 1) ? $sql .= "ASC" : $sql .= "DESC";
2832  } else {
2833  $sql .= $this->db->order("p.ref");
2834  }
2835 
2836  $sql .= $this->db->plimit($limit, 0);
2837 
2838  // Build output string
2839  dol_syslog(get_class($this) . "::select_produits_list search products", LOG_DEBUG);
2840  $result = $this->db->query($sql);
2841  if ($result) {
2842  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
2843  require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
2844  require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
2845 
2846  $num = $this->db->num_rows($result);
2847 
2848  $events = null;
2849 
2850  if (!$forcecombo) {
2851  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2852  $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
2853  }
2854 
2855  $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
2856 
2857  $textifempty = '';
2858  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2859  //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2860  if (!empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2861  if ($showempty && !is_numeric($showempty)) {
2862  $textifempty = $langs->trans($showempty);
2863  } else {
2864  $textifempty .= $langs->trans("All");
2865  }
2866  } else {
2867  if ($showempty && !is_numeric($showempty)) {
2868  $textifempty = $langs->trans($showempty);
2869  }
2870  }
2871  if ($showempty) {
2872  $out .= '<option value="-1" selected>' . ($textifempty ? $textifempty : '&nbsp;') . '</option>';
2873  }
2874 
2875  $i = 0;
2876  while ($num && $i < $num) {
2877  $opt = '';
2878  $optJson = array();
2879  $objp = $this->db->fetch_object($result);
2880 
2881  if ((!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) && !empty($objp->price_by_qty) && $objp->price_by_qty == 1) { // Price by quantity will return many prices for the same product
2882  $sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
2883  $sql .= " FROM " . $this->db->prefix() . "product_price_by_qty";
2884  $sql .= " WHERE fk_product_price = " . ((int) $objp->price_rowid);
2885  $sql .= " ORDER BY quantity ASC";
2886 
2887  dol_syslog(get_class($this) . "::select_produits_list search prices by qty", LOG_DEBUG);
2888  $result2 = $this->db->query($sql);
2889  if ($result2) {
2890  $nb_prices = $this->db->num_rows($result2);
2891  $j = 0;
2892  while ($nb_prices && $j < $nb_prices) {
2893  $objp2 = $this->db->fetch_object($result2);
2894 
2895  $objp->price_by_qty_rowid = $objp2->rowid;
2896  $objp->price_by_qty_price_base_type = $objp2->price_base_type;
2897  $objp->price_by_qty_quantity = $objp2->quantity;
2898  $objp->price_by_qty_unitprice = $objp2->unitprice;
2899  $objp->price_by_qty_remise_percent = $objp2->remise_percent;
2900  // For backward compatibility
2901  $objp->quantity = $objp2->quantity;
2902  $objp->price = $objp2->price;
2903  $objp->unitprice = $objp2->unitprice;
2904  $objp->remise_percent = $objp2->remise_percent;
2905 
2906  //$objp->tva_tx is not overwritten by $objp2 value
2907  //$objp->default_vat_code is not overwritten by $objp2 value
2908 
2909  $this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
2910 
2911  $j++;
2912 
2913  // Add new entry
2914  // "key" value of json key array is used by jQuery automatically as selected value
2915  // "label" value of json key array is used by jQuery automatically as text for combo box
2916  $out .= $opt;
2917  array_push($outarray, $optJson);
2918  }
2919  }
2920  } else {
2921  if (isModEnabled('dynamicprices') && !empty($objp->fk_price_expression)) {
2922  $price_product = new Product($this->db);
2923  $price_product->fetch($objp->rowid, '', '', 1);
2924 
2925  require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
2926  $priceparser = new PriceParser($this->db);
2927  $price_result = $priceparser->parseProduct($price_product);
2928  if ($price_result >= 0) {
2929  $objp->price = $price_result;
2930  $objp->unitprice = $price_result;
2931  //Calculate the VAT
2932  $objp->price_ttc = price2num($objp->price) * (1 + ($objp->tva_tx / 100));
2933  $objp->price_ttc = price2num($objp->price_ttc, 'MU');
2934  }
2935  }
2936 
2937  $this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
2938  // Add new entry
2939  // "key" value of json key array is used by jQuery automatically as selected value
2940  // "label" value of json key array is used by jQuery automatically as text for combo box
2941  $out .= $opt;
2942  array_push($outarray, $optJson);
2943  }
2944 
2945  $i++;
2946  }
2947 
2948  $out .= '</select>';
2949 
2950  $this->db->free($result);
2951 
2952  if (empty($outputmode)) {
2953  return $out;
2954  }
2955 
2956  return $outarray;
2957  } else {
2958  dol_print_error($this->db);
2959  }
2960 
2961  return '';
2962  }
2963 
2979  protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
2980  {
2981  global $langs, $conf, $user;
2982  global $hookmanager;
2983 
2984  $outkey = '';
2985  $outval = '';
2986  $outref = '';
2987  $outlabel = '';
2988  $outlabel_translated = '';
2989  $outdesc = '';
2990  $outdesc_translated = '';
2991  $outbarcode = '';
2992  $outorigin = '';
2993  $outtype = '';
2994  $outprice_ht = '';
2995  $outprice_ttc = '';
2996  $outpricebasetype = '';
2997  $outtva_tx = '';
2998  $outdefault_vat_code = '';
2999  $outqty = 1;
3000  $outdiscount = 0;
3001 
3002  $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3003 
3004  $label = $objp->label;
3005  if (!empty($objp->label_translated)) {
3006  $label = $objp->label_translated;
3007  }
3008  if (!empty($filterkey) && $filterkey != '') {
3009  $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
3010  }
3011 
3012  $outkey = $objp->rowid;
3013  $outref = $objp->ref;
3014  $outrefcust = empty($objp->custref) ? '' : $objp->custref;
3015  $outlabel = $objp->label;
3016  $outdesc = $objp->description;
3017  if (getDolGlobalInt('MAIN_MULTILANGS')) {
3018  $outlabel_translated = $objp->label_translated;
3019  $outdesc_translated = $objp->description_translated;
3020  }
3021  $outbarcode = $objp->barcode;
3022  $outorigin = $objp->fk_country;
3023  $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
3024 
3025  $outtype = $objp->fk_product_type;
3026  $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3027  $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
3028 
3029  if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
3030  require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
3031  }
3032 
3033  // Units
3034  $outvalUnits = '';
3035  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3036  if (!empty($objp->unit_short)) {
3037  $outvalUnits .= ' - ' . $objp->unit_short;
3038  }
3039  }
3040  if (!empty($conf->global->PRODUCT_SHOW_DIMENSIONS_IN_COMBO)) {
3041  if (!empty($objp->weight) && $objp->weight_units !== null) {
3042  $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3043  $outvalUnits .= ' - ' . $unitToShow;
3044  }
3045  if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3046  $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
3047  $outvalUnits .= ' - ' . $unitToShow;
3048  }
3049  if (!empty($objp->surface) && $objp->surface_units !== null) {
3050  $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3051  $outvalUnits .= ' - ' . $unitToShow;
3052  }
3053  if (!empty($objp->volume) && $objp->volume_units !== null) {
3054  $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3055  $outvalUnits .= ' - ' . $unitToShow;
3056  }
3057  }
3058  if ($outdurationvalue && $outdurationunit) {
3059  $da = array(
3060  'h' => $langs->trans('Hour'),
3061  'd' => $langs->trans('Day'),
3062  'w' => $langs->trans('Week'),
3063  'm' => $langs->trans('Month'),
3064  'y' => $langs->trans('Year')
3065  );
3066  if (isset($da[$outdurationunit])) {
3067  $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
3068  }
3069  }
3070 
3071  $opt = '<option value="' . $objp->rowid . '"';
3072  $opt .= ($objp->rowid == $selected) ? ' selected' : '';
3073  if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
3074  $opt .= ' pbq="' . $objp->price_by_qty_rowid . '" data-pbq="' . $objp->price_by_qty_rowid . '" data-pbqup="' . $objp->price_by_qty_unitprice . '" data-pbqbase="' . $objp->price_by_qty_price_base_type . '" data-pbqqty="' . $objp->price_by_qty_quantity . '" data-pbqpercent="' . $objp->price_by_qty_remise_percent . '"';
3075  }
3076  if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3077  if (!empty($user->rights->stock->lire)) {
3078  if ($objp->stock > 0) {
3079  $opt .= ' class="product_line_stock_ok"';
3080  } elseif ($objp->stock <= 0) {
3081  $opt .= ' class="product_line_stock_too_low"';
3082  }
3083  }
3084  }
3085  if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) {
3086  $opt .= ' data-labeltrans="' . $outlabel_translated . '"';
3087  $opt .= ' data-desctrans="' . dol_escape_htmltag($outdesc_translated) . '"';
3088  }
3089  $opt .= '>';
3090  $opt .= $objp->ref;
3091  if (!empty($objp->custref)) {
3092  $opt .= ' (' . $objp->custref . ')';
3093  }
3094  if ($outbarcode) {
3095  $opt .= ' (' . $outbarcode . ')';
3096  }
3097  $opt .= ' - ' . dol_trunc($label, $maxlengtharticle);
3098  if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
3099  $opt .= ' (' . getCountry($outorigin, 1) . ')';
3100  }
3101 
3102  $objRef = $objp->ref;
3103  if (!empty($objp->custref)) {
3104  $objRef .= ' (' . $objp->custref . ')';
3105  }
3106  if (!empty($filterkey) && $filterkey != '') {
3107  $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
3108  }
3109  $outval .= $objRef;
3110  if ($outbarcode) {
3111  $outval .= ' (' . $outbarcode . ')';
3112  }
3113  $outval .= ' - ' . dol_trunc($label, $maxlengtharticle);
3114  if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
3115  $outval .= ' (' . getCountry($outorigin, 1) . ')';
3116  }
3117 
3118  // Units
3119  $opt .= $outvalUnits;
3120  $outval .= $outvalUnits;
3121 
3122  $found = 0;
3123 
3124  // Multiprice
3125  // If we need a particular price level (from 1 to n)
3126  if (empty($hidepriceinlabel) && $price_level >= 1 && (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) {
3127  $sql = "SELECT price, price_ttc, price_base_type, tva_tx, default_vat_code";
3128  $sql .= " FROM " . $this->db->prefix() . "product_price";
3129  $sql .= " WHERE fk_product = " . ((int) $objp->rowid);
3130  $sql .= " AND entity IN (" . getEntity('productprice') . ")";
3131  $sql .= " AND price_level = " . ((int) $price_level);
3132  $sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
3133  $sql .= " LIMIT 1";
3134 
3135  dol_syslog(get_class($this) . '::constructProductListOption search price for product ' . $objp->rowid . ' AND level ' . $price_level, LOG_DEBUG);
3136  $result2 = $this->db->query($sql);
3137  if ($result2) {
3138  $objp2 = $this->db->fetch_object($result2);
3139  if ($objp2) {
3140  $found = 1;
3141  if ($objp2->price_base_type == 'HT') {
3142  $opt .= ' - ' . price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
3143  $outval .= ' - ' . price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
3144  } else {
3145  $opt .= ' - ' . price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
3146  $outval .= ' - ' . price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
3147  }
3148  $outprice_ht = price($objp2->price);
3149  $outprice_ttc = price($objp2->price_ttc);
3150  $outpricebasetype = $objp2->price_base_type;
3151  if (!empty($conf->global->PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL)) { // using this option is a bug. kept for backward compatibility
3152  $outtva_tx = $objp2->tva_tx; // We use the vat rate on line of multiprice
3153  $outdefault_vat_code = $objp2->default_vat_code; // We use the vat code on line of multiprice
3154  } else {
3155  $outtva_tx = $objp->tva_tx; // We use the vat rate of product, not the one on line of multiprice
3156  $outdefault_vat_code = $objp->default_vat_code; // We use the vat code or product, not the one on line of multiprice
3157  }
3158  }
3159  } else {
3160  dol_print_error($this->db);
3161  }
3162  }
3163 
3164  // Price by quantity
3165  if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1 && (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) {
3166  $found = 1;
3167  $outqty = $objp->quantity;
3168  $outdiscount = $objp->remise_percent;
3169  if ($objp->quantity == 1) {
3170  $opt .= ' - ' . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/";
3171  $outval .= ' - ' . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/";
3172  $opt .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3173  $outval .= $langs->transnoentities("Unit");
3174  } else {
3175  $opt .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
3176  $outval .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
3177  $opt .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3178  $outval .= $langs->transnoentities("Units");
3179  }
3180 
3181  $outprice_ht = price($objp->unitprice);
3182  $outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
3183  $outpricebasetype = $objp->price_base_type;
3184  $outtva_tx = $objp->tva_tx; // This value is the value on product when constructProductListOption is called by select_produits_list even if other field $objp-> are from table price_by_qty
3185  $outdefault_vat_code = $objp->default_vat_code; // This value is the value on product when constructProductListOption is called by select_produits_list even if other field $objp-> are from table price_by_qty
3186  }
3187  if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
3188  $opt .= " (" . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
3189  $outval .= " (" . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->transnoentities("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
3190  }
3191  if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
3192  $opt .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
3193  $outval .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
3194  }
3195 
3196  // Price by customer
3197  if (empty($hidepriceinlabel) && !empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
3198  if (!empty($objp->idprodcustprice)) {
3199  $found = 1;
3200 
3201  if ($objp->custprice_base_type == 'HT') {
3202  $opt .= ' - ' . price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
3203  $outval .= ' - ' . price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
3204  } else {
3205  $opt .= ' - ' . price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
3206  $outval .= ' - ' . price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
3207  }
3208 
3209  $outprice_ht = price($objp->custprice);
3210  $outprice_ttc = price($objp->custprice_ttc);
3211  $outpricebasetype = $objp->custprice_base_type;
3212  $outtva_tx = $objp->custtva_tx;
3213  $outdefault_vat_code = $objp->custdefault_vat_code;
3214  }
3215  }
3216 
3217  // If level no defined or multiprice not found, we used the default price
3218  if (empty($hidepriceinlabel) && !$found) {
3219  if ($objp->price_base_type == 'HT') {
3220  $opt .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
3221  $outval .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
3222  } else {
3223  $opt .= ' - ' . price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
3224  $outval .= ' - ' . price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
3225  }
3226  $outprice_ht = price($objp->price);
3227  $outprice_ttc = price($objp->price_ttc);
3228  $outpricebasetype = $objp->price_base_type;
3229  $outtva_tx = $objp->tva_tx;
3230  $outdefault_vat_code = $objp->default_vat_code;
3231  }
3232 
3233  if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3234  if (!empty($user->rights->stock->lire)) {
3235  $opt .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'));
3236 
3237  if ($objp->stock > 0) {
3238  $outval .= ' - <span class="product_line_stock_ok">';
3239  } elseif ($objp->stock <= 0) {
3240  $outval .= ' - <span class="product_line_stock_too_low">';
3241  }
3242  $outval .= $langs->transnoentities("Stock") . ': ' . price(price2num($objp->stock, 'MS'));
3243  $outval .= '</span>';
3244  if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) { // Warning, this option may slow down combo list generation
3245  $langs->load("stocks");
3246 
3247  $tmpproduct = new Product($this->db);
3248  $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3249  $tmpproduct->load_virtual_stock();
3250  $virtualstock = $tmpproduct->stock_theorique;
3251 
3252  $opt .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
3253 
3254  $outval .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
3255  if ($virtualstock > 0) {
3256  $outval .= '<span class="product_line_stock_ok">';
3257  } elseif ($virtualstock <= 0) {
3258  $outval .= '<span class="product_line_stock_too_low">';
3259  }
3260  $outval .= $virtualstock;
3261  $outval .= '</span>';
3262 
3263  unset($tmpproduct);
3264  }
3265  }
3266  }
3267 
3268  $parameters = array('objp'=>$objp);
3269  $reshook = $hookmanager->executeHooks('constructProductListOption', $parameters); // Note that $action and $object may have been modified by hook
3270  if (empty($reshook)) {
3271  $opt .= $hookmanager->resPrint;
3272  } else {
3273  $opt = $hookmanager->resPrint;
3274  }
3275 
3276  $opt .= "</option>\n";
3277  $optJson = array(
3278  'key' => $outkey,
3279  'value' => $outref,
3280  'label' => $outval,
3281  'label2' => $outlabel,
3282  'desc' => $outdesc,
3283  'type' => $outtype,
3284  'price_ht' => price2num($outprice_ht),
3285  'price_ttc' => price2num($outprice_ttc),
3286  'price_ht_locale' => price(price2num($outprice_ht)),
3287  'price_ttc_locale' => price(price2num($outprice_ttc)),
3288  'pricebasetype' => $outpricebasetype,
3289  'tva_tx' => $outtva_tx,
3290  'default_vat_code' => $outdefault_vat_code,
3291  'qty' => $outqty,
3292  'discount' => $outdiscount,
3293  'duration_value' => $outdurationvalue,
3294  'duration_unit' => $outdurationunit,
3295  'pbq' => $outpbq,
3296  'labeltrans' => $outlabel_translated,
3297  'desctrans' => $outdesc_translated,
3298  'ref_customer' => $outrefcust
3299  );
3300  }
3301 
3302  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3303 
3319  public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '')
3320  {
3321  // phpcs:enable
3322  global $langs, $conf;
3323  global $price_level, $status, $finished;
3324 
3325  if (!isset($status)) {
3326  $status = 1;
3327  }
3328 
3329  $selected_input_value = '';
3330  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
3331  if ($selected > 0) {
3332  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3333  $producttmpselect = new Product($this->db);
3334  $producttmpselect->fetch($selected);
3335  $selected_input_value = $producttmpselect->ref;
3336  unset($producttmpselect);
3337  }
3338 
3339  // mode=2 means suppliers products
3340  $urloption = ($socid > 0 ? 'socid=' . $socid . '&' : '') . 'htmlname=' . $htmlname . '&outjson=1&price_level=' . $price_level . '&type=' . $filtertype . '&mode=2&status=' . $status . '&finished=' . $finished . '&alsoproductwithnosupplierprice=' . $alsoproductwithnosupplierprice;
3341  print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
3342 
3343  print ($hidelabel ? '' : $langs->trans("RefOrLabel") . ' : ') . '<input type="text" class="minwidth300" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . $placeholder . '"' : '') . '>';
3344  } else {
3345  print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, 0, $placeholder);
3346  }
3347  }
3348 
3349  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3350 
3369  public function select_produits_fournisseurs_list($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $filterkey = '', $statut = -1, $outputmode = 0, $limit = 100, $alsoproductwithnosupplierprice = 0, $morecss = '', $showstockinlist = 0, $placeholder = '')
3370  {
3371  // phpcs:enable
3372  global $langs, $conf, $user;
3373  global $hookmanager;
3374 
3375  $out = '';
3376  $outarray = array();
3377 
3378  $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3379 
3380  $langs->load('stocks');
3381  // Units
3382  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3383  $langs->load('other');
3384  }
3385 
3386  $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, p.fk_product_type, p.stock, p.tva_tx as tva_tx_sale, p.default_vat_code as default_vat_code_sale,";
3387  $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,";
3388  $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.default_vat_code, pfp.fk_soc, s.nom as name,";
3389  $sql .= " pfp.supplier_reputation";
3390  // if we use supplier description of the products
3391  if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3392  $sql .= ", pfp.desc_fourn as description";
3393  } else {
3394  $sql .= ", p.description";
3395  }
3396  // Units
3397  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3398  $sql .= ", u.label as unit_long, u.short_label as unit_short, p.weight, p.weight_units, p.length, p.length_units, p.width, p.width_units, p.height, p.height_units, p.surface, p.surface_units, p.volume, p.volume_units";
3399  }
3400  if (isModEnabled('barcode')) {
3401  $sql .= ", pfp.barcode";
3402  }
3403  $sql .= " FROM " . $this->db->prefix() . "product as p";
3404  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (" . getEntity('product') . ") )";
3405  if ($socid > 0) {
3406  $sql .= " AND pfp.fk_soc = " . ((int) $socid);
3407  }
3408  $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
3409  // Units
3410  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3411  $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units u ON u.rowid = p.fk_unit";
3412  }
3413  $sql .= " WHERE p.entity IN (" . getEntity('product') . ")";
3414  if ($statut != -1) {
3415  $sql .= " AND p.tobuy = " . ((int) $statut);
3416  }
3417  if (strval($filtertype) != '') {
3418  $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
3419  }
3420  if (!empty($filtre)) {
3421  $sql .= " " . $filtre;
3422  }
3423  // Add where from hooks
3424  $parameters = array();
3425  $reshook = $hookmanager->executeHooks('selectSuppliersProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3426  $sql .= $hookmanager->resPrint;
3427  // Add criteria on ref/label
3428  if ($filterkey != '') {
3429  $sql .= ' AND (';
3430  $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3431  // For natural search
3432  $scrit = explode(' ', $filterkey);
3433  $i = 0;
3434  if (count($scrit) > 1) {
3435  $sql .= "(";
3436  }
3437  foreach ($scrit as $crit) {
3438  if ($i > 0) {
3439  $sql .= " AND ";
3440  }
3441  $sql .= "(pfp.ref_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3442  if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3443  $sql .= " OR pfp.desc_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3444  }
3445  $sql .= ")";
3446  $i++;
3447  }
3448  if (count($scrit) > 1) {
3449  $sql .= ")";
3450  }
3451  if (isModEnabled('barcode')) {
3452  $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3453  $sql .= " OR pfp.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3454  }
3455  $sql .= ')';
3456  }
3457  $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
3458  $sql .= $this->db->plimit($limit, 0);
3459 
3460  // Build output string
3461 
3462  dol_syslog(get_class($this) . "::select_produits_fournisseurs_list", LOG_DEBUG);
3463  $result = $this->db->query($sql);
3464  if ($result) {
3465  require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3466  require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
3467 
3468  $num = $this->db->num_rows($result);
3469 
3470  //$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">'; // remove select to have id same with combo and ajax
3471  $out .= '<select class="flat ' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '">';
3472  if (!$selected) {
3473  $out .= '<option value="-1" selected>' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
3474  } else {
3475  $out .= '<option value="-1">' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
3476  }
3477 
3478  $i = 0;
3479  while ($i < $num) {
3480  $objp = $this->db->fetch_object($result);
3481 
3482  if (is_null($objp->idprodfournprice)) {
3483  // There is no supplier price found, we will use the vat rate for sale
3484  $objp->tva_tx = $objp->tva_tx_sale;
3485  $objp->default_vat_code = $objp->default_vat_code_sale;
3486  }
3487 
3488  $outkey = $objp->idprodfournprice; // id in table of price
3489  if (!$outkey && $alsoproductwithnosupplierprice) {
3490  $outkey = 'idprod_' . $objp->rowid; // id of product
3491  }
3492 
3493  $outref = $objp->ref;
3494  $outbarcode = $objp->barcode;
3495  $outqty = 1;
3496  $outdiscount = 0;
3497  $outtype = $objp->fk_product_type;
3498  $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3499  $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
3500 
3501  // Units
3502  $outvalUnits = '';
3503  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3504  if (!empty($objp->unit_short)) {
3505  $outvalUnits .= ' - ' . $objp->unit_short;
3506  }
3507  if (!empty($objp->weight) && $objp->weight_units !== null) {
3508  $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3509  $outvalUnits .= ' - ' . $unitToShow;
3510  }
3511  if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3512  $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
3513  $outvalUnits .= ' - ' . $unitToShow;
3514  }
3515  if (!empty($objp->surface) && $objp->surface_units !== null) {
3516  $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3517  $outvalUnits .= ' - ' . $unitToShow;
3518  }
3519  if (!empty($objp->volume) && $objp->volume_units !== null) {
3520  $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3521  $outvalUnits .= ' - ' . $unitToShow;
3522  }
3523  if ($outdurationvalue && $outdurationunit) {
3524  $da = array(
3525  'h' => $langs->trans('Hour'),
3526  'd' => $langs->trans('Day'),
3527  'w' => $langs->trans('Week'),
3528  'm' => $langs->trans('Month'),
3529  'y' => $langs->trans('Year')
3530  );
3531  if (isset($da[$outdurationunit])) {
3532  $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
3533  }
3534  }
3535  }
3536 
3537  $objRef = $objp->ref;
3538  if ($filterkey && $filterkey != '') {
3539  $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
3540  }
3541  $objRefFourn = $objp->ref_fourn;
3542  if ($filterkey && $filterkey != '') {
3543  $objRefFourn = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRefFourn, 1);
3544  }
3545  $label = $objp->label;
3546  if ($filterkey && $filterkey != '') {
3547  $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
3548  }
3549 
3550  switch ($objp->fk_product_type) {
3551  case Product::TYPE_PRODUCT:
3552  $picto = 'product';
3553  break;
3554  case Product::TYPE_SERVICE:
3555  $picto = 'service';
3556  break;
3557  default:
3558  $picto = '';
3559  break;
3560  }
3561 
3562  if (empty($picto)) {
3563  $optlabel = '';
3564  } else {
3565  $optlabel = img_object('', $picto, 'class="paddingright classfortooltip"', 0, 0, 1);
3566  }
3567 
3568  $optlabel .= $objp->ref;
3569  if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3570  $optlabel .= ' <span class="opacitymedium">(' . $objp->ref_fourn . ')</span>';
3571  }
3572  if (isModEnabled('barcode') && !empty($objp->barcode)) {
3573  $optlabel .= ' (' . $outbarcode . ')';
3574  }
3575  $optlabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
3576 
3577  $outvallabel = $objRef;
3578  if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3579  $outvallabel .= ' (' . $objRefFourn . ')';
3580  }
3581  if (isModEnabled('barcode') && !empty($objp->barcode)) {
3582  $outvallabel .= ' (' . $outbarcode . ')';
3583  }
3584  $outvallabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
3585 
3586  // Units
3587  $optlabel .= $outvalUnits;
3588  $outvallabel .= $outvalUnits;
3589 
3590  if (!empty($objp->idprodfournprice)) {
3591  $outqty = $objp->quantity;
3592  $outdiscount = $objp->remise_percent;
3593  if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
3594  $prod_supplier = new ProductFournisseur($this->db);
3595  $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3596  $prod_supplier->id = $objp->fk_product;
3597  $prod_supplier->fourn_qty = $objp->quantity;
3598  $prod_supplier->fourn_tva_tx = $objp->tva_tx;
3599  $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3600 
3601  require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3602  $priceparser = new PriceParser($this->db);
3603  $price_result = $priceparser->parseProductSupplier($prod_supplier);
3604  if ($price_result >= 0) {
3605  $objp->fprice = $price_result;
3606  if ($objp->quantity >= 1) {
3607  $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
3608  }
3609  }
3610  }
3611  if ($objp->quantity == 1) {
3612  $optlabel .= ' - ' . price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
3613  $outvallabel .= ' - ' . price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/";
3614  $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3615  $outvallabel .= $langs->transnoentities("Unit");
3616  } else {
3617  $optlabel .= ' - ' . price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
3618  $outvallabel .= ' - ' . price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
3619  $optlabel .= ' ' . $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3620  $outvallabel .= ' ' . $langs->transnoentities("Units");
3621  }
3622 
3623  if ($objp->quantity > 1) {
3624  $optlabel .= " (" . price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
3625  $outvallabel .= " (" . price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->transnoentities("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
3626  }
3627  if ($objp->remise_percent >= 1) {
3628  $optlabel .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
3629  $outvallabel .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
3630  }
3631  if ($objp->duration) {
3632  $optlabel .= " - " . $objp->duration;
3633  $outvallabel .= " - " . $objp->duration;
3634  }
3635  if (!$socid) {
3636  $optlabel .= " - " . dol_trunc($objp->name, 8);
3637  $outvallabel .= " - " . dol_trunc($objp->name, 8);
3638  }
3639  if ($objp->supplier_reputation) {
3640  //TODO dictionary
3641  $reputations = array('' => $langs->trans('Standard'), 'FAVORITE' => $langs->trans('Favorite'), 'NOTTHGOOD' => $langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER' => $langs->trans('DoNotOrderThisProductToThisSupplier'));
3642 
3643  $optlabel .= " - " . $reputations[$objp->supplier_reputation];
3644  $outvallabel .= " - " . $reputations[$objp->supplier_reputation];
3645  }
3646  } else {
3647  if (empty($alsoproductwithnosupplierprice)) { // No supplier price defined for couple product/supplier
3648  $optlabel .= " - <span class='opacitymedium'>" . $langs->trans("NoPriceDefinedForThisSupplier") . '</span>';
3649  $outvallabel .= ' - ' . $langs->transnoentities("NoPriceDefinedForThisSupplier");
3650  } else // No supplier price defined for product, even on other suppliers
3651  {
3652  $optlabel .= " - <span class='opacitymedium'>" . $langs->trans("NoPriceDefinedForThisSupplier") . '</span>';
3653  $outvallabel .= ' - ' . $langs->transnoentities("NoPriceDefinedForThisSupplier");
3654  }
3655  }
3656 
3657  if (isModEnabled('stock') && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3658  $novirtualstock = ($showstockinlist == 2);
3659 
3660  if (!empty($user->rights->stock->lire)) {
3661  $outvallabel .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'));
3662 
3663  if ($objp->stock > 0) {
3664  $optlabel .= ' - <span class="product_line_stock_ok">';
3665  } elseif ($objp->stock <= 0) {
3666  $optlabel .= ' - <span class="product_line_stock_too_low">';
3667  }
3668  $optlabel .= $langs->transnoentities("Stock") . ':' . price(price2num($objp->stock, 'MS'));
3669  $optlabel .= '</span>';
3670  if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) { // Warning, this option may slow down combo list generation
3671  $langs->load("stocks");
3672 
3673  $tmpproduct = new Product($this->db);
3674  $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3675  $tmpproduct->load_virtual_stock();
3676  $virtualstock = $tmpproduct->stock_theorique;
3677 
3678  $outvallabel .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
3679 
3680  $optlabel .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
3681  if ($virtualstock > 0) {
3682  $optlabel .= '<span class="product_line_stock_ok">';
3683  } elseif ($virtualstock <= 0) {
3684  $optlabel .= '<span class="product_line_stock_too_low">';
3685  }
3686  $optlabel .= $virtualstock;
3687  $optlabel .= '</span>';
3688 
3689  unset($tmpproduct);
3690  }
3691  }
3692  }
3693 
3694  $optstart = '<option value="' . $outkey . '"';
3695  if ($selected && $selected == $objp->idprodfournprice) {
3696  $optstart .= ' selected';
3697  }
3698  if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
3699  $optstart .= ' disabled';
3700  }
3701 
3702  if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
3703  $optstart .= ' data-product-id="' . dol_escape_htmltag($objp->rowid) . '"';
3704  $optstart .= ' data-price-id="' . dol_escape_htmltag($objp->idprodfournprice) . '"';
3705  $optstart .= ' data-qty="' . dol_escape_htmltag($objp->quantity) . '"';
3706  $optstart .= ' data-up="' . dol_escape_htmltag(price2num($objp->unitprice)) . '"';
3707  $optstart .= ' data-up-locale="' . dol_escape_htmltag(price($objp->unitprice)) . '"';
3708  $optstart .= ' data-discount="' . dol_escape_htmltag($outdiscount) . '"';
3709  $optstart .= ' data-tvatx="' . dol_escape_htmltag(price2num($objp->tva_tx)) . '"';
3710  $optstart .= ' data-tvatx-formated="' . dol_escape_htmltag(price($objp->tva_tx, 0, $langs, 1, -1, 2)) . '"';
3711  $optstart .= ' data-default-vat-code="' . dol_escape_htmltag($objp->default_vat_code) . '"';
3712  }
3713  $optstart .= ' data-description="' . dol_escape_htmltag($objp->description, 0, 1) . '"';
3714 
3715  $outarrayentry = array(
3716  'key' => $outkey,
3717  'value' => $outref,
3718  'label' => $outvallabel,
3719  'qty' => $outqty,
3720  'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
3721  'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
3722  'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
3723  'tva_tx_formated' => price($objp->tva_tx, 0, $langs, 1, -1, 2),
3724  'tva_tx' => price2num($objp->tva_tx),
3725  'default_vat_code' => $objp->default_vat_code,
3726  'discount' => $outdiscount,
3727  'type' => $outtype,
3728  'duration_value' => $outdurationvalue,
3729  'duration_unit' => $outdurationunit,
3730  'disabled' => (empty($objp->idprodfournprice) ? true : false),
3731  'description' => $objp->description
3732  );
3733 
3734  $parameters = array(
3735  'objp' => &$objp,
3736  'optstart' => &$optstart,
3737  'optlabel' => &$optlabel,
3738  'outvallabel' => &$outvallabel,
3739  'outarrayentry' => &$outarrayentry
3740  );
3741  $reshook = $hookmanager->executeHooks('selectProduitsFournisseurListOption', $parameters, $this);
3742 
3743 
3744  // Add new entry
3745  // "key" value of json key array is used by jQuery automatically as selected value. Example: 'type' = product or service, 'price_ht' = unit price without tax
3746  // "label" value of json key array is used by jQuery automatically as text for combo box
3747  $out .= $optstart . ' data-html="' . dol_escape_htmltag($optlabel) . '">' . $optlabel . "</option>\n";
3748  array_push(
3749  $outarray,
3750  array('key' => $outkey,
3751  'value' => $outref,
3752  'label' => $outvallabel,
3753  'qty' => $outqty,
3754  'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
3755  'price_qty_ht_locale' => price($objp->fprice),
3756  'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
3757  'price_unit_ht_locale' => price($objp->unitprice),
3758  'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
3759  'tva_tx_formated' => price($objp->tva_tx),
3760  'tva_tx' => price2num($objp->tva_tx),
3761  'default_vat_code' => $objp->default_vat_code,
3762  'discount' => $outdiscount,
3763  'type' => $outtype,
3764  'duration_value' => $outdurationvalue,
3765  'duration_unit' => $outdurationunit,
3766  'disabled' => (empty($objp->idprodfournprice) ? true : false),
3767  'description' => $objp->description
3768  )
3769  );
3770  // Exemple of var_dump $outarray
3771  // array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
3772  // ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)"
3773  // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
3774  //}
3775  //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3776  //$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)');
3777  //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3778 
3779  $i++;
3780  }
3781  $out .= '</select>';
3782 
3783  $this->db->free($result);
3784 
3785  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3786  $out .= ajax_combobox($htmlname);
3787  } else {
3788  dol_print_error($this->db);
3789  }
3790 
3791  if (empty($outputmode)) {
3792  return $out;
3793  }
3794  return $outarray;
3795  }
3796 
3797  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3798 
3807  public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = '')
3808  {
3809  // phpcs:enable
3810  global $langs, $conf;
3811 
3812  $langs->load('stocks');
3813 
3814  $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
3815  $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
3816  $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
3817  $sql .= " FROM " . $this->db->prefix() . "product as p";
3818  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
3819  $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
3820  $sql .= " WHERE pfp.entity IN (" . getEntity('productsupplierprice') . ")";
3821  $sql .= " AND p.tobuy = 1";
3822  $sql .= " AND s.fournisseur = 1";
3823  $sql .= " AND p.rowid = " . ((int) $productid);
3824  if (empty($conf->global->PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED)) {
3825  $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
3826  } else {
3827  $sql .= " ORDER BY pfp.unitprice ASC";
3828  }
3829 
3830  dol_syslog(get_class($this) . "::select_product_fourn_price", LOG_DEBUG);
3831  $result = $this->db->query($sql);
3832 
3833  if ($result) {
3834  $num = $this->db->num_rows($result);
3835 
3836  $form = '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
3837 
3838  if (!$num) {
3839  $form .= '<option value="0">-- ' . $langs->trans("NoSupplierPriceDefinedForThisProduct") . ' --</option>';
3840  } else {
3841  require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3842  $form .= '<option value="0">&nbsp;</option>';
3843 
3844  $i = 0;
3845  while ($i < $num) {
3846  $objp = $this->db->fetch_object($result);
3847 
3848  $opt = '<option value="' . $objp->idprodfournprice . '"';
3849  //if there is only one supplier, preselect it
3850  if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier) || ($i == 0 && !empty($conf->global->PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED))) {
3851  $opt .= ' selected';
3852  }
3853  $opt .= '>' . $objp->name . ' - ' . $objp->ref_fourn . ' - ';
3854 
3855  if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
3856  $prod_supplier = new ProductFournisseur($this->db);
3857  $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3858  $prod_supplier->id = $productid;
3859  $prod_supplier->fourn_qty = $objp->quantity;
3860  $prod_supplier->fourn_tva_tx = $objp->tva_tx;
3861  $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3862 
3863  require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3864  $priceparser = new PriceParser($this->db);
3865  $price_result = $priceparser->parseProductSupplier($prod_supplier);
3866  if ($price_result >= 0) {
3867  $objp->fprice = $price_result;
3868  if ($objp->quantity >= 1) {
3869  $objp->unitprice = $objp->fprice / $objp->quantity;
3870  }
3871  }
3872  }
3873  if ($objp->quantity == 1) {
3874  $opt .= price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
3875  }
3876 
3877  $opt .= $objp->quantity . ' ';
3878 
3879  if ($objp->quantity == 1) {
3880  $opt .= $langs->trans("Unit");
3881  } else {
3882  $opt .= $langs->trans("Units");
3883  }
3884  if ($objp->quantity > 1) {
3885  $opt .= " - ";
3886  $opt .= price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit");
3887  }
3888  if ($objp->duration) {
3889  $opt .= " - " . $objp->duration;
3890  }
3891  $opt .= "</option>\n";
3892 
3893  $form .= $opt;
3894  $i++;
3895  }
3896  }
3897 
3898  $form .= '</select>';
3899  $this->db->free($result);
3900  return $form;
3901  } else {
3902  dol_print_error($this->db);
3903  return '';
3904  }
3905  }
3906 
3907  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3908 
3918  public function select_address($selected, $socid, $htmlname = 'address_id', $showempty = 0)
3919  {
3920  // phpcs:enable
3921  // looking for users
3922  $sql = "SELECT a.rowid, a.label";
3923  $sql .= " FROM " . $this->db->prefix() . "societe_address as a";
3924  $sql .= " WHERE a.fk_soc = " . ((int) $socid);
3925  $sql .= " ORDER BY a.label ASC";
3926 
3927  dol_syslog(get_class($this) . "::select_address", LOG_DEBUG);
3928  $resql = $this->db->query($sql);
3929  if ($resql) {
3930  print '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
3931  if ($showempty) {
3932  print '<option value="0">&nbsp;</option>';
3933  }
3934  $num = $this->db->num_rows($resql);
3935  $i = 0;
3936  if ($num) {
3937  while ($i < $num) {
3938  $obj = $this->db->fetch_object($resql);
3939 
3940  if ($selected && $selected == $obj->rowid) {
3941  print '<option value="' . $obj->rowid . '" selected>' . $obj->label . '</option>';
3942  } else {
3943  print '<option value="' . $obj->rowid . '">' . $obj->label . '</option>';
3944  }
3945  $i++;
3946  }
3947  }
3948  print '</select>';
3949  return $num;
3950  } else {
3951  dol_print_error($this->db);
3952  return -1;
3953  }
3954  }
3955 
3956  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3957 
3964  {
3965  // phpcs:enable
3966  global $langs;
3967 
3968  $num = count($this->cache_conditions_paiements);
3969  if ($num > 0) {
3970  return 0; // Cache already loaded
3971  }
3972 
3973  dol_syslog(__METHOD__, LOG_DEBUG);
3974 
3975  $sql = "SELECT rowid, code, libelle as label, deposit_percent";
3976  $sql .= " FROM " . $this->db->prefix() . 'c_payment_term';
3977  $sql .= " WHERE entity IN (" . getEntity('c_payment_term') . ")";
3978  $sql .= " AND active > 0";
3979  $sql .= " ORDER BY sortorder";
3980 
3981  $resql = $this->db->query($sql);
3982  if ($resql) {
3983  $num = $this->db->num_rows($resql);
3984  $i = 0;
3985  while ($i < $num) {
3986  $obj = $this->db->fetch_object($resql);
3987 
3988  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3989  $label = ($langs->trans("PaymentConditionShort" . $obj->code) != ("PaymentConditionShort" . $obj->code) ? $langs->trans("PaymentConditionShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
3990  $this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code;
3991  $this->cache_conditions_paiements[$obj->rowid]['label'] = $label;
3992  $this->cache_conditions_paiements[$obj->rowid]['deposit_percent'] = $obj->deposit_percent;
3993  $i++;
3994  }
3995 
3996  //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table
3997 
3998  return $num;
3999  } else {
4000  dol_print_error($this->db);
4001  return -1;
4002  }
4003  }
4004 
4005  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4006 
4012  public function load_cache_availability()
4013  {
4014  // phpcs:enable
4015  global $langs;
4016 
4017  $num = count($this->cache_availability); // TODO Use $conf->cache['availability'] instead of $this->cache_availability
4018  if ($num > 0) {
4019  return 0; // Cache already loaded
4020  }
4021 
4022  dol_syslog(__METHOD__, LOG_DEBUG);
4023 
4024  $langs->load('propal');
4025 
4026  $sql = "SELECT rowid, code, label, position";
4027  $sql .= " FROM " . $this->db->prefix() . 'c_availability';
4028  $sql .= " WHERE active > 0";
4029 
4030  $resql = $this->db->query($sql);
4031  if ($resql) {
4032  $num = $this->db->num_rows($resql);
4033  $i = 0;
4034  while ($i < $num) {
4035  $obj = $this->db->fetch_object($resql);
4036 
4037  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
4038  $label = ($langs->trans("AvailabilityType" . $obj->code) != ("AvailabilityType" . $obj->code) ? $langs->trans("AvailabilityType" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
4039  $this->cache_availability[$obj->rowid]['code'] = $obj->code;
4040  $this->cache_availability[$obj->rowid]['label'] = $label;
4041  $this->cache_availability[$obj->rowid]['position'] = $obj->position;
4042  $i++;
4043  }
4044 
4045  $this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
4046 
4047  return $num;
4048  } else {
4049  dol_print_error($this->db);
4050  return -1;
4051  }
4052  }
4053 
4064  public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '')
4065  {
4066  global $langs, $user;
4067 
4068  $this->load_cache_availability();
4069 
4070  dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
4071 
4072  print '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
4073  if ($addempty) {
4074  print '<option value="0">&nbsp;</option>';
4075  }
4076  foreach ($this->cache_availability as $id => $arrayavailability) {
4077  if ($selected == $id) {
4078  print '<option value="' . $id . '" selected>';
4079  } else {
4080  print '<option value="' . $id . '">';
4081  }
4082  print dol_escape_htmltag($arrayavailability['label']);
4083  print '</option>';
4084  }
4085  print '</select>';
4086  if ($user->admin) {
4087  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4088  }
4089  print ajax_combobox($htmlname);
4090  }
4091 
4097  public function loadCacheInputReason()
4098  {
4099  global $langs;
4100 
4101  $num = count($this->cache_demand_reason); // TODO Use $conf->cache['input_reason'] instead of $this->cache_demand_reason
4102  if ($num > 0) {
4103  return 0; // Cache already loaded
4104  }
4105 
4106  $sql = "SELECT rowid, code, label";
4107  $sql .= " FROM " . $this->db->prefix() . 'c_input_reason';
4108  $sql .= " WHERE active > 0";
4109 
4110  $resql = $this->db->query($sql);
4111  if ($resql) {
4112  $num = $this->db->num_rows($resql);
4113  $i = 0;
4114  $tmparray = array();
4115  while ($i < $num) {
4116  $obj = $this->db->fetch_object($resql);
4117 
4118  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
4119  $label = ($obj->label != '-' ? $obj->label : '');
4120  if ($langs->trans("DemandReasonType" . $obj->code) != ("DemandReasonType" . $obj->code)) {
4121  $label = $langs->trans("DemandReasonType" . $obj->code); // So translation key DemandReasonTypeSRC_XXX will work
4122  }
4123  if ($langs->trans($obj->code) != $obj->code) {
4124  $label = $langs->trans($obj->code); // So translation key SRC_XXX will work
4125  }
4126 
4127  $tmparray[$obj->rowid]['id'] = $obj->rowid;
4128  $tmparray[$obj->rowid]['code'] = $obj->code;
4129  $tmparray[$obj->rowid]['label'] = $label;
4130  $i++;
4131  }
4132 
4133  $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
4134 
4135  unset($tmparray);
4136  return $num;
4137  } else {
4138  dol_print_error($this->db);
4139  return -1;
4140  }
4141  }
4142 
4155  public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
4156  {
4157  global $langs, $user;
4158 
4159  $this->loadCacheInputReason();
4160 
4161  print '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4162  if ($addempty) {
4163  print '<option value="0"' . (empty($selected) ? ' selected' : '') . '>&nbsp;</option>';
4164  }
4165  foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
4166  if ($arraydemandreason['code'] == $exclude) {
4167  continue;
4168  }
4169 
4170  if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
4171  print '<option value="' . $arraydemandreason['id'] . '" selected>';
4172  } else {
4173  print '<option value="' . $arraydemandreason['id'] . '">';
4174  }
4175  $label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
4176  print $langs->trans($label);
4177  print '</option>';
4178  }
4179  print '</select>';
4180  if ($user->admin && empty($notooltip)) {
4181  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4182  }
4183  print ajax_combobox('select_' . $htmlname);
4184  }
4185 
4186  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4187 
4193  public function load_cache_types_paiements()
4194  {
4195  // phpcs:enable
4196  global $langs;
4197 
4198  $num = count($this->cache_types_paiements); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_types_paiements
4199  if ($num > 0) {
4200  return $num; // Cache already loaded
4201  }
4202 
4203  dol_syslog(__METHOD__, LOG_DEBUG);
4204 
4205  $this->cache_types_paiements = array();
4206 
4207  $sql = "SELECT id, code, libelle as label, type, active";
4208  $sql .= " FROM " . $this->db->prefix() . "c_paiement";
4209  $sql .= " WHERE entity IN (" . getEntity('c_paiement') . ")";
4210 
4211  $resql = $this->db->query($sql);
4212  if ($resql) {
4213  $num = $this->db->num_rows($resql);
4214  $i = 0;
4215  while ($i < $num) {
4216  $obj = $this->db->fetch_object($resql);
4217 
4218  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
4219  $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != ("PaymentTypeShort" . $obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
4220  $this->cache_types_paiements[$obj->id]['id'] = $obj->id;
4221  $this->cache_types_paiements[$obj->id]['code'] = $obj->code;
4222  $this->cache_types_paiements[$obj->id]['label'] = $label;
4223  $this->cache_types_paiements[$obj->id]['type'] = $obj->type;
4224  $this->cache_types_paiements[$obj->id]['active'] = $obj->active;
4225  $i++;
4226  }
4227 
4228  $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
4229 
4230  return $num;
4231  } else {
4232  dol_print_error($this->db);
4233  return -1;
4234  }
4235  }
4236 
4237 
4238  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4239 
4257  public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
4258  {
4259  // phpcs:enable
4260  print $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, $noinfoadmin, $morecss, $deposit_percent);
4261  }
4262 
4263 
4280  public function getSelectConditionsPaiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
4281  {
4282  global $langs, $user, $conf;
4283 
4284  $out = '';
4285  dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
4286 
4288 
4289  // Set default value if not already set by caller
4290  if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) {
4291  dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TERM_ID", LOG_NOTICE);
4292  $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID;
4293  }
4294 
4295  $out .= '<select id="' . $htmlname . '" class="flat selectpaymentterms' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
4296  if ($addempty) {
4297  $out .= '<option value="0">&nbsp;</option>';
4298  }
4299 
4300  $selectedDepositPercent = null;
4301 
4302  foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
4303  if ($filtertype <= 0 && !empty($arrayconditions['deposit_percent'])) {
4304  continue;
4305  }
4306 
4307  if ($selected == $id) {
4308  $selectedDepositPercent = $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'];
4309  $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '" selected>';
4310  } else {
4311  $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '">';
4312  }
4313  $label = $arrayconditions['label'];
4314 
4315  if (!empty($arrayconditions['deposit_percent'])) {
4316  $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'], $label);
4317  }
4318 
4319  $out .= $label;
4320  $out .= '</option>';
4321  }
4322  $out .= '</select>';
4323  if ($user->admin && empty($noinfoadmin)) {
4324  $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4325  }
4326  $out .= ajax_combobox($htmlname);
4327 
4328  if ($deposit_percent >= 0) {
4329  $out .= ' <span id="' . $htmlname . '_deposit_percent_container"' . (empty($selectedDepositPercent) ? ' style="display: none"' : '') . '>';
4330  $out .= $langs->trans('DepositPercent') . ' : ';
4331  $out .= '<input id="' . $htmlname . '_deposit_percent" name="' . $htmlname . '_deposit_percent" class="maxwidth50" value="' . $deposit_percent . '" />';
4332  $out .= '</span>';
4333  $out .= '
4334  <script nonce="' . getNonce() . '">
4335  $(document).ready(function () {
4336  $("#' . $htmlname . '").change(function () {
4337  let $selected = $(this).find("option:selected");
4338  let depositPercent = $selected.attr("data-deposit_percent");
4339 
4340  if (depositPercent.length > 0) {
4341  $("#' . $htmlname . '_deposit_percent_container").show().find("#' . $htmlname . '_deposit_percent").val(depositPercent);
4342  } else {
4343  $("#' . $htmlname . '_deposit_percent_container").hide();
4344  }
4345 
4346  return true;
4347  });
4348  });
4349  </script>';
4350  }
4351 
4352  return $out;
4353  }
4354 
4355 
4356  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4357 
4374  public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
4375  {
4376  // phpcs:enable
4377  global $langs, $user, $conf;
4378 
4379  $out = '';
4380 
4381  dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $filtertype . ", " . $format, LOG_DEBUG);
4382 
4383  $filterarray = array();
4384  if ($filtertype == 'CRDT') {
4385  $filterarray = array(0, 2, 3);
4386  } elseif ($filtertype == 'DBIT') {
4387  $filterarray = array(1, 2, 3);
4388  } elseif ($filtertype != '' && $filtertype != '-1') {
4389  $filterarray = explode(',', $filtertype);
4390  }
4391 
4392  $this->load_cache_types_paiements();
4393 
4394  // Set default value if not already set by caller
4395  if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID)) {
4396  dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TYPE_ID", LOG_NOTICE);
4397  $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID;
4398  }
4399 
4400  $out .= '<select id="select' . $htmlname . '" class="flat selectpaymenttypes' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
4401  if ($empty) {
4402  $out .= '<option value="">&nbsp;</option>';
4403  }
4404  foreach ($this->cache_types_paiements as $id => $arraytypes) {
4405  // If not good status
4406  if ($active >= 0 && $arraytypes['active'] != $active) {
4407  continue;
4408  }
4409 
4410  // On passe si on a demande de filtrer sur des modes de paiments particuliers
4411  if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
4412  continue;
4413  }
4414 
4415  // We discard empty line if showempty is on because an empty line has already been output.
4416  if ($empty && empty($arraytypes['code'])) {
4417  continue;
4418  }
4419 
4420  if ($format == 0) {
4421  $out .= '<option value="' . $id . '"';
4422  } elseif ($format == 1) {
4423  $out .= '<option value="' . $arraytypes['code'] . '"';
4424  } elseif ($format == 2) {
4425  $out .= '<option value="' . $arraytypes['code'] . '"';
4426  } elseif ($format == 3) {
4427  $out .= '<option value="' . $id . '"';
4428  }
4429  // Print attribute selected or not
4430  if ($format == 1 || $format == 2) {
4431  if ($selected == $arraytypes['code']) {
4432  $out .= ' selected';
4433  }
4434  } else {
4435  if ($selected == $id) {
4436  $out .= ' selected';
4437  }
4438  }
4439  $out .= '>';
4440  $value = '';
4441  if ($format == 0) {
4442  $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4443  } elseif ($format == 1) {
4444  $value = $arraytypes['code'];
4445  } elseif ($format == 2) {
4446  $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4447  } elseif ($format == 3) {
4448  $value = $arraytypes['code'];
4449  }
4450  $out .= $value ? $value : '&nbsp;';
4451  $out .= '</option>';
4452  }
4453  $out .= '</select>';
4454  if ($user->admin && !$noadmininfo) {
4455  $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4456  }
4457  $out .= ajax_combobox('select' . $htmlname);
4458 
4459  if (empty($nooutput)) {
4460  print $out;
4461  } else {
4462  return $out;
4463  }
4464  }
4465 
4466 
4475  public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
4476  {
4477  global $langs;
4478 
4479  $return = '<select class="flat maxwidth100" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4480  $options = array(
4481  'HT' => $langs->trans("HT"),
4482  'TTC' => $langs->trans("TTC")
4483  );
4484  foreach ($options as $id => $value) {
4485  if ($selected == $id) {
4486  $return .= '<option value="' . $id . '" selected>' . $value;
4487  } else {
4488  $return .= '<option value="' . $id . '">' . $value;
4489  }
4490  $return .= '</option>';
4491  }
4492  $return .= '</select>';
4493  if ($addjscombo) {
4494  $return .= ajax_combobox('select_' . $htmlname);
4495  }
4496 
4497  return $return;
4498  }
4499 
4500  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4501 
4507  public function load_cache_transport_mode()
4508  {
4509  // phpcs:enable
4510  global $langs;
4511 
4512  $num = count($this->cache_transport_mode); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_transport_mode
4513  if ($num > 0) {
4514  return $num; // Cache already loaded
4515  }
4516 
4517  dol_syslog(__METHOD__, LOG_DEBUG);
4518 
4519  $this->cache_transport_mode = array();
4520 
4521  $sql = "SELECT rowid, code, label, active";
4522  $sql .= " FROM " . $this->db->prefix() . "c_transport_mode";
4523  $sql .= " WHERE entity IN (" . getEntity('c_transport_mode') . ")";
4524 
4525  $resql = $this->db->query($sql);
4526  if ($resql) {
4527  $num = $this->db->num_rows($resql);
4528  $i = 0;
4529  while ($i < $num) {
4530  $obj = $this->db->fetch_object($resql);
4531 
4532  // If traduction exist, we use it else we take the default label
4533  $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != ("PaymentTypeShort" . $obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
4534  $this->cache_transport_mode[$obj->rowid]['rowid'] = $obj->rowid;
4535  $this->cache_transport_mode[$obj->rowid]['code'] = $obj->code;
4536  $this->cache_transport_mode[$obj->rowid]['label'] = $label;
4537  $this->cache_transport_mode[$obj->rowid]['active'] = $obj->active;
4538  $i++;
4539  }
4540 
4541  $this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
4542 
4543  return $num;
4544  } else {
4545  dol_print_error($this->db);
4546  return -1;
4547  }
4548  }
4549 
4563  public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
4564  {
4565  global $langs, $user;
4566 
4567  dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $format, LOG_DEBUG);
4568 
4569  $this->load_cache_transport_mode();
4570 
4571  print '<select id="select' . $htmlname . '" class="flat selectmodetransport' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
4572  if ($empty) {
4573  print '<option value="">&nbsp;</option>';
4574  }
4575  foreach ($this->cache_transport_mode as $id => $arraytypes) {
4576  // If not good status
4577  if ($active >= 0 && $arraytypes['active'] != $active) {
4578  continue;
4579  }
4580 
4581  // We discard empty line if showempty is on because an empty line has already been output.
4582  if ($empty && empty($arraytypes['code'])) {
4583  continue;
4584  }
4585 
4586  if ($format == 0) {
4587  print '<option value="' . $id . '"';
4588  } elseif ($format == 1) {
4589  print '<option value="' . $arraytypes['code'] . '"';
4590  } elseif ($format == 2) {
4591  print '<option value="' . $arraytypes['code'] . '"';
4592  } elseif ($format == 3) {
4593  print '<option value="' . $id . '"';
4594  }
4595  // If text is selected, we compare with code, else with id
4596  if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
4597  print ' selected';
4598  } elseif ($selected == $id) {
4599  print ' selected';
4600  }
4601  print '>';
4602  $value = '';
4603  if ($format == 0) {
4604  $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4605  } elseif ($format == 1) {
4606  $value = $arraytypes['code'];
4607  } elseif ($format == 2) {
4608  $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4609  } elseif ($format == 3) {
4610  $value = $arraytypes['code'];
4611  }
4612  print $value ? $value : '&nbsp;';
4613  print '</option>';
4614  }
4615  print '</select>';
4616  if ($user->admin && !$noadmininfo) {
4617  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4618  }
4619  }
4620 
4633  public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
4634  {
4635  global $langs, $conf, $user;
4636 
4637  $langs->load("admin");
4638  $langs->load("deliveries");
4639 
4640  $sql = "SELECT rowid, code, libelle as label";
4641  $sql .= " FROM " . $this->db->prefix() . "c_shipment_mode";
4642  $sql .= " WHERE active > 0";
4643  if ($filtre) {
4644  $sql .= " AND " . $filtre;
4645  }
4646  $sql .= " ORDER BY libelle ASC";
4647 
4648  dol_syslog(get_class($this) . "::selectShippingMode", LOG_DEBUG);
4649  $result = $this->db->query($sql);
4650  if ($result) {
4651  $num = $this->db->num_rows($result);
4652  $i = 0;
4653  if ($num) {
4654  print '<select id="select' . $htmlname . '" class="flat selectshippingmethod' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
4655  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4656  print '<option value="-1">&nbsp;</option>';
4657  }
4658  while ($i < $num) {
4659  $obj = $this->db->fetch_object($result);
4660  if ($selected == $obj->rowid) {
4661  print '<option value="' . $obj->rowid . '" selected>';
4662  } else {
4663  print '<option value="' . $obj->rowid . '">';
4664  }
4665  print ($langs->trans("SendingMethod" . strtoupper($obj->code)) != "SendingMethod" . strtoupper($obj->code)) ? $langs->trans("SendingMethod" . strtoupper($obj->code)) : $obj->label;
4666  print '</option>';
4667  $i++;
4668  }
4669  print "</select>";
4670  if ($user->admin && empty($noinfoadmin)) {
4671  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4672  }
4673 
4674  print ajax_combobox('select' . $htmlname);
4675  } else {
4676  print $langs->trans("NoShippingMethodDefined");
4677  }
4678  } else {
4679  dol_print_error($this->db);
4680  }
4681  }
4682 
4692  public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
4693  {
4694  global $langs;
4695 
4696  $langs->load("deliveries");
4697 
4698  if ($htmlname != "none") {
4699  print '<form method="POST" action="' . $page . '">';
4700  print '<input type="hidden" name="action" value="setshippingmethod">';
4701  print '<input type="hidden" name="token" value="' . newToken() . '">';
4702  $this->selectShippingMethod($selected, $htmlname, '', $addempty);
4703  print '<input type="submit" class="button valignmiddle" value="' . $langs->trans("Modify") . '">';
4704  print '</form>';
4705  } else {
4706  if ($selected) {
4707  $code = $langs->getLabelFromKey($this->db, $selected, 'c_shipment_mode', 'rowid', 'code');
4708  print $langs->trans("SendingMethod" . strtoupper($code));
4709  } else {
4710  print "&nbsp;";
4711  }
4712  }
4713  }
4714 
4723  public function selectSituationInvoices($selected = '', $socid = 0)
4724  {
4725  global $langs;
4726 
4727  $langs->load('bills');
4728 
4729  $opt = '<option value="" selected></option>';
4730  $sql = "SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc";
4731  $sql .= ' FROM ' . $this->db->prefix() . 'facture';
4732  $sql .= ' WHERE entity IN (' . getEntity('invoice') . ')';
4733  $sql .= ' AND situation_counter >= 1';
4734  $sql .= ' AND fk_soc = ' . (int) $socid;
4735  $sql .= ' AND type <> 2';
4736  $sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
4737  $resql = $this->db->query($sql);
4738 
4739  if ($resql && $this->db->num_rows($resql) > 0) {
4740  // Last seen cycle
4741  $ref = 0;
4742  while ($obj = $this->db->fetch_object($resql)) {
4743  //Same cycle ?
4744  if ($obj->situation_cycle_ref != $ref) {
4745  // Just seen this cycle
4746  $ref = $obj->situation_cycle_ref;
4747  //not final ?
4748  if ($obj->situation_final != 1) {
4749  //Not prov?
4750  if (substr($obj->ref, 1, 4) != 'PROV') {
4751  if ($selected == $obj->rowid) {
4752  $opt .= '<option value="' . $obj->rowid . '" selected>' . $obj->ref . '</option>';
4753  } else {
4754  $opt .= '<option value="' . $obj->rowid . '">' . $obj->ref . '</option>';
4755  }
4756  }
4757  }
4758  }
4759  }
4760  } else {
4761  dol_syslog("Error sql=" . $sql . ", error=" . $this->error, LOG_ERR);
4762  }
4763  if ($opt == '<option value ="" selected></option>') {
4764  $opt = '<option value ="0" selected>' . $langs->trans('NoSituations') . '</option>';
4765  }
4766  return $opt;
4767  }
4768 
4778  public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
4779  {
4780  global $langs;
4781 
4782  $langs->load('products');
4783 
4784  $return = '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '">';
4785 
4786  $sql = "SELECT rowid, label, code FROM " . $this->db->prefix() . "c_units";
4787  $sql .= ' WHERE active > 0';
4788  if (!empty($unit_type)) {
4789  $sql .= " AND unit_type = '" . $this->db->escape($unit_type) . "'";
4790  }
4791  $sql .= " ORDER BY sortorder";
4792 
4793  $resql = $this->db->query($sql);
4794  if ($resql && $this->db->num_rows($resql) > 0) {
4795  if ($showempty) {
4796  $return .= '<option value="none"></option>';
4797  }
4798 
4799  while ($res = $this->db->fetch_object($resql)) {
4800  $unitLabel = $res->label;
4801  if (!empty($langs->tab_translate['unit' . $res->code])) { // check if Translation is available before
4802  $unitLabel = $langs->trans('unit' . $res->code) != $res->label ? $langs->trans('unit' . $res->code) : $res->label;
4803  }
4804 
4805  if ($selected == $res->rowid) {
4806  $return .= '<option value="' . $res->rowid . '" selected>' . $unitLabel . '</option>';
4807  } else {
4808  $return .= '<option value="' . $res->rowid . '">' . $unitLabel . '</option>';
4809  }
4810  }
4811  $return .= '</select>';
4812  }
4813  return $return;
4814  }
4815 
4816  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4817 
4832  public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0)
4833  {
4834  // phpcs:enable
4835  global $langs, $conf;
4836 
4837  $out = '';
4838 
4839  $langs->load("admin");
4840  $num = 0;
4841 
4842  $sql = "SELECT rowid, label, bank, clos as status, currency_code";
4843  $sql .= " FROM " . $this->db->prefix() . "bank_account";
4844  $sql .= " WHERE entity IN (" . getEntity('bank_account') . ")";
4845  if ($status != 2) {
4846  $sql .= " AND clos = " . (int) $status;
4847  }
4848  if ($filtre) {
4849  $sql .= " AND " . $filtre;
4850  }
4851  $sql .= " ORDER BY label";
4852 
4853  dol_syslog(get_class($this) . "::select_comptes", LOG_DEBUG);
4854  $result = $this->db->query($sql);
4855  if ($result) {
4856  $num = $this->db->num_rows($result);
4857  $i = 0;
4858  if ($num) {
4859  $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
4860  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4861  $out .= '<option value="-1">&nbsp;</option>';
4862  }
4863 
4864  while ($i < $num) {
4865  $obj = $this->db->fetch_object($result);
4866  if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
4867  $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" selected>';
4868  } else {
4869  $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '">';
4870  }
4871  $out .= trim($obj->label);
4872  if ($showcurrency) {
4873  $out .= ' (' . $obj->currency_code . ')';
4874  }
4875  if ($status == 2 && $obj->status == 1) {
4876  $out .= ' (' . $langs->trans("Closed") . ')';
4877  }
4878  $out .= '</option>';
4879  $i++;
4880  }
4881  $out .= "</select>";
4882  $out .= ajax_combobox('select' . $htmlname);
4883  } else {
4884  if ($status == 0) {
4885  $out .= '<span class="opacitymedium">' . $langs->trans("NoActiveBankAccountDefined") . '</span>';
4886  } else {
4887  $out .= '<span class="opacitymedium">' . $langs->trans("NoBankAccountFound") . '</span>';
4888  }
4889  }
4890  } else {
4891  dol_print_error($this->db);
4892  }
4893 
4894  // Output or return
4895  if (empty($nooutput)) {
4896  print $out;
4897  } else {
4898  return $out;
4899  }
4900 
4901  return $num;
4902  }
4903 
4915  public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
4916  {
4917  global $langs, $conf;
4918 
4919  $langs->load("admin");
4920  $num = 0;
4921 
4922  $sql = "SELECT rowid, name, fk_country, status, entity";
4923  $sql .= " FROM " . $this->db->prefix() . "establishment";
4924  $sql .= " WHERE 1=1";
4925  if ($status != 2) {
4926  $sql .= " AND status = " . (int) $status;
4927  }
4928  if ($filtre) {
4929  $sql .= " AND " . $filtre;
4930  }
4931  $sql .= " ORDER BY name";
4932 
4933  dol_syslog(get_class($this) . "::select_establishment", LOG_DEBUG);
4934  $result = $this->db->query($sql);
4935  if ($result) {
4936  $num = $this->db->num_rows($result);
4937  $i = 0;
4938  if ($num) {
4939  print '<select id="select' . $htmlname . '" class="flat selectestablishment" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
4940  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4941  print '<option value="-1">&nbsp;</option>';
4942  }
4943 
4944  while ($i < $num) {
4945  $obj = $this->db->fetch_object($result);
4946  if ($selected == $obj->rowid) {
4947  print '<option value="' . $obj->rowid . '" selected>';
4948  } else {
4949  print '<option value="' . $obj->rowid . '">';
4950  }
4951  print trim($obj->name);
4952  if ($status == 2 && $obj->status == 1) {
4953  print ' (' . $langs->trans("Closed") . ')';
4954  }
4955  print '</option>';
4956  $i++;
4957  }
4958  print "</select>";
4959  } else {
4960  if ($status == 0) {
4961  print '<span class="opacitymedium">' . $langs->trans("NoActiveEstablishmentDefined") . '</span>';
4962  } else {
4963  print '<span class="opacitymedium">' . $langs->trans("NoEstablishmentFound") . '</span>';
4964  }
4965  }
4966 
4967  return $num;
4968  } else {
4969  dol_print_error($this->db);
4970  return -1;
4971  }
4972  }
4973 
4983  public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
4984  {
4985  global $langs;
4986  if ($htmlname != "none") {
4987  print '<form method="POST" action="' . $page . '">';
4988  print '<input type="hidden" name="action" value="setbankaccount">';
4989  print '<input type="hidden" name="token" value="' . newToken() . '">';
4990  print img_picto('', 'bank_account', 'class="pictofixedwidth"');
4991  $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
4992  if ($nbaccountfound > 0) {
4993  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
4994  }
4995  print '</form>';
4996  } else {
4997  $langs->load('banks');
4998 
4999  if ($selected) {
5000  require_once DOL_DOCUMENT_ROOT . '/compta/bank/class/account.class.php';
5001  $bankstatic = new Account($this->db);
5002  $result = $bankstatic->fetch($selected);
5003  if ($result) {
5004  print $bankstatic->getNomUrl(1);
5005  }
5006  } else {
5007  print "&nbsp;";
5008  }
5009  }
5010  }
5011 
5012  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5013 
5032  public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $markafterid = 0, $outputmode = 0, $include = 0, $morecss = '')
5033  {
5034  // phpcs:enable
5035  global $conf, $langs;
5036  $langs->load("categories");
5037 
5038  include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
5039 
5040  // For backward compatibility
5041  if (is_numeric($type)) {
5042  dol_syslog(__METHOD__ . ': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING);
5043  }
5044 
5045  if ($type === Categorie::TYPE_BANK_LINE) {
5046  // TODO Move this into common category feature
5047  $cate_arbo = array();
5048  $sql = "SELECT c.label, c.rowid";
5049  $sql .= " FROM " . $this->db->prefix() . "bank_categ as c";
5050  $sql .= " WHERE entity = " . $conf->entity;
5051  $sql .= " ORDER BY c.label";
5052  $result = $this->db->query($sql);
5053  if ($result) {
5054  $num = $this->db->num_rows($result);
5055  $i = 0;
5056  while ($i < $num) {
5057  $objp = $this->db->fetch_object($result);
5058  if ($objp) {
5059  $cate_arbo[$objp->rowid] = array('id' => $objp->rowid, 'fulllabel' => $objp->label, 'color' => '', 'picto' => 'category');
5060  }
5061  $i++;
5062  }
5063  $this->db->free($result);
5064  } else {
5065  dol_print_error($this->db);
5066  }
5067  } else {
5068  $cat = new Categorie($this->db);
5069  $cate_arbo = $cat->get_full_arbo($type, $markafterid, $include);
5070  }
5071 
5072  $outarray = array();
5073 
5074  $output = '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
5075  if (is_array($cate_arbo)) {
5076  if (!count($cate_arbo)) {
5077  $output .= '<option value="-1" disabled>' . $langs->trans("NoCategoriesDefined") . '</option>';
5078  } else {
5079  $output .= '<option value="-1">&nbsp;</option>';
5080  foreach ($cate_arbo as $key => $value) {
5081  if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
5082  $add = 'selected ';
5083  } else {
5084  $add = '';
5085  }
5086  $output .= '<option ' . $add . 'value="' . $cate_arbo[$key]['id'] . '"';
5087  $output .= ' data-html="' . dol_escape_htmltag(img_picto('', 'category', 'class="pictofixedwidth" style="color: #' . $cate_arbo[$key]['color'] . '"') . dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle')) . '"';
5088  $output .= '>';
5089  $output .= dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle');
5090  $output .= '</option>';
5091 
5092  $outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
5093  }
5094  }
5095  }
5096  $output .= '</select>';
5097  $output .= "\n";
5098 
5099  if ($outputmode == 2) {
5100  return $cate_arbo;
5101  } elseif ($outputmode) {
5102  return $outarray;
5103  }
5104  return $output;
5105  }
5106 
5107  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5108 
5125  public function form_confirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
5126  {
5127  // phpcs:enable
5128  dol_syslog(__METHOD__ . ': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
5129  print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
5130  }
5131 
5159  public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0, $labelbuttonyes = 'Yes', $labelbuttonno = 'No')
5160  {
5161  global $langs, $conf;
5162 
5163  $more = '<!-- formconfirm - before call, page=' . dol_escape_htmltag($page) . ' -->';
5164  $formconfirm = '';
5165  $inputok = array();
5166  $inputko = array();
5167 
5168  // Clean parameters
5169  $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
5170  if ($conf->browser->layout == 'phone') {
5171  $width = '95%';
5172  }
5173 
5174  // Set height automatically if not defined
5175  if (empty($height)) {
5176  $height = 220;
5177  if (is_array($formquestion) && count($formquestion) > 2) {
5178  $height += ((count($formquestion) - 2) * 24);
5179  }
5180  }
5181 
5182  if (is_array($formquestion) && !empty($formquestion)) {
5183  // First add hidden fields and value
5184  foreach ($formquestion as $key => $input) {
5185  if (is_array($input) && !empty($input)) {
5186  if ($input['type'] == 'hidden') {
5187  $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
5188  $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
5189 
5190  $more .= '<input type="hidden" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '" value="' . dol_escape_htmltag($input['value']) . '" class="' . $morecss . '"' . $moreattr . '>' . "\n";
5191  }
5192  }
5193  }
5194 
5195  // Now add questions
5196  $moreonecolumn = '';
5197  $more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">' . "\n";
5198  foreach ($formquestion as $key => $input) {
5199  if (is_array($input) && !empty($input)) {
5200  $size = (!empty($input['size']) ? ' size="' . $input['size'] . '"' : ''); // deprecated. Use morecss instead.
5201  $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
5202  $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
5203 
5204  if ($input['type'] == 'text') {
5205  $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">' . $input['label'] . '</div><div class="tagtd"><input type="text" class="flat' . $morecss . '" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '"' . $size . ' value="' . (empty($input['value']) ? '' : $input['value']) . '"' . $moreattr . ' /></div></div>' . "\n";
5206  } elseif ($input['type'] == 'password') {
5207  $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">' . $input['label'] . '</div><div class="tagtd"><input type="password" class="flat' . $morecss . '" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '"' . $size . ' value="' . (empty($input['value']) ? '' : $input['value']) . '"' . $moreattr . ' /></div></div>' . "\n";
5208  } elseif ($input['type'] == 'textarea') {
5209  /*$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd">';
5210  $more .= '<textarea name="'.$input['name'].'" class="'.$morecss.'"'.$moreattr.'>';
5211  $more .= $input['value'];
5212  $more .= '</textarea>';
5213  $more .= '</div></div>'."\n";*/
5214  $moreonecolumn .= '<div class="margintoponly">';
5215  $moreonecolumn .= $input['label'] . '<br>';
5216  $moreonecolumn .= '<textarea name="' . dol_escape_htmltag($input['name']) . '" id="' . dol_escape_htmltag($input['name']) . '" class="' . $morecss . '"' . $moreattr . '>';
5217  $moreonecolumn .= $input['value'];
5218  $moreonecolumn .= '</textarea>';
5219  $moreonecolumn .= '</div>';
5220  } elseif (in_array($input['type'], ['select', 'multiselect'])) {
5221  if (empty($morecss)) {
5222  $morecss = 'minwidth100';
5223  }
5224 
5225  $show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
5226  $key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
5227  $value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
5228  $translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
5229  $maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
5230  $disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
5231  $sort = isset($input['select_sort']) ? $input['select_sort'] : '';
5232 
5233  $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">';
5234  if (!empty($input['label'])) {
5235  $more .= $input['label'] . '</div><div class="tagtd left">';
5236  }
5237  if ($input['type'] == 'select') {
5238  $more .= $this->selectarray($input['name'], $input['values'], isset($input['default']) ? $input['default'] : '-1', $show_empty, $key_in_label, $value_as_key, $moreattr, $translate, $maxlen, $disabled, $sort, $morecss);
5239  } else {
5240  $more .= $this->multiselectarray($input['name'], $input['values'], is_array($input['default']) ? $input['default'] : [$input['default']], $key_in_label, $value_as_key, $morecss, $translate, $maxlen, $moreattr);
5241  }
5242  $more .= '</div></div>' . "\n";
5243  } elseif ($input['type'] == 'checkbox') {
5244  $more .= '<div class="tagtr">';
5245  $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '"><label for="' . dol_escape_htmltag($input['name']) . '">' . $input['label'] . '</label></div><div class="tagtd">';
5246  $more .= '<input type="checkbox" class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '"' . $moreattr;
5247  if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0' && $input['value'] != '') {
5248  $more .= ' checked';
5249  }
5250  if (is_bool($input['value']) && $input['value']) {
5251  $more .= ' checked';
5252  }
5253  if (isset($input['disabled'])) {
5254  $more .= ' disabled';
5255  }
5256  $more .= ' /></div>';
5257  $more .= '</div>' . "\n";
5258  } elseif ($input['type'] == 'radio') {
5259  $i = 0;
5260  foreach ($input['values'] as $selkey => $selval) {
5261  $more .= '<div class="tagtr">';
5262  if (isset($input['label'])) {
5263  if ($i == 0) {
5264  $more .= '<div class="tagtd' . (empty($input['tdclass']) ? ' tdtop' : (' tdtop ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
5265  } else {
5266  $more .= '<div clas="tagtd' . (empty($input['tdclass']) ? '' : (' "' . $input['tdclass'])) . '">&nbsp;</div>';
5267  }
5268  }
5269  $more .= '<div class="tagtd' . ($i == 0 ? ' tdtop' : '') . '"><input type="radio" class="flat' . $morecss . '" id="' . dol_escape_htmltag($input['name'] . $selkey) . '" name="' . dol_escape_htmltag($input['name']) . '" value="' . $selkey . '"' . $moreattr;
5270  if (!empty($input['disabled'])) {
5271  $more .= ' disabled';
5272  }
5273  if (isset($input['default']) && $input['default'] === $selkey) {
5274  $more .= ' checked="checked"';
5275  }
5276  $more .= ' /> ';
5277  $more .= '<label for="' . dol_escape_htmltag($input['name'] . $selkey) . '" class="valignmiddle">' . $selval . '</label>';
5278  $more .= '</div></div>' . "\n";
5279  $i++;
5280  }
5281  } elseif ($input['type'] == 'date' || $input['type'] == 'datetime') {
5282  $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
5283  $more .= '<div class="tagtd">';
5284  $addnowlink = (empty($input['datenow']) ? 0 : 1);
5285  $h = $m = 0;
5286  if ($input['type'] == 'datetime') {
5287  $h = isset($input['hours']) ? $input['hours'] : 1;
5288  $m = isset($input['minutes']) ? $input['minutes'] : 1;
5289  }
5290  $more .= $this->selectDate($input['value'], $input['name'], $h, $m, 0, '', 1, $addnowlink);
5291  $more .= '</div></div>'."\n";
5292  $formquestion[] = array('name'=>$input['name'].'day');
5293  $formquestion[] = array('name'=>$input['name'].'month');
5294  $formquestion[] = array('name'=>$input['name'].'year');
5295  $formquestion[] = array('name'=>$input['name'].'hour');
5296  $formquestion[] = array('name'=>$input['name'].'min');
5297  } elseif ($input['type'] == 'other') { // can be 1 column or 2 depending if label is set or not
5298  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
5299  if (!empty($input['label'])) {
5300  $more .= $input['label'] . '</div><div class="tagtd">';
5301  }
5302  $more .= $input['value'];
5303  $more .= '</div></div>' . "\n";
5304  } elseif ($input['type'] == 'onecolumn') {
5305  $moreonecolumn .= '<div class="margintoponly">';
5306  $moreonecolumn .= $input['value'];
5307  $moreonecolumn .= '</div>' . "\n";
5308  } elseif ($input['type'] == 'hidden') {
5309  // Do nothing more, already added by a previous loop
5310  } elseif ($input['type'] == 'separator') {
5311  $more .= '<br>';
5312  } else {
5313  $more .= 'Error type ' . $input['type'] . ' for the confirm box is not a supported type';
5314  }
5315  }
5316  }
5317  $more .= '</div>' . "\n";
5318  $more .= $moreonecolumn;
5319  }
5320 
5321  // JQUERY method dialog is broken with smartphone, we use standard HTML.
5322  // Note: When using dol_use_jmobile or no js, you must also check code for button use a GET url with action=xxx and check that you also output the confirm code when action=xxx
5323  // See page product/card.php for example
5324  if (!empty($conf->dol_use_jmobile)) {
5325  $useajax = 0;
5326  }
5327  if (empty($conf->use_javascript_ajax)) {
5328  $useajax = 0;
5329  }
5330 
5331  if ($useajax) {
5332  $autoOpen = true;
5333  $dialogconfirm = 'dialog-confirm';
5334  $button = '';
5335  if (!is_numeric($useajax)) {
5336  $button = $useajax;
5337  $useajax = 1;
5338  $autoOpen = false;
5339  $dialogconfirm .= '-' . $button;
5340  }
5341  $pageyes = $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=yes';
5342  $pageno = ($useajax == 2 ? $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=no' : '');
5343 
5344  // Add input fields into list of fields to read during submit (inputok and inputko)
5345  if (is_array($formquestion)) {
5346  foreach ($formquestion as $key => $input) {
5347  //print "xx ".$key." rr ".is_array($input)."<br>\n";
5348  // Add name of fields to propagate with the GET when submitting the form with button OK.
5349  if (is_array($input) && isset($input['name'])) {
5350  if (strpos($input['name'], ',') > 0) {
5351  $inputok = array_merge($inputok, explode(',', $input['name']));
5352  } else {
5353  array_push($inputok, $input['name']);
5354  }
5355  }
5356  // Add name of fields to propagate with the GET when submitting the form with button KO.
5357  if (isset($input['inputko']) && $input['inputko'] == 1) {
5358  array_push($inputko, $input['name']);
5359  }
5360  }
5361  }
5362 
5363  // Show JQuery confirm box.
5364  $formconfirm .= '<div id="' . $dialogconfirm . '" title="' . dol_escape_htmltag($title) . '" style="display: none;">';
5365  if (is_array($formquestion) && !empty($formquestion['text'])) {
5366  $formconfirm .= '<div class="confirmtext">' . $formquestion['text'] . '</div>' . "\n";
5367  }
5368  if (!empty($more)) {
5369  $formconfirm .= '<div class="confirmquestions">' . $more . '</div>' . "\n";
5370  }
5371  $formconfirm .= ($question ? '<div class="confirmmessage">' . img_help('', '') . ' ' . $question . '</div>' : '');
5372  $formconfirm .= '</div>' . "\n";
5373 
5374  $formconfirm .= "\n<!-- begin code of popup for formconfirm page=" . $page . " -->\n";
5375  $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
5376  $formconfirm .= "/* Code for the jQuery('#dialogforpopup').dialog() */\n";
5377  $formconfirm .= 'jQuery(document).ready(function() {
5378  $(function() {
5379  $( "#' . $dialogconfirm . '" ).dialog(
5380  {
5381  autoOpen: ' . ($autoOpen ? "true" : "false") . ',';
5382  if ($newselectedchoice == 'no') {
5383  $formconfirm .= '
5384  open: function() {
5385  $(this).parent().find("button.ui-button:eq(2)").focus();
5386  },';
5387  }
5388 
5389  $jsforcursor = '';
5390  if ($useajax == 1) {
5391  $jsforcursor = '// The call to urljump can be slow, so we set the wait cursor' . "\n";
5392  $jsforcursor .= 'jQuery("html,body,#id-container").addClass("cursorwait");' . "\n";
5393  }
5394 
5395  $postconfirmas = 'GET';
5396 
5397  $formconfirm .= '
5398  resizable: false,
5399  height: "' . $height . '",
5400  width: "' . $width . '",
5401  modal: true,
5402  closeOnEscape: false,
5403  buttons: {
5404  "' . dol_escape_js($langs->transnoentities($labelbuttonyes)) . '": function() {
5405  var options = "token=' . urlencode(newToken()) . '";
5406  var inputok = ' . json_encode($inputok) . '; /* List of fields into form */
5407  var page = "' . dol_escape_js(!empty($page) ? $page : '') . '";
5408  var pageyes = "' . dol_escape_js(!empty($pageyes) ? $pageyes : '') . '";
5409 
5410  if (inputok.length > 0) {
5411  $.each(inputok, function(i, inputname) {
5412  var more = "";
5413  var inputvalue;
5414  if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
5415  inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
5416  } else {
5417  if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5418  inputvalue = $("#" + inputname + more).val();
5419  }
5420  if (typeof inputvalue == "undefined") { inputvalue=""; }
5421  console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
5422  options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5423  });
5424  }
5425  var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "&") + options;
5426  if (pageyes.length > 0) {';
5427  if ($postconfirmas == 'GET') {
5428  $formconfirm .= 'location.href = urljump;';
5429  } else {
5430  $formconfirm .= $jsforcursor;
5431  $formconfirm .= 'var post = $.post(
5432  pageyes,
5433  options,
5434  function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
5435  );';
5436  }
5437  $formconfirm .= '
5438  console.log("after post ok");
5439  }
5440  $(this).dialog("close");
5441  },
5442  "' . dol_escape_js($langs->transnoentities($labelbuttonno)) . '": function() {
5443  var options = "token=' . urlencode(newToken()) . '";
5444  var inputko = ' . json_encode($inputko) . '; /* List of fields into form */
5445  var page = "' . dol_escape_js(!empty($page) ? $page : '') . '";
5446  var pageno="' . dol_escape_js(!empty($pageno) ? $pageno : '') . '";
5447  if (inputko.length > 0) {
5448  $.each(inputko, function(i, inputname) {
5449  var more = "";
5450  if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5451  var inputvalue = $("#" + inputname + more).val();
5452  if (typeof inputvalue == "undefined") { inputvalue=""; }
5453  options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5454  });
5455  }
5456  var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "&") + options;
5457  //alert(urljump);
5458  if (pageno.length > 0) {';
5459  if ($postconfirmas == 'GET') {
5460  $formconfirm .= 'location.href = urljump;';
5461  } else {
5462  $formconfirm .= $jsforcursor;
5463  $formconfirm .= 'var post = $.post(
5464  pageno,
5465  options,
5466  function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
5467  );';
5468  }
5469  $formconfirm .= '
5470  console.log("after post ko");
5471  }
5472  $(this).dialog("close");
5473  }
5474  }
5475  }
5476  );
5477 
5478  var button = "' . $button . '";
5479  if (button.length > 0) {
5480  $( "#" + button ).click(function() {
5481  $("#' . $dialogconfirm . '").dialog("open");
5482  });
5483  }
5484  });
5485  });
5486  </script>';
5487  $formconfirm .= "<!-- end ajax formconfirm -->\n";
5488  } else {
5489  $formconfirm .= "\n<!-- begin formconfirm page=" . dol_escape_htmltag($page) . " -->\n";
5490 
5491  if (empty($disableformtag)) {
5492  $formconfirm .= '<form method="POST" action="' . $page . '" class="notoptoleftroright">' . "\n";
5493  }
5494 
5495  $formconfirm .= '<input type="hidden" name="action" value="' . $action . '">' . "\n";
5496  $formconfirm .= '<input type="hidden" name="token" value="' . newToken() . '">' . "\n";
5497 
5498  $formconfirm .= '<table class="valid centpercent">' . "\n";
5499 
5500  // Line title
5501  $formconfirm .= '<tr class="validtitre"><td class="validtitre" colspan="2">';
5502  $formconfirm .= img_picto('', 'pictoconfirm') . ' ' . $title;
5503  $formconfirm .= '</td></tr>' . "\n";
5504 
5505  // Line text
5506  if (is_array($formquestion) && !empty($formquestion['text'])) {
5507  $formconfirm .= '<tr class="valid"><td class="valid" colspan="2">' . $formquestion['text'] . '</td></tr>' . "\n";
5508  }
5509 
5510  // Line form fields
5511  if ($more) {
5512  $formconfirm .= '<tr class="valid"><td class="valid" colspan="2">' . "\n";
5513  $formconfirm .= $more;
5514  $formconfirm .= '</td></tr>' . "\n";
5515  }
5516 
5517  // Line with question
5518  $formconfirm .= '<tr class="valid">';
5519  $formconfirm .= '<td class="valid">' . $question . '</td>';
5520  $formconfirm .= '<td class="valid center">';
5521  $formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly', $labelbuttonyes, $labelbuttonno);
5522  $formconfirm .= '<input class="button valignmiddle confirmvalidatebutton small" type="submit" value="' . $langs->trans("Validate") . '">';
5523  $formconfirm .= '</td>';
5524  $formconfirm .= '</tr>' . "\n";
5525 
5526  $formconfirm .= '</table>' . "\n";
5527 
5528  if (empty($disableformtag)) {
5529  $formconfirm .= "</form>\n";
5530  }
5531  $formconfirm .= '<br>';
5532 
5533  if (!empty($conf->use_javascript_ajax)) {
5534  $formconfirm .= '<!-- code to disable button to avoid double clic -->';
5535  $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
5536  $formconfirm .= '
5537  $(document).ready(function () {
5538  $(".confirmvalidatebutton").on("click", function() {
5539  console.log("We click on button");
5540  $(this).attr("disabled", "disabled");
5541  setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
5542  //console.log($(this).closest("form"));
5543  $(this).closest("form").submit();
5544  });
5545  });
5546  ';
5547  $formconfirm .= '</script>' . "\n";
5548  }
5549 
5550  $formconfirm .= "<!-- end formconfirm -->\n";
5551  }
5552 
5553  return $formconfirm;
5554  }
5555 
5556 
5557  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5558 
5574  public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0, $textifnoproject = '', $morecss = '')
5575  {
5576  // phpcs:enable
5577  global $langs;
5578 
5579  require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
5580  require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
5581 
5582  $out = '';
5583 
5584  $formproject = new FormProjets($this->db);
5585 
5586  $langs->load("project");
5587  if ($htmlname != "none") {
5588  $out .= '<form method="post" action="' . $page . '">';
5589  $out .= '<input type="hidden" name="action" value="classin">';
5590  $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
5591  $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1, 0, $morecss);
5592  $out .= '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
5593  $out .= '</form>';
5594  } else {
5595  $out .= '<span class="project_head_block">';
5596  if ($selected) {
5597  $projet = new Project($this->db);
5598  $projet->fetch($selected);
5599  $out .= $projet->getNomUrl(0, '', 1);
5600  } else {
5601  $out .= '<span class="opacitymedium">' . $textifnoproject . '</span>';
5602  }
5603  $out .= '</span>';
5604  }
5605 
5606  if (empty($nooutput)) {
5607  print $out;
5608  return '';
5609  }
5610  return $out;
5611  }
5612 
5613  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5614 
5630  public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0, $type = '', $filtertype = -1, $deposit_percent = -1, $nooutput = 0)
5631  {
5632  // phpcs:enable
5633  global $langs;
5634 
5635  $out = '';
5636 
5637  if ($htmlname != "none") {
5638  $out .= '<form method="POST" action="' . $page . '">';
5639  $out .= '<input type="hidden" name="action" value="setconditions">';
5640  $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
5641  if ($type) {
5642  $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
5643  }
5644  $out .= $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, 0, '', $deposit_percent);
5645  $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
5646  $out .= '</form>';
5647  } else {
5648  if ($selected) {
5650  if (isset($this->cache_conditions_paiements[$selected])) {
5651  $label = $this->cache_conditions_paiements[$selected]['label'];
5652 
5653  if (!empty($this->cache_conditions_paiements[$selected]['deposit_percent'])) {
5654  $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $this->cache_conditions_paiements[$selected]['deposit_percent'], $label);
5655  }
5656 
5657  $out .= $label;
5658  } else {
5659  $langs->load('errors');
5660  $out .= $langs->trans('ErrorNotInDictionaryPaymentConditions');
5661  }
5662  } else {
5663  $out .= '&nbsp;';
5664  }
5665  }
5666 
5667  if (empty($nooutput)) {
5668  print $out;
5669  return '';
5670  }
5671  return $out;
5672  }
5673 
5674  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5675 
5685  public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
5686  {
5687  // phpcs:enable
5688  global $langs;
5689  if ($htmlname != "none") {
5690  print '<form method="post" action="' . $page . '">';
5691  print '<input type="hidden" name="action" value="setavailability">';
5692  print '<input type="hidden" name="token" value="' . newToken() . '">';
5693  $this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty);
5694  print '<input type="submit" name="modify" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
5695  print '<input type="submit" name="cancel" class="button smallpaddingimp" value="' . $langs->trans("Cancel") . '">';
5696  print '</form>';
5697  } else {
5698  if ($selected) {
5699  $this->load_cache_availability();
5700  print $this->cache_availability[$selected]['label'];
5701  } else {
5702  print "&nbsp;";
5703  }
5704  }
5705  }
5706 
5717  public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0)
5718  {
5719  global $langs;
5720  if ($htmlname != "none") {
5721  print '<form method="post" action="' . $page . '">';
5722  print '<input type="hidden" name="action" value="setdemandreason">';
5723  print '<input type="hidden" name="token" value="' . newToken() . '">';
5724  $this->selectInputReason($selected, $htmlname, -1, $addempty);
5725  print '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
5726  print '</form>';
5727  } else {
5728  if ($selected) {
5729  $this->loadCacheInputReason();
5730  foreach ($this->cache_demand_reason as $key => $val) {
5731  if ($val['id'] == $selected) {
5732  print $val['label'];
5733  break;
5734  }
5735  }
5736  } else {
5737  print "&nbsp;";
5738  }
5739  }
5740  }
5741 
5742  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5743 
5757  public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0, $type = '')
5758  {
5759  // phpcs:enable
5760  global $langs;
5761 
5762  $ret = '';
5763 
5764  if ($htmlname != "none") {
5765  $ret .= '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
5766  $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
5767  $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
5768  if ($type) {
5769  $ret .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
5770  }
5771  $ret .= '<table class="nobordernopadding">';
5772  $ret .= '<tr><td>';
5773  $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form' . $htmlname, 1, 0);
5774  $ret .= '</td>';
5775  $ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
5776  $ret .= '</tr></table></form>';
5777  } else {
5778  if ($displayhour) {
5779  $ret .= dol_print_date($selected, 'dayhour');
5780  } else {
5781  $ret .= dol_print_date($selected, 'day');
5782  }
5783  }
5784 
5785  if (empty($nooutput)) {
5786  print $ret;
5787  }
5788  return $ret;
5789  }
5790 
5791 
5792  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5793 
5804  public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = '', $include = '')
5805  {
5806  // phpcs:enable
5807  global $langs;
5808 
5809  if ($htmlname != "none") {
5810  print '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
5811  print '<input type="hidden" name="action" value="set' . $htmlname . '">';
5812  print '<input type="hidden" name="token" value="' . newToken() . '">';
5813  print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
5814  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
5815  print '</form>';
5816  } else {
5817  if ($selected) {
5818  require_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
5819  $theuser = new User($this->db);
5820  $theuser->fetch($selected);
5821  print $theuser->getNomUrl(1);
5822  } else {
5823  print "&nbsp;";
5824  }
5825  }
5826  }
5827 
5828 
5829  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5830 
5844  public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0, $type = '', $nooutput = 0)
5845  {
5846  // phpcs:enable
5847  global $langs;
5848 
5849  $out = '';
5850  if ($htmlname != "none") {
5851  $out .= '<form method="POST" action="' . $page . '">';
5852  $out .= '<input type="hidden" name="action" value="setmode">';
5853  $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
5854  if ($type) {
5855  $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
5856  }
5857  $out .= $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active, '', 1);
5858  $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
5859  $out .= '</form>';
5860  } else {
5861  if ($selected) {
5862  $this->load_cache_types_paiements();
5863  $out .= $this->cache_types_paiements[$selected]['label'];
5864  } else {
5865  $out .= "&nbsp;";
5866  }
5867  }
5868 
5869  if ($nooutput) {
5870  return $out;
5871  } else {
5872  print $out;
5873  }
5874  return '';
5875  }
5876 
5887  public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
5888  {
5889  global $langs;
5890  if ($htmlname != "none") {
5891  print '<form method="POST" action="' . $page . '">';
5892  print '<input type="hidden" name="action" value="settransportmode">';
5893  print '<input type="hidden" name="token" value="' . newToken() . '">';
5894  $this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
5895  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
5896  print '</form>';
5897  } else {
5898  if ($selected) {
5899  $this->load_cache_transport_mode();
5900  print $this->cache_transport_mode[$selected]['label'];
5901  } else {
5902  print "&nbsp;";
5903  }
5904  }
5905  }
5906 
5907  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5908 
5917  public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
5918  {
5919  // phpcs:enable
5920  global $langs;
5921  if ($htmlname != "none") {
5922  print '<form method="POST" action="' . $page . '">';
5923  print '<input type="hidden" name="action" value="setmulticurrencycode">';
5924  print '<input type="hidden" name="token" value="' . newToken() . '">';
5925  print $this->selectMultiCurrency($selected, $htmlname, 0);
5926  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
5927  print '</form>';
5928  } else {
5929  dol_include_once('/core/lib/company.lib.php');
5930  print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
5931  }
5932  }
5933 
5934  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5935 
5945  public function form_multicurrency_rate($page, $rate = '', $htmlname = 'multicurrency_tx', $currency = '')
5946  {
5947  // phpcs:enable
5948  global $langs, $mysoc, $conf;
5949 
5950  if ($htmlname != "none") {
5951  print '<form method="POST" action="' . $page . '">';
5952  print '<input type="hidden" name="action" value="setmulticurrencyrate">';
5953  print '<input type="hidden" name="token" value="' . newToken() . '">';
5954  print '<input type="text" class="maxwidth100" name="' . $htmlname . '" value="' . (!empty($rate) ? price(price2num($rate, 'CU')) : 1) . '" /> ';
5955  print '<select name="calculation_mode">';
5956  print '<option value="1">Change ' . $langs->trans("PriceUHT") . ' of lines</option>';
5957  print '<option value="2">Change ' . $langs->trans("PriceUHTCurrency") . ' of lines</option>';
5958  print '</select> ';
5959  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
5960  print '</form>';
5961  } else {
5962  if (!empty($rate)) {
5963  print price($rate, 1, $langs, 1, 0);
5964  if ($currency && $rate != 1) {
5965  print ' &nbsp; (' . price($rate, 1, $langs, 1, 0) . ' ' . $currency . ' = 1 ' . $conf->currency . ')';
5966  }
5967  } else {
5968  print 1;
5969  }
5970  }
5971  }
5972 
5973 
5974  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5975 
5991  public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0)
5992  {
5993  // phpcs:enable
5994  global $conf, $langs;
5995  if ($htmlname != "none") {
5996  print '<form method="post" action="' . $page . '">';
5997  print '<input type="hidden" name="action" value="setabsolutediscount">';
5998  print '<input type="hidden" name="token" value="' . newToken() . '">';
5999  print '<div class="inline-block">';
6000  if (!empty($discount_type)) {
6001  if (!empty($conf->global->FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS)) {
6002  if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
6003  $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be substracted to payments only and not to total of final invoice
6004  } else {
6005  $translationKey = 'HasCreditNoteFromSupplier';
6006  }
6007  } else {
6008  if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
6009  $translationKey = 'HasAbsoluteDiscountFromSupplier';
6010  } else {
6011  $translationKey = 'HasCreditNoteFromSupplier';
6012  }
6013  }
6014  } else {
6015  if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
6016  if (!$filter || $filter == "fk_facture_source IS NULL") {
6017  $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be substracted to payments only and not to total of final invoice
6018  } else {
6019  $translationKey = 'CompanyHasCreditNote';
6020  }
6021  } else {
6022  if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
6023  $translationKey = 'CompanyHasAbsoluteDiscount';
6024  } else {
6025  $translationKey = 'CompanyHasCreditNote';
6026  }
6027  }
6028  }
6029  print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
6030  if (empty($hidelist)) {
6031  print ' ';
6032  }
6033  print '</div>';
6034  if (empty($hidelist)) {
6035  print '<div class="inline-block" style="padding-right: 10px">';
6036  $newfilter = 'discount_type=' . intval($discount_type);
6037  if (!empty($discount_type)) {
6038  $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
6039  } else {
6040  $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
6041  }
6042  if ($filter) {
6043  $newfilter .= ' AND (' . $filter . ')';
6044  }
6045  // output the combo of discounts
6046  $nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue);
6047  if ($nbqualifiedlines > 0) {
6048  print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="' . dol_escape_htmltag($langs->trans("UseLine")) . '"';
6049  if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
6050  print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
6051  }
6052  if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
6053  print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
6054  }
6055 
6056  print '>';
6057  }
6058  print '</div>';
6059  }
6060  if ($more) {
6061  print '<div class="inline-block">';
6062  print $more;
6063  print '</div>';
6064  }
6065  print '</form>';
6066  } else {
6067  if ($selected) {
6068  print $selected;
6069  } else {
6070  print "0";
6071  }
6072  }
6073  }
6074 
6075 
6076  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6077 
6087  public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
6088  {
6089  // phpcs:enable
6090  global $langs, $conf;
6091 
6092  if ($htmlname != "none") {
6093  print '<form method="post" action="' . $page . '">';
6094  print '<input type="hidden" name="action" value="set_contact">';
6095  print '<input type="hidden" name="token" value="' . newToken() . '">';
6096  print '<table class="nobordernopadding">';
6097  print '<tr><td>';
6098  print $this->selectcontacts($societe->id, $selected, $htmlname);
6099  $num = $this->num;
6100  if ($num == 0) {
6101  $addcontact = (!empty($conf->global->SOCIETE_ADDRESSES_MANAGEMENT) ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
6102  print '<a href="' . DOL_URL_ROOT . '/contact/card.php?socid=' . $societe->id . '&amp;action=create&amp;backtoreferer=1">' . $addcontact . '</a>';
6103  }
6104  print '</td>';
6105  print '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
6106  print '</tr></table></form>';
6107  } else {
6108  if ($selected) {
6109  require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
6110  $contact = new Contact($this->db);
6111  $contact->fetch($selected);
6112  print $contact->getFullName($langs);
6113  } else {
6114  print "&nbsp;";
6115  }
6116  }
6117  }
6118 
6119  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6120 
6137  public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array(), $textifnothirdparty = '')
6138  {
6139  // phpcs:enable
6140  global $langs;
6141 
6142  $out = '';
6143  if ($htmlname != "none") {
6144  $out .= '<form method="post" action="' . $page . '">';
6145  $out .= '<input type="hidden" name="action" value="set_thirdparty">';
6146  $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
6147  $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, 0, 'minwidth100', '', '', 1, array(), false, $excludeids);
6148  $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6149  $out .= '</form>';
6150  } else {
6151  if ($selected) {
6152  require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
6153  $soc = new Societe($this->db);
6154  $soc->fetch($selected);
6155  $out .= $soc->getNomUrl(0, '');
6156  } else {
6157  $out .= '<span class="opacitymedium">' . $textifnothirdparty . '</span>';
6158  }
6159  }
6160 
6161  if ($nooutput) {
6162  return $out;
6163  } else {
6164  print $out;
6165  }
6166 
6167  return '';
6168  }
6169 
6170  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6171 
6180  public function select_currency($selected = '', $htmlname = 'currency_id')
6181  {
6182  // phpcs:enable
6183  print $this->selectCurrency($selected, $htmlname);
6184  }
6185 
6195  public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0, $useempty = '')
6196  {
6197  global $conf, $langs, $user;
6198 
6199  $langs->loadCacheCurrencies('');
6200 
6201  $out = '';
6202 
6203  if ($selected == 'euro' || $selected == 'euros') {
6204  $selected = 'EUR'; // Pour compatibilite
6205  }
6206 
6207  $out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="' . $htmlname . '" id="' . $htmlname . '">';
6208  if ($useempty) {
6209  $out .= '<option value="-1" selected></option>';
6210  }
6211  foreach ($langs->cache_currencies as $code_iso => $currency) {
6212  $labeltoshow = $currency['label'];
6213  if ($mode == 1) {
6214  $labeltoshow .= ' <span class="opacitymedium">(' . $code_iso . ')</span>';
6215  } else {
6216  $labeltoshow .= ' <span class="opacitymedium">(' . $langs->getCurrencySymbol($code_iso) . ')</span>';
6217  }
6218 
6219  if ($selected && $selected == $code_iso) {
6220  $out .= '<option value="' . $code_iso . '" selected data-html="' . dol_escape_htmltag($labeltoshow) . '">';
6221  } else {
6222  $out .= '<option value="' . $code_iso . '" data-html="' . dol_escape_htmltag($labeltoshow) . '">';
6223  }
6224  $out .= $labeltoshow;
6225  $out .= '</option>';
6226  }
6227  $out .= '</select>';
6228  if ($user->admin) {
6229  $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
6230  }
6231 
6232  // Make select dynamic
6233  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
6234  $out .= ajax_combobox($htmlname);
6235 
6236  return $out;
6237  }
6238 
6251  public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false, $morecss = '')
6252  {
6253  global $conf, $langs;
6254 
6255  $langs->loadCacheCurrencies(''); // Load ->cache_currencies
6256 
6257  $TCurrency = array();
6258 
6259  $sql = "SELECT code FROM " . $this->db->prefix() . "multicurrency";
6260  $sql .= " WHERE entity IN ('" . getEntity('mutlicurrency') . "')";
6261  if ($filter) {
6262  $sql .= " AND " . $filter;
6263  }
6264  $resql = $this->db->query($sql);
6265  if ($resql) {
6266  while ($obj = $this->db->fetch_object($resql)) {
6267  $TCurrency[$obj->code] = $obj->code;
6268  }
6269  }
6270 
6271  $out = '';
6272  $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
6273  if ($useempty) {
6274  $out .= '<option value="">&nbsp;</option>';
6275  }
6276  // If company current currency not in table, we add it into list. Should always be available.
6277  if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
6278  $TCurrency[$conf->currency] = $conf->currency;
6279  }
6280  if (count($TCurrency) > 0) {
6281  foreach ($langs->cache_currencies as $code_iso => $currency) {
6282  if (isset($TCurrency[$code_iso])) {
6283  if (!empty($selected) && $selected == $code_iso) {
6284  $out .= '<option value="' . $code_iso . '" selected="selected">';
6285  } else {
6286  $out .= '<option value="' . $code_iso . '">';
6287  }
6288 
6289  $out .= $currency['label'];
6290  $out .= ' (' . $langs->getCurrencySymbol($code_iso) . ')';
6291  $out .= '</option>';
6292  }
6293  }
6294  }
6295 
6296  $out .= '</select>';
6297 
6298  // Make select dynamic
6299  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
6300  $out .= ajax_combobox($htmlname);
6301 
6302  return $out;
6303  }
6304 
6305  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6306 
6313  public function load_cache_vatrates($country_code)
6314  {
6315  // phpcs:enable
6316  global $langs, $user;
6317 
6318  $num = count($this->cache_vatrates);
6319  if ($num > 0) {
6320  return $num; // Cache already loaded
6321  }
6322 
6323  dol_syslog(__METHOD__, LOG_DEBUG);
6324 
6325  $sql = "SELECT DISTINCT t.rowid, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly";
6326  $sql .= " FROM " . $this->db->prefix() . "c_tva as t, " . $this->db->prefix() . "c_country as c";
6327  $sql .= " WHERE t.fk_pays = c.rowid";
6328  $sql .= " AND t.active > 0";
6329  $sql .= " AND c.code IN (" . $this->db->sanitize($country_code, 1) . ")";
6330  $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
6331 
6332  $resql = $this->db->query($sql);
6333  if ($resql) {
6334  $num = $this->db->num_rows($resql);
6335  if ($num) {
6336  for ($i = 0; $i < $num; $i++) {
6337  $obj = $this->db->fetch_object($resql);
6338  $this->cache_vatrates[$i]['rowid'] = $obj->rowid;
6339  $this->cache_vatrates[$i]['code'] = $obj->code;
6340  $this->cache_vatrates[$i]['txtva'] = $obj->taux;
6341  $this->cache_vatrates[$i]['nprtva'] = $obj->recuperableonly;
6342  $this->cache_vatrates[$i]['localtax1'] = $obj->localtax1;
6343  $this->cache_vatrates[$i]['localtax1_type'] = $obj->localtax1_type;
6344  $this->cache_vatrates[$i]['localtax2'] = $obj->localtax2;
6345  $this->cache_vatrates[$i]['localtax2_type'] = $obj->localtax1_type;
6346 
6347  $this->cache_vatrates[$i]['label'] = $obj->taux . '%' . ($obj->code ? ' (' . $obj->code . ')' : ''); // Label must contains only 0-9 , . % or *
6348  $this->cache_vatrates[$i]['labelallrates'] = $obj->taux . '/' . ($obj->localtax1 ? $obj->localtax1 : '0') . '/' . ($obj->localtax2 ? $obj->localtax2 : '0') . ($obj->code ? ' (' . $obj->code . ')' : ''); // Must never be used as key, only label
6349  $positiverates = '';
6350  if ($obj->taux) {
6351  $positiverates .= ($positiverates ? '/' : '') . $obj->taux;
6352  }
6353  if ($obj->localtax1) {
6354  $positiverates .= ($positiverates ? '/' : '') . $obj->localtax1;
6355  }
6356  if ($obj->localtax2) {
6357  $positiverates .= ($positiverates ? '/' : '') . $obj->localtax2;
6358  }
6359  if (empty($positiverates)) {
6360  $positiverates = '0';
6361  }
6362  $this->cache_vatrates[$i]['labelpositiverates'] = $positiverates . ($obj->code ? ' (' . $obj->code . ')' : ''); // Must never be used as key, only label
6363  }
6364 
6365  return $num;
6366  } else {
6367  $this->error = '<span class="error">';
6368  $this->error .= $langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code);
6369  $reg = array();
6370  if (!empty($user) && $user->admin && preg_match('/\'(..)\'/', $country_code, $reg)) {
6371  $langs->load("errors");
6372  $new_country_code = $reg[1];
6373  $country_id = dol_getIdFromCode($this->db, $new_country_code, 'c_pays', 'code', 'rowid');
6374  $this->error .= '<br>'.$langs->trans("ErrorFixThisHere", DOL_URL_ROOT.'/admin/dict.php?id=10'.($country_id > 0 ? '&countryidforinsert='.$country_id : ''));
6375  }
6376  $this->error .= '</span>';
6377  return -1;
6378  }
6379  } else {
6380  $this->error = '<span class="error">' . $this->db->error() . '</span>';
6381  return -2;
6382  }
6383  }
6384 
6385  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6386 
6408  public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = '', $societe_acheteuse = '', $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0)
6409  {
6410  // phpcs:enable
6411  global $langs, $conf, $mysoc;
6412 
6413  $langs->load('errors');
6414 
6415  $return = '';
6416 
6417  // Define defaultnpr, defaultttx and defaultcode
6418  $defaultnpr = ($info_bits & 0x01);
6419  $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
6420  $defaulttx = str_replace('*', '', $selectedrate);
6421  $defaultcode = '';
6422  $reg = array();
6423  if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
6424  $defaultcode = $reg[1];
6425  $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
6426  }
6427  //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
6428 
6429  // Check parameters
6430  if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
6431  if ($societe_vendeuse->id == $mysoc->id) {
6432  $return .= '<span class="error">' . $langs->trans("ErrorYourCountryIsNotDefined") . '</span>';
6433  } else {
6434  $return .= '<span class="error">' . $langs->trans("ErrorSupplierCountryIsNotDefined") . '</span>';
6435  }
6436  return $return;
6437  }
6438 
6439  //var_dump($societe_acheteuse);
6440  //print "name=$name, selectedrate=$selectedrate, seller=".$societe_vendeuse->country_code." buyer=".$societe_acheteuse->country_code." buyer is company=".$societe_acheteuse->isACompany()." idprod=$idprod, info_bits=$info_bits type=$type";
6441  //exit;
6442 
6443  // Define list of countries to use to search VAT rates to show
6444  // First we defined code_country to use to find list
6445  if (is_object($societe_vendeuse)) {
6446  $code_country = "'" . $societe_vendeuse->country_code . "'";
6447  } else {
6448  $code_country = "'" . $mysoc->country_code . "'"; // Pour compatibilite ascendente
6449  }
6450  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) { // If option to have vat for end customer for services is on
6451  require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
6452  if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) {
6453  // We also add the buyer country code
6454  if (is_numeric($type)) {
6455  if ($type == 1) { // We know product is a service
6456  $code_country .= ",'" . $societe_acheteuse->country_code . "'";
6457  }
6458  } elseif (!$idprod) { // We don't know type of product
6459  $code_country .= ",'" . $societe_acheteuse->country_code . "'";
6460  } else {
6461  $prodstatic = new Product($this->db);
6462  $prodstatic->fetch($idprod);
6463  if ($prodstatic->type == Product::TYPE_SERVICE) { // We know product is a service
6464  $code_country .= ",'" . $societe_acheteuse->country_code . "'";
6465  }
6466  }
6467  }
6468  }
6469 
6470  // Now we get list
6471  $num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
6472 
6473  if ($num > 0) {
6474  // Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '')
6475  if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
6476  $tmpthirdparty = new Societe($this->db);
6477 
6478  $defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
6479  $defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
6480 
6481  if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
6482  $defaultcode = $reg[1];
6483  $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
6484  }
6485  if (empty($defaulttx)) {
6486  $defaultnpr = 0;
6487  }
6488  }
6489 
6490  // If we fails to find a default vat rate, we take the last one in list
6491  // Because they are sorted in ascending order, the last one will be the higher one (we suppose the higher one is the current rate)
6492  if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
6493  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6494  // We take the last one found in list
6495  $defaulttx = $this->cache_vatrates[$num - 1]['txtva'];
6496  } else {
6497  // We will use the rate defined into MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS
6498  $defaulttx = '';
6499  if ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS != 'none') {
6500  $defaulttx = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;
6501  }
6502  if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
6503  $defaultcode = $reg[1];
6504  $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
6505  }
6506  }
6507  }
6508 
6509  // Disabled if seller is not subject to VAT
6510  $disabled = false;
6511  $title = '';
6512  if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0") {
6513  // Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead
6514  // of using supplier invoices (this is a very bad idea !)
6515  if (empty($conf->global->EXPENSEREPORT_OVERRIDE_VAT)) {
6516  $title = ' title="' . dol_escape_htmltag($langs->trans('VATIsNotUsed')) . '"';
6517  $disabled = true;
6518  }
6519  }
6520 
6521  if (!$options_only) {
6522  $return .= '<select class="flat minwidth50imp maxwidth100" id="' . $htmlname . '" name="' . $htmlname . '"' . ($disabled ? ' disabled' : '') . $title . '>';
6523  }
6524 
6525  $selectedfound = false;
6526  foreach ($this->cache_vatrates as $rate) {
6527  // Keep only 0 if seller is not subject to VAT
6528  if ($disabled && $rate['txtva'] != 0) {
6529  continue;
6530  }
6531 
6532  // Define key to use into select list
6533  $key = $rate['txtva'];
6534  $key .= $rate['nprtva'] ? '*' : '';
6535  if ($mode > 0 && $rate['code']) {
6536  $key .= ' (' . $rate['code'] . ')';
6537  }
6538  if ($mode < 0) {
6539  $key = $rate['rowid'];
6540  }
6541 
6542  $return .= '<option value="' . $key . '"';
6543  if (!$selectedfound) {
6544  if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
6545  if ($defaultcode == $rate['code']) {
6546  $return .= ' selected';
6547  $selectedfound = true;
6548  }
6549  } elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
6550  $return .= ' selected';
6551  $selectedfound = true;
6552  }
6553  }
6554  $return .= '>';
6555 
6556  // Show label of VAT
6557  if ($mysoc->country_code == 'IN' || !empty($conf->global->MAIN_VAT_LABEL_IS_POSITIVE_RATES)) {
6558  // Label with all localtax and code. For example: x.y / a.b / c.d (CODE)'
6559  $return .= $rate['labelpositiverates'];
6560  } else {
6561  // Simple label
6562  $return .= vatrate($rate['label']);
6563  }
6564 
6565  //$return.=($rate['code']?' '.$rate['code']:'');
6566  $return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the * (old behaviour only if new vat code is not used)
6567 
6568  $return .= '</option>';
6569  }
6570 
6571  if (!$options_only) {
6572  $return .= '</select>';
6573  //$return .= ajax_combobox($htmlname); // This break for the moment the dynamic autoselection of a value when selecting a product in object lines
6574  }
6575  } else {
6576  $return .= $this->error;
6577  }
6578 
6579  $this->num = $num;
6580  return $return;
6581  }
6582 
6583 
6584  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6585 
6610  public function select_date($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $nooutput = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '')
6611  {
6612  // phpcs:enable
6613  $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
6614  if (!empty($nooutput)) {
6615  return $retstring;
6616  }
6617  print $retstring;
6618 
6619  return '';
6620  }
6621 
6637  public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0, $forcenewline = 0)
6638  {
6639  global $langs;
6640 
6641  $ret = $this->selectDate($set_time, $prefix . '_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
6642  if ($forcenewline) {
6643  $ret .= '<br>';
6644  }
6645  $ret .= $this->selectDate($set_time_end, $prefix . '_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
6646  return $ret;
6647  }
6648 
6676  public function selectDate($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '', $openinghours = '', $stepminutes = 1, $labeladddateof = '', $placeholder = '', $gm = 'auto')
6677  {
6678  global $conf, $langs;
6679 
6680  if ($gm === 'auto') {
6681  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
6682  }
6683 
6684  $retstring = '';
6685 
6686  if ($prefix == '') {
6687  $prefix = 're';
6688  }
6689  if ($h == '') {
6690  $h = 0;
6691  }
6692  if ($m == '') {
6693  $m = 0;
6694  }
6695  $emptydate = 0;
6696  $emptyhours = 0;
6697  if ($stepminutes <= 0 || $stepminutes > 30) {
6698  $stepminutes = 1;
6699  }
6700  if ($empty == 1) {
6701  $emptydate = 1;
6702  $emptyhours = 1;
6703  }
6704  if ($empty == 2) {
6705  $emptydate = 0;
6706  $emptyhours = 1;
6707  }
6708  $orig_set_time = $set_time;
6709 
6710  if ($set_time === '' && $emptydate == 0) {
6711  include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
6712  if ($gm == 'tzuser' || $gm == 'tzuserrel') {
6713  $set_time = dol_now($gm);
6714  } else {
6715  $set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
6716  }
6717  }
6718 
6719  // Analysis of the pre-selection date
6720  $reg = array();
6721  $shour = '';
6722  $smin = '';
6723  $ssec = '';
6724  if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', $set_time, $reg)) { // deprecated usage
6725  // Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
6726  $syear = (!empty($reg[1]) ? $reg[1] : '');
6727  $smonth = (!empty($reg[2]) ? $reg[2] : '');
6728  $sday = (!empty($reg[3]) ? $reg[3] : '');
6729  $shour = (!empty($reg[4]) ? $reg[4] : '');
6730  $smin = (!empty($reg[5]) ? $reg[5] : '');
6731  } elseif (strval($set_time) != '' && $set_time != -1) {
6732  // set_time est un timestamps (0 possible)
6733  $syear = dol_print_date($set_time, "%Y", $gm);
6734  $smonth = dol_print_date($set_time, "%m", $gm);
6735  $sday = dol_print_date($set_time, "%d", $gm);
6736  if ($orig_set_time != '') {
6737  $shour = dol_print_date($set_time, "%H", $gm);
6738  $smin = dol_print_date($set_time, "%M", $gm);
6739  $ssec = dol_print_date($set_time, "%S", $gm);
6740  }
6741  } else {
6742  // Date est '' ou vaut -1
6743  $syear = '';
6744  $smonth = '';
6745  $sday = '';
6746  $shour = !isset($conf->global->MAIN_DEFAULT_DATE_HOUR) ? ($h == -1 ? '23' : '') : $conf->global->MAIN_DEFAULT_DATE_HOUR;
6747  $smin = !isset($conf->global->MAIN_DEFAULT_DATE_MIN) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_MIN;
6748  $ssec = !isset($conf->global->MAIN_DEFAULT_DATE_SEC) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_SEC;
6749  }
6750  if ($h == 3) {
6751  $shour = '';
6752  }
6753  if ($m == 3) {
6754  $smin = '';
6755  }
6756 
6757  $nowgmt = dol_now('gmt');
6758  //var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
6759 
6760  // You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
6761  $usecalendar = 'combo';
6762  if (!empty($conf->use_javascript_ajax) && (empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR != "none")) {
6763  $usecalendar = ((empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR == 'eldy') ? 'jquery' : $conf->global->MAIN_POPUP_CALENDAR);
6764  }
6765 
6766  if ($d) {
6767  // Show date with popup
6768  if ($usecalendar != 'combo') {
6769  $formated_date = '';
6770  //print "e".$set_time." t ".$conf->format_date_short;
6771  if (strval($set_time) != '' && $set_time != -1) {
6772  //$formated_date=dol_print_date($set_time,$conf->format_date_short);
6773  $formated_date = dol_print_date($set_time, $langs->trans("FormatDateShortInput"), $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6774  }
6775 
6776  // Calendrier popup version eldy
6777  if ($usecalendar == "eldy") {
6778  // Input area to enter date manually
6779  $retstring .= '<input id="' . $prefix . '" name="' . $prefix . '" type="text" class="maxwidthdate" maxlength="11" value="' . $formated_date . '"';
6780  $retstring .= ($disabled ? ' disabled' : '');
6781  $retstring .= ' onChange="dpChangeDay(\'' . $prefix . '\',\'' . $langs->trans("FormatDateShortJavaInput") . '\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6782  $retstring .= '>';
6783 
6784  // Icon calendar
6785  $retstringbuttom = '';
6786  if (!$disabled) {
6787  $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons"';
6788  $base = DOL_URL_ROOT . '/core/';
6789  $retstringbuttom .= ' onClick="showDP(\'' . $base . '\',\'' . $prefix . '\',\'' . $langs->trans("FormatDateShortJavaInput") . '\',\'' . $langs->defaultlang . '\');"';
6790  $retstringbuttom .= '>' . img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink"') . '</button>';
6791  } else {
6792  $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"') . '</button>';
6793  }
6794  $retstring = $retstringbuttom . $retstring;
6795 
6796  $retstring .= '<input type="hidden" id="' . $prefix . 'day" name="' . $prefix . 'day" value="' . $sday . '">' . "\n";
6797  $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
6798  $retstring .= '<input type="hidden" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">' . "\n";
6799  } elseif ($usecalendar == 'jquery') {
6800  if (!$disabled) {
6801  // Output javascript for datepicker
6802  $minYear = getDolGlobalInt('MIN_YEAR_SELECT_DATE', (date('Y') - 100));
6803  $maxYear = getDolGlobalInt('MAX_YEAR_SELECT_DATE', (date('Y') + 100));
6804 
6805  $retstring .= '<script nonce="' . getNonce() . '" type="text/javascript">';
6806  $retstring .= "$(function(){ $('#" . $prefix . "').datepicker({
6807  dateFormat: '" . $langs->trans("FormatDateShortJQueryInput") . "',
6808  autoclose: true,
6809  todayHighlight: true,
6810  yearRange: '" . $minYear . ":" . $maxYear . "',";
6811  if (!empty($conf->dol_use_jmobile)) {
6812  $retstring .= "
6813  beforeShow: function (input, datePicker) {
6814  input.disabled = true;
6815  },
6816  onClose: function (dateText, datePicker) {
6817  this.disabled = false;
6818  },
6819  ";
6820  }
6821  // Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
6822  if (empty($conf->global->MAIN_POPUP_CALENDAR_ON_FOCUS)) {
6823  $retstring .= "
6824  showOn: 'button', /* both has problem with autocompletion */
6825  buttonImage: '" . DOL_URL_ROOT . "/theme/" . dol_escape_js($conf->theme) . "/img/object_calendarday.png',
6826  buttonImageOnly: true";
6827  }
6828  $retstring .= "
6829  }) });";
6830  $retstring .= "</script>";
6831  }
6832 
6833  // Zone de saisie manuelle de la date
6834  $retstring .= '<div class="nowraponall inline-block divfordateinput">';
6835  $retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
6836  $retstring .= ($disabled ? ' disabled' : '');
6837  $retstring .= ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '');
6838  $retstring .= ' onChange="dpChangeDay(\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($langs->trans("FormatDateShortJavaInput")) . '\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6839  $retstring .= '>';
6840 
6841  // Icone calendrier
6842  if (!$disabled) {
6843  /* Not required. Managed by option buttonImage of jquery
6844  $retstring.=img_object($langs->trans("SelectDate"),'calendarday','id="'.$prefix.'id" class="datecallink"');
6845  $retstring.='<script nonce="'.getNonce().'" type="text/javascript">';
6846  $retstring.="jQuery(document).ready(function() {";
6847  $retstring.=' jQuery("#'.$prefix.'id").click(function() {';
6848  $retstring.=" jQuery('#".$prefix."').focus();";
6849  $retstring.=' });';
6850  $retstring.='});';
6851  $retstring.="</script>";*/
6852  } else {
6853  $retstringbutton = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"') . '</button>';
6854  $retsring = $retstringbutton . $retstring;
6855  }
6856 
6857  $retstring .= '</div>';
6858  $retstring .= '<input type="hidden" id="' . $prefix . 'day" name="' . $prefix . 'day" value="' . $sday . '">' . "\n";
6859  $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
6860  $retstring .= '<input type="hidden" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">' . "\n";
6861  } else {
6862  $retstring .= "Bad value of MAIN_POPUP_CALENDAR";
6863  }
6864  } else {
6865  // Show date with combo selects
6866  // Day
6867  $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50imp" id="' . $prefix . 'day" name="' . $prefix . 'day">';
6868 
6869  if ($emptydate || $set_time == -1) {
6870  $retstring .= '<option value="0" selected>&nbsp;</option>';
6871  }
6872 
6873  for ($day = 1; $day <= 31; $day++) {
6874  $retstring .= '<option value="' . $day . '"' . ($day == $sday ? ' selected' : '') . '>' . $day . '</option>';
6875  }
6876 
6877  $retstring .= "</select>";
6878 
6879  $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'month" name="' . $prefix . 'month">';
6880  if ($emptydate || $set_time == -1) {
6881  $retstring .= '<option value="0" selected>&nbsp;</option>';
6882  }
6883 
6884  // Month
6885  for ($month = 1; $month <= 12; $month++) {
6886  $retstring .= '<option value="' . $month . '"' . ($month == $smonth ? ' selected' : '') . '>';
6887  $retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
6888  $retstring .= "</option>";
6889  }
6890  $retstring .= "</select>";
6891 
6892  // Year
6893  if ($emptydate || $set_time == -1) {
6894  $retstring .= '<input' . ($disabled ? ' disabled' : '') . ' placeholder="' . dol_escape_htmltag($langs->trans("Year")) . '" class="flat maxwidth50imp valignmiddle" type="number" min="0" max="3000" maxlength="4" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">';
6895  } else {
6896  $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'year" name="' . $prefix . 'year">';
6897 
6898  for ($year = $syear - 10; $year < $syear + 10; $year++) {
6899  $retstring .= '<option value="' . $year . '"' . ($year == $syear ? ' selected' : '') . '>' . $year . '</option>';
6900  }
6901  $retstring .= "</select>\n";
6902  }
6903  }
6904  }
6905 
6906  if ($d && $h) {
6907  $retstring .= ($h == 2 ? '<br>' : ' ');
6908  $retstring .= '<span class="nowraponall">';
6909  }
6910 
6911  if ($h) {
6912  $hourstart = 0;
6913  $hourend = 24;
6914  if ($openinghours != '') {
6915  $openinghours = explode(',', $openinghours);
6916  $hourstart = $openinghours[0];
6917  $hourend = $openinghours[1];
6918  if ($hourend < $hourstart) {
6919  $hourend = $hourstart;
6920  }
6921  }
6922  // Show hour
6923  $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50 ' . ($fullday ? $fullday . 'hour' : '') . '" id="' . $prefix . 'hour" name="' . $prefix . 'hour">';
6924  if ($emptyhours) {
6925  $retstring .= '<option value="-1">&nbsp;</option>';
6926  }
6927  for ($hour = $hourstart; $hour < $hourend; $hour++) {
6928  if (strlen($hour) < 2) {
6929  $hour = "0" . $hour;
6930  }
6931  $retstring .= '<option value="' . $hour . '"' . (($hour == $shour) ? ' selected' : '') . '>' . $hour;
6932  //$retstring .= (empty($conf->dol_optimize_smallscreen) ? '' : 'H');
6933  $retstring .= '</option>';
6934  }
6935  $retstring .= '</select>';
6936  //if ($m && empty($conf->dol_optimize_smallscreen)) $retstring .= ":";
6937  if ($m) {
6938  $retstring .= ":";
6939  }
6940  }
6941 
6942  if ($m) {
6943  // Show minutes
6944  $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50 ' . ($fullday ? $fullday . 'min' : '') . '" id="' . $prefix . 'min" name="' . $prefix . 'min">';
6945  if ($emptyhours) {
6946  $retstring .= '<option value="-1">&nbsp;</option>';
6947  }
6948  for ($min = 0; $min < 60; $min += $stepminutes) {
6949  if (strlen($min) < 2) {
6950  $min = "0" . $min;
6951  }
6952  $retstring .= '<option value="' . $min . '"' . (($min == $smin) ? ' selected' : '') . '>' . $min . (empty($conf->dol_optimize_smallscreen) ? '' : '') . '</option>';
6953  }
6954  $retstring .= '</select>';
6955 
6956  $retstring .= '<input type="hidden" name="' . $prefix . 'sec" value="' . $ssec . '">';
6957  }
6958 
6959  if ($d && $h) {
6960  $retstring .= '</span>';
6961  }
6962 
6963  // Add a "Now" link
6964  if (!empty($conf->use_javascript_ajax) && $addnowlink) {
6965  // Script which will be inserted in the onClick of the "Now" link
6966  $reset_scripts = "";
6967  if ($addnowlink == 2) { // local computer time
6968  // pad add leading 0 on numbers
6969  $reset_scripts .= "Number.prototype.pad = function(size) {
6970  var s = String(this);
6971  while (s.length < (size || 2)) {s = '0' + s;}
6972  return s;
6973  };
6974  var d = new Date();";
6975  }
6976 
6977  // Generate the date part, depending on the use or not of the javascript calendar
6978  if ($addnowlink == 1) { // server time expressed in user time setup
6979  $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
6980  $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
6981  $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
6982  $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
6983  } elseif ($addnowlink == 2) {
6984  /* Disabled because the output does not use the string format defined by FormatDateShort key to forge the value into #prefix.
6985  * This break application for foreign languages.
6986  $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));';
6987  $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());';
6988  $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);';
6989  $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());';
6990  */
6991  $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
6992  $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
6993  $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
6994  $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
6995  }
6996  /*if ($usecalendar == "eldy")
6997  {
6998  $base=DOL_URL_ROOT.'/core/';
6999  $reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');';
7000  }
7001  else
7002  {
7003  $reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); ';
7004  $reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); ';
7005  $reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); ';
7006  }*/
7007  // Update the hour part
7008  if ($h) {
7009  if ($fullday) {
7010  $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
7011  }
7012  //$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); ';
7013  if ($addnowlink == 1) {
7014  $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
7015  $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
7016  } elseif ($addnowlink == 2) {
7017  $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(d.getHours().pad());';
7018  $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
7019  }
7020 
7021  if ($fullday) {
7022  $reset_scripts .= ' } ';
7023  }
7024  }
7025  // Update the minute part
7026  if ($m) {
7027  if ($fullday) {
7028  $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
7029  }
7030  //$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); ';
7031  if ($addnowlink == 1) {
7032  $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
7033  $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
7034  } elseif ($addnowlink == 2) {
7035  $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(d.getMinutes().pad());';
7036  $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
7037  }
7038  if ($fullday) {
7039  $reset_scripts .= ' } ';
7040  }
7041  }
7042  // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
7043  if ($reset_scripts && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
7044  $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonNow" type="button" name="_useless" value="now" onClick="' . $reset_scripts . '">';
7045  $retstring .= $langs->trans("Now");
7046  $retstring .= '</button> ';
7047  }
7048  }
7049 
7050  // Add a "Plus one hour" link
7051  if ($conf->use_javascript_ajax && $addplusone) {
7052  // Script which will be inserted in the onClick of the "Add plusone" link
7053  $reset_scripts = "";
7054 
7055  // Generate the date part, depending on the use or not of the javascript calendar
7056  $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'dayinputnoreduce', 'tzuserrel') . '\');';
7057  $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
7058  $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
7059  $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
7060  // Update the hour part
7061  if ($h) {
7062  if ($fullday) {
7063  $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
7064  }
7065  $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
7066  if ($fullday) {
7067  $reset_scripts .= ' } ';
7068  }
7069  }
7070  // Update the minute part
7071  if ($m) {
7072  if ($fullday) {
7073  $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
7074  }
7075  $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
7076  if ($fullday) {
7077  $reset_scripts .= ' } ';
7078  }
7079  }
7080  // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
7081  if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
7082  $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonPlusOne" type="button" name="_useless2" value="plusone" onClick="' . $reset_scripts . '">';
7083  $retstring .= $langs->trans("DateStartPlusOne");
7084  $retstring .= '</button> ';
7085  }
7086  }
7087 
7088  // Add a link to set data
7089  if ($conf->use_javascript_ajax && !empty($adddateof)) {
7090  if (!is_array($adddateof)) {
7091  $arrayofdateof = array(array('adddateof'=>$adddateof, 'labeladddateof'=>$labeladddateof));
7092  } else {
7093  $arrayofdateof = $adddateof;
7094  }
7095  foreach ($arrayofdateof as $valuedateof) {
7096  $tmpadddateof = $valuedateof['adddateof'];
7097  $tmplabeladddateof = $valuedateof['labeladddateof'];
7098  $tmparray = dol_getdate($tmpadddateof);
7099  if (empty($tmplabeladddateof)) {
7100  $tmplabeladddateof = $langs->trans("DateInvoice");
7101  }
7102  $reset_scripts = 'console.log(\'Click on now link\'); ';
7103  $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($tmpadddateof, 'dayinputnoreduce').'\');';
7104  $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.$tmparray['mday'].'\');';
7105  $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.$tmparray['mon'].'\');';
7106  $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.$tmparray['year'].'\');';
7107  $retstring .= ' - <button class="dpInvisibleButtons datenowlink" id="dateofinvoice" type="button" name="_dateofinvoice" value="now" onclick="'.$reset_scripts.'">'.$tmplabeladddateof.'</button>';
7108  }
7109  }
7110 
7111  return $retstring;
7112  }
7113 
7122  public function selectTypeDuration($prefix, $selected = 'i', $excludetypes = array())
7123  {
7124  global $langs;
7125 
7126  $TDurationTypes = array(
7127  'y' => $langs->trans('Years'),
7128  'm' => $langs->trans('Month'),
7129  'w' => $langs->trans('Weeks'),
7130  'd' => $langs->trans('Days'),
7131  'h' => $langs->trans('Hours'),
7132  'i' => $langs->trans('Minutes')
7133  );
7134 
7135  // Removed undesired duration types
7136  foreach ($excludetypes as $value) {
7137  unset($TDurationTypes[$value]);
7138  }
7139 
7140  $retstring = '<select class="flat minwidth75 maxwidth100" id="select_' . $prefix . 'type_duration" name="' . $prefix . 'type_duration">';
7141  foreach ($TDurationTypes as $key => $typeduration) {
7142  $retstring .= '<option value="' . $key . '"';
7143  if ($key == $selected) {
7144  $retstring .= " selected";
7145  }
7146  $retstring .= ">" . $typeduration . "</option>";
7147  }
7148  $retstring .= "</select>";
7149 
7150  $retstring .= ajax_combobox('select_' . $prefix . 'type_duration');
7151 
7152  return $retstring;
7153  }
7154 
7155  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7156 
7170  public function select_duration($prefix, $iSecond = '', $disabled = 0, $typehour = 'select', $minunderhours = 0, $nooutput = 0)
7171  {
7172  // phpcs:enable
7173  global $langs;
7174 
7175  $retstring = '<span class="nowraponall">';
7176 
7177  $hourSelected = '';
7178  $minSelected = '';
7179 
7180  // Hours
7181  if ($iSecond != '') {
7182  require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
7183 
7184  $hourSelected = convertSecondToTime($iSecond, 'allhour');
7185  $minSelected = convertSecondToTime($iSecond, 'min');
7186  }
7187 
7188  if ($typehour == 'select') {
7189  $retstring .= '<select class="flat" id="select_' . $prefix . 'hour" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . '>';
7190  for ($hour = 0; $hour < 25; $hour++) { // For a duration, we allow 24 hours
7191  $retstring .= '<option value="' . $hour . '"';
7192  if (is_numeric($hourSelected) && $hourSelected == $hour) {
7193  $retstring .= " selected";
7194  }
7195  $retstring .= ">" . $hour . "</option>";
7196  }
7197  $retstring .= "</select>";
7198  } elseif ($typehour == 'text' || $typehour == 'textselect') {
7199  $retstring .= '<input placeholder="' . $langs->trans('HourShort') . '" type="number" min="0" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputhour right" value="' . (($hourSelected != '') ? ((int) $hourSelected) : '') . '">';
7200  } else {
7201  return 'BadValueForParameterTypeHour';
7202  }
7203 
7204  if ($typehour != 'text') {
7205  $retstring .= ' ' . $langs->trans('HourShort');
7206  } else {
7207  $retstring .= '<span class="">:</span>';
7208  }
7209 
7210  // Minutes
7211  if ($minunderhours) {
7212  $retstring .= '<br>';
7213  } else {
7214  if ($typehour != 'text') {
7215  $retstring .= '<span class="hideonsmartphone">&nbsp;</span>';
7216  }
7217  }
7218 
7219  if ($typehour == 'select' || $typehour == 'textselect') {
7220  $retstring .= '<select class="flat" id="select_' . $prefix . 'min" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . '>';
7221  for ($min = 0; $min <= 55; $min = $min + 5) {
7222  $retstring .= '<option value="' . $min . '"';
7223  if (is_numeric($minSelected) && $minSelected == $min) {
7224  $retstring .= ' selected';
7225  }
7226  $retstring .= '>' . $min . '</option>';
7227  }
7228  $retstring .= "</select>";
7229  } elseif ($typehour == 'text') {
7230  $retstring .= '<input placeholder="' . $langs->trans('MinuteShort') . '" type="number" min="0" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputminute right" value="' . (($minSelected != '') ? ((int) $minSelected) : '') . '">';
7231  }
7232 
7233  if ($typehour != 'text') {
7234  $retstring .= ' ' . $langs->trans('MinuteShort');
7235  }
7236 
7237  $retstring .= "</span>";
7238 
7239  if (!empty($nooutput)) {
7240  return $retstring;
7241  }
7242 
7243  print $retstring;
7244 
7245  return '';
7246  }
7247 
7267  public function selectTickets($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0)
7268  {
7269  global $langs, $conf;
7270 
7271  $out = '';
7272 
7273  // check parameters
7274  if (is_null($ajaxoptions)) $ajaxoptions = array();
7275 
7276  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
7277  $placeholder = '';
7278 
7279  if ($selected && empty($selected_input_value)) {
7280  require_once DOL_DOCUMENT_ROOT . '/ticket/class/ticket.class.php';
7281  $tickettmpselect = new Ticket($this->db);
7282  $tickettmpselect->fetch($selected);
7283  $selected_input_value = $tickettmpselect->ref;
7284  unset($tickettmpselect);
7285  }
7286 
7287  $urloption = '';
7288  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/ticket/ajax/tickets.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
7289 
7290  if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel") . ' : ';
7291  elseif ($hidelabel > 1) {
7292  $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
7293  if ($hidelabel == 2) {
7294  $out .= img_picto($langs->trans("Search"), 'search');
7295  }
7296  }
7297  $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '') . ' />';
7298  if ($hidelabel == 3) {
7299  $out .= img_picto($langs->trans("Search"), 'search');
7300  }
7301  } else {
7302  $out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss);
7303  }
7304 
7305  if (empty($nooutput)) {
7306  print $out;
7307  } else {
7308  return $out;
7309  }
7310  return '';
7311  }
7312 
7313 
7330  public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
7331  {
7332  global $langs, $conf;
7333 
7334  $out = '';
7335  $outarray = array();
7336 
7337  $selectFields = " p.rowid, p.ref, p.message";
7338 
7339  $sql = "SELECT ";
7340  $sql .= $selectFields;
7341  $sql .= " FROM " . $this->db->prefix() . "ticket as p";
7342  $sql .= ' WHERE p.entity IN (' . getEntity('ticket') . ')';
7343 
7344  // Add criteria on ref/label
7345  if ($filterkey != '') {
7346  $sql .= ' AND (';
7347  $prefix = empty($conf->global->TICKET_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
7348  // For natural search
7349  $scrit = explode(' ', $filterkey);
7350  $i = 0;
7351  if (count($scrit) > 1) $sql .= "(";
7352  foreach ($scrit as $crit) {
7353  if ($i > 0) $sql .= " AND ";
7354  $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.subject LIKE '" . $this->db->escape($prefix . $crit) . "%'";
7355  $sql .= ")";
7356  $i++;
7357  }
7358  if (count($scrit) > 1) $sql .= ")";
7359  $sql .= ')';
7360  }
7361 
7362  $sql .= $this->db->plimit($limit, 0);
7363 
7364  // Build output string
7365  dol_syslog(get_class($this) . "::selectTicketsList search tickets", LOG_DEBUG);
7366  $result = $this->db->query($sql);
7367  if ($result) {
7368  require_once DOL_DOCUMENT_ROOT . '/ticket/class/ticket.class.php';
7369  require_once DOL_DOCUMENT_ROOT . '/core/lib/ticket.lib.php';
7370 
7371  $num = $this->db->num_rows($result);
7372 
7373  $events = null;
7374 
7375  if (!$forcecombo) {
7376  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7377  $out .= ajax_combobox($htmlname, $events, $conf->global->TICKET_USE_SEARCH_TO_SELECT);
7378  }
7379 
7380  $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
7381 
7382  $textifempty = '';
7383  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
7384  //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
7385  if (!empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
7386  if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
7387  else $textifempty .= $langs->trans("All");
7388  } else {
7389  if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
7390  }
7391  if ($showempty) $out .= '<option value="0" selected>' . $textifempty . '</option>';
7392 
7393  $i = 0;
7394  while ($num && $i < $num) {
7395  $opt = '';
7396  $optJson = array();
7397  $objp = $this->db->fetch_object($result);
7398 
7399  $this->constructTicketListOption($objp, $opt, $optJson, $selected, $filterkey);
7400  // Add new entry
7401  // "key" value of json key array is used by jQuery automatically as selected value
7402  // "label" value of json key array is used by jQuery automatically as text for combo box
7403  $out .= $opt;
7404  array_push($outarray, $optJson);
7405 
7406  $i++;
7407  }
7408 
7409  $out .= '</select>';
7410 
7411  $this->db->free($result);
7412 
7413  if (empty($outputmode)) {
7414  return $out;
7415  }
7416  return $outarray;
7417  } else {
7418  dol_print_error($this->db);
7419  }
7420 
7421  return array();
7422  }
7423 
7435  protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
7436  {
7437  $outkey = '';
7438  $outref = '';
7439  $outtype = '';
7440 
7441  $outkey = $objp->rowid;
7442  $outref = $objp->ref;
7443  $outtype = $objp->fk_product_type;
7444 
7445  $opt = '<option value="' . $objp->rowid . '"';
7446  $opt .= ($objp->rowid == $selected) ? ' selected' : '';
7447  $opt .= '>';
7448  $opt .= $objp->ref;
7449  $objRef = $objp->ref;
7450  if (!empty($filterkey) && $filterkey != '') $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
7451 
7452  $opt .= "</option>\n";
7453  $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
7454  }
7455 
7475  public function selectProjects($selected = '', $htmlname = 'projectid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0)
7476  {
7477  global $langs, $conf;
7478 
7479  $out = '';
7480 
7481  // check parameters
7482  if (is_null($ajaxoptions)) $ajaxoptions = array();
7483 
7484  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
7485  $placeholder = '';
7486 
7487  if ($selected && empty($selected_input_value)) {
7488  require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
7489  $projecttmpselect = new Project($this->db);
7490  $projecttmpselect->fetch($selected);
7491  $selected_input_value = $projecttmpselect->ref;
7492  unset($projecttmpselect);
7493  }
7494 
7495  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/projet/ajax/projects.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
7496 
7497  if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel") . ' : ';
7498  elseif ($hidelabel > 1) {
7499  $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
7500  if ($hidelabel == 2) {
7501  $out .= img_picto($langs->trans("Search"), 'search');
7502  }
7503  }
7504  $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '') . ' />';
7505  if ($hidelabel == 3) {
7506  $out .= img_picto($langs->trans("Search"), 'search');
7507  }
7508  } else {
7509  $out .= $this->selectProjectsList($selected, $htmlname, $filtertype, $limit, $status, 0, $socid, $showempty, $forcecombo, $morecss);
7510  }
7511 
7512  if (empty($nooutput)) {
7513  print $out;
7514  } else {
7515  return $out;
7516  }
7517  return '';
7518  }
7519 
7536  public function selectProjectsList($selected = '', $htmlname = 'projectid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
7537  {
7538  global $langs, $conf;
7539 
7540  $out = '';
7541  $outarray = array();
7542 
7543  $selectFields = " p.rowid, p.ref";
7544 
7545  $sql = "SELECT ";
7546  $sql .= $selectFields;
7547  $sql .= " FROM " . $this->db->prefix() . "projet as p";
7548  $sql .= ' WHERE p.entity IN (' . getEntity('project') . ')';
7549 
7550  // Add criteria on ref/label
7551  if ($filterkey != '') {
7552  $sql .= ' AND (';
7553  $prefix = empty($conf->global->TICKET_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
7554  // For natural search
7555  $scrit = explode(' ', $filterkey);
7556  $i = 0;
7557  if (count($scrit) > 1) $sql .= "(";
7558  foreach ($scrit as $crit) {
7559  if ($i > 0) $sql .= " AND ";
7560  $sql .= "p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%'";
7561  $sql .= "";
7562  $i++;
7563  }
7564  if (count($scrit) > 1) $sql .= ")";
7565  $sql .= ')';
7566  }
7567 
7568  $sql .= $this->db->plimit($limit, 0);
7569 
7570  // Build output string
7571  dol_syslog(get_class($this) . "::selectProjectsList search projects", LOG_DEBUG);
7572  $result = $this->db->query($sql);
7573  if ($result) {
7574  require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
7575  require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
7576 
7577  $num = $this->db->num_rows($result);
7578 
7579  $events = null;
7580 
7581  if (!$forcecombo) {
7582  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7583  $out .= ajax_combobox($htmlname, $events, $conf->global->PROJECT_USE_SEARCH_TO_SELECT);
7584  }
7585 
7586  $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
7587 
7588  $textifempty = '';
7589  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
7590  //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
7591  if (!empty($conf->global->PROJECT_USE_SEARCH_TO_SELECT)) {
7592  if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
7593  else $textifempty .= $langs->trans("All");
7594  } else {
7595  if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
7596  }
7597  if ($showempty) $out .= '<option value="0" selected>' . $textifempty . '</option>';
7598 
7599  $i = 0;
7600  while ($num && $i < $num) {
7601  $opt = '';
7602  $optJson = array();
7603  $objp = $this->db->fetch_object($result);
7604 
7605  $this->constructProjectListOption($objp, $opt, $optJson, $selected, $filterkey);
7606  // Add new entry
7607  // "key" value of json key array is used by jQuery automatically as selected value
7608  // "label" value of json key array is used by jQuery automatically as text for combo box
7609  $out .= $opt;
7610  array_push($outarray, $optJson);
7611 
7612  $i++;
7613  }
7614 
7615  $out .= '</select>';
7616 
7617  $this->db->free($result);
7618 
7619  if (empty($outputmode)) {
7620  return $out;
7621  }
7622  return $outarray;
7623  } else {
7624  dol_print_error($this->db);
7625  }
7626 
7627  return array();
7628  }
7629 
7641  protected function constructProjectListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
7642  {
7643  $outkey = '';
7644  $outref = '';
7645  $outtype = '';
7646 
7647  $label = $objp->label;
7648 
7649  $outkey = $objp->rowid;
7650  $outref = $objp->ref;
7651  $outlabel = $objp->label;
7652  $outtype = $objp->fk_product_type;
7653 
7654  $opt = '<option value="' . $objp->rowid . '"';
7655  $opt .= ($objp->rowid == $selected) ? ' selected' : '';
7656  $opt .= '>';
7657  $opt .= $objp->ref;
7658  $objRef = $objp->ref;
7659  if (!empty($filterkey) && $filterkey != '') $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
7660 
7661  $opt .= "</option>\n";
7662  $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
7663  }
7664 
7665 
7685  public function selectMembers($selected = '', $htmlname = 'adherentid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0)
7686  {
7687  global $langs, $conf;
7688 
7689  $out = '';
7690 
7691  // check parameters
7692  if (is_null($ajaxoptions)) $ajaxoptions = array();
7693 
7694  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->TICKET_USE_SEARCH_TO_SELECT)) {
7695  $placeholder = '';
7696  $urloption = '';
7697 
7698  if ($selected && empty($selected_input_value)) {
7699  require_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
7700  $adherenttmpselect = new Adherent($this->db);
7701  $adherenttmpselect->fetch($selected);
7702  $selected_input_value = $adherenttmpselect->ref;
7703  unset($adherenttmpselect);
7704  }
7705 
7706  $urloption = '';
7707 
7708  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/adherents/ajax/adherents.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
7709 
7710  if (empty($hidelabel)) $out .= $langs->trans("RefOrLabel") . ' : ';
7711  elseif ($hidelabel > 1) {
7712  $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
7713  if ($hidelabel == 2) {
7714  $out .= img_picto($langs->trans("Search"), 'search');
7715  }
7716  }
7717  $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '') . ' />';
7718  if ($hidelabel == 3) {
7719  $out .= img_picto($langs->trans("Search"), 'search');
7720  }
7721  } else {
7722  $filterkey = '';
7723 
7724  $out .= $this->selectMembersList($selected, $htmlname, $filtertype, $limit, $filterkey, $status, 0, $showempty, $forcecombo, $morecss);
7725  }
7726 
7727  if (empty($nooutput)) {
7728  print $out;
7729  } else {
7730  return $out;
7731  }
7732  return '';
7733  }
7734 
7751  public function selectMembersList($selected = '', $htmlname = 'adherentid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
7752  {
7753  global $langs, $conf;
7754 
7755  $out = '';
7756  $outarray = array();
7757 
7758  $selectFields = " p.rowid, p.ref, p.firstname, p.lastname, p.fk_adherent_type";
7759 
7760  $sql = "SELECT ";
7761  $sql .= $selectFields;
7762  $sql .= " FROM " . $this->db->prefix() . "adherent as p";
7763  $sql .= ' WHERE p.entity IN (' . getEntity('adherent') . ')';
7764 
7765  // Add criteria on ref/label
7766  if ($filterkey != '') {
7767  $sql .= ' AND (';
7768  $prefix = empty($conf->global->MEMBER_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
7769  // For natural search
7770  $scrit = explode(' ', $filterkey);
7771  $i = 0;
7772  if (count($scrit) > 1) $sql .= "(";
7773  foreach ($scrit as $crit) {
7774  if ($i > 0) $sql .= " AND ";
7775  $sql .= "(p.firstname LIKE '" . $this->db->escape($prefix . $crit) . "%'";
7776  $sql .= " OR p.lastname LIKE '" . $this->db->escape($prefix . $crit) . "%')";
7777  $i++;
7778  }
7779  if (count($scrit) > 1) $sql .= ")";
7780  $sql .= ')';
7781  }
7782  if ($status != -1) {
7783  $sql .= ' AND statut = ' . ((int) $status);
7784  }
7785  $sql .= $this->db->plimit($limit, 0);
7786 
7787  // Build output string
7788  dol_syslog(get_class($this) . "::selectMembersList search adherents", LOG_DEBUG);
7789  $result = $this->db->query($sql);
7790  if ($result) {
7791  require_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
7792  require_once DOL_DOCUMENT_ROOT . '/core/lib/member.lib.php';
7793 
7794  $num = $this->db->num_rows($result);
7795 
7796  $events = null;
7797 
7798  if (!$forcecombo) {
7799  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7800  $out .= ajax_combobox($htmlname, $events, !empty($conf->global->PROJECT_USE_SEARCH_TO_SELECT) ? $conf->global->PROJECT_USE_SEARCH_TO_SELECT : '');
7801  }
7802 
7803  $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
7804 
7805  $textifempty = '';
7806  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
7807  //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
7808  if (!empty($conf->global->PROJECT_USE_SEARCH_TO_SELECT)) {
7809  if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
7810  else $textifempty .= $langs->trans("All");
7811  } else {
7812  if ($showempty && !is_numeric($showempty)) $textifempty = $langs->trans($showempty);
7813  }
7814  if ($showempty) {
7815  $out .= '<option value="-1" selected>' . $textifempty . '</option>';
7816  }
7817 
7818  $i = 0;
7819  while ($num && $i < $num) {
7820  $opt = '';
7821  $optJson = array();
7822  $objp = $this->db->fetch_object($result);
7823 
7824  $this->constructMemberListOption($objp, $opt, $optJson, $selected, $filterkey);
7825 
7826  // Add new entry
7827  // "key" value of json key array is used by jQuery automatically as selected value
7828  // "label" value of json key array is used by jQuery automatically as text for combo box
7829  $out .= $opt;
7830  array_push($outarray, $optJson);
7831 
7832  $i++;
7833  }
7834 
7835  $out .= '</select>';
7836 
7837  $this->db->free($result);
7838 
7839  if (empty($outputmode)) {
7840  return $out;
7841  }
7842  return $outarray;
7843  } else {
7844  dol_print_error($this->db);
7845  }
7846 
7847  return array();
7848  }
7849 
7861  protected function constructMemberListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
7862  {
7863  $outkey = '';
7864  $outlabel = '';
7865  $outtype = '';
7866 
7867  $outkey = $objp->rowid;
7868  $outlabel = dolGetFirstLastname($objp->firstname, $objp->lastname);
7869  $outtype = $objp->fk_adherent_type;
7870 
7871  $opt = '<option value="' . $objp->rowid . '"';
7872  $opt .= ($objp->rowid == $selected) ? ' selected' : '';
7873  $opt .= '>';
7874  if (!empty($filterkey) && $filterkey != '') {
7875  $outlabel = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $outlabel, 1);
7876  }
7877  $opt .= $outlabel;
7878  $opt .= "</option>\n";
7879 
7880  $optJson = array('key' => $outkey, 'value' => $outlabel, 'type' => $outtype);
7881  }
7882 
7902  public function selectForForms($objectdesc, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $disabled = 0, $selected_input_value = '')
7903  {
7904  global $conf, $user;
7905 
7906  $objecttmp = null;
7907 
7908  // Example of value for $objectdec:
7909  // Bom:bom/class/bom.class.php:0:t.status=1
7910  // Bom:bom/class/bom.class.php:0:t.status=1:ref
7911  // Bom:bom/class/bom.class.php:0:(t.status:=:1):ref
7912  $InfoFieldList = explode(":", $objectdesc, 4);
7913  $vartmp = (empty($InfoFieldList[3]) ? '' : $InfoFieldList[3]);
7914  $reg = array();
7915  if (preg_match('/^.*:(\w*)$/', $vartmp, $reg)) {
7916  $InfoFieldList[4] = $reg[1]; // take the sort field
7917  }
7918  $InfoFieldList[3] = preg_replace('/:\w*$/', '', $vartmp); // take the filter field
7919 
7920  $classname = $InfoFieldList[0];
7921  $classpath = $InfoFieldList[1];
7922  $addcreatebuttonornot = empty($InfoFieldList[2]) ? 0 : $InfoFieldList[2];
7923  $filter = empty($InfoFieldList[3]) ? '' : $InfoFieldList[3];
7924  $sortfield = empty($InfoFieldList[4]) ? '' : $InfoFieldList[4];
7925 
7926  if (!empty($classpath)) {
7927  dol_include_once($classpath);
7928 
7929  if ($classname && class_exists($classname)) {
7930  $objecttmp = new $classname($this->db);
7931 
7932  // Make some replacement
7933  $sharedentities = getEntity(strtolower($classname));
7934  $filter = str_replace(
7935  array('__ENTITY__', '__SHARED_ENTITIES__', '__USER_ID__'),
7936  array($conf->entity, $sharedentities, $user->id),
7937  $filter
7938  );
7939  }
7940  }
7941  if (!is_object($objecttmp)) {
7942  dol_syslog('Error bad setup of type for field ' . join(',', $InfoFieldList), LOG_WARNING);
7943  return 'Error bad setup of type for field ' . join(',', $InfoFieldList);
7944  }
7945 
7946  //var_dump($filter);
7947  $prefixforautocompletemode = $objecttmp->element;
7948  if ($prefixforautocompletemode == 'societe') {
7949  $prefixforautocompletemode = 'company';
7950  }
7951  if ($prefixforautocompletemode == 'product') {
7952  $prefixforautocompletemode = 'produit';
7953  }
7954  $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
7955 
7956  dol_syslog(get_class($this) . "::selectForForms filter=" . $filter, LOG_DEBUG);
7957  $out = '';
7958  if (!empty($conf->use_javascript_ajax) && getDolGlobalString($confkeyforautocompletemode) && !$forcecombo) {
7959  // No immediate load of all database
7960  $placeholder = '';
7961  if ($preselectedvalue && empty($selected_input_value)) {
7962  $objecttmp->fetch($preselectedvalue);
7963  $selected_input_value = ($prefixforautocompletemode == 'company' ? $objecttmp->name : $objecttmp->ref);
7964  //unset($objecttmp);
7965  }
7966 
7967  $objectdesc = $classname . ':' . $classpath . ':' . $addcreatebuttonornot . ':' . $filter;
7968  $urlforajaxcall = DOL_URL_ROOT . '/core/ajax/selectobject.php';
7969 
7970  // No immediate load of all database
7971  $urloption = 'htmlname=' . urlencode($htmlname) . '&outjson=1&objectdesc=' . urlencode($objectdesc) . '&filter=' . urlencode($filter) . ($sortfield ? '&sortfield=' . urlencode($sortfield) : '');
7972  // Activate the auto complete using ajax call.
7973  $out .= ajax_autocompleter($preselectedvalue, $htmlname, $urlforajaxcall, $urloption, $conf->global->$confkeyforautocompletemode, 0, array());
7974  $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
7975  $out .= '<input type="text" class="' . $morecss . '"' . ($disabled ? ' disabled="disabled"' : '') . ' name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '') . ' />';
7976  } else {
7977  // Immediate load of table record.
7978  $out .= $this->selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled, $sortfield, $filter);
7979  }
7980 
7981  return $out;
7982  }
7983 
7984 
8005  public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0, $sortfield = '', $filter = '')
8006  {
8007  global $conf, $langs, $user, $hookmanager;
8008 
8009  //print "$htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, $outputmode, $disabled";
8010 
8011  $prefixforautocompletemode = $objecttmp->element;
8012  if ($prefixforautocompletemode == 'societe') {
8013  $prefixforautocompletemode = 'company';
8014  }
8015  $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
8016 
8017  if (!empty($objecttmp->fields)) { // For object that declare it, it is better to use declared fields (like societe, contact, ...)
8018  $tmpfieldstoshow = '';
8019  foreach ($objecttmp->fields as $key => $val) {
8020  if (!dol_eval($val['enabled'], 1, 1, '1')) {
8021  continue;
8022  }
8023  if (!empty($val['showoncombobox'])) {
8024  $tmpfieldstoshow .= ($tmpfieldstoshow ? ',' : '') . 't.' . $key;
8025  }
8026  }
8027  if ($tmpfieldstoshow) {
8028  $fieldstoshow = $tmpfieldstoshow;
8029  }
8030  } else {
8031  // For backward compatibility
8032  $objecttmp->fields['ref'] = array('type' => 'varchar(30)', 'label' => 'Ref', 'showoncombobox' => 1);
8033  }
8034 
8035  if (empty($fieldstoshow)) {
8036  if (isset($objecttmp->fields['ref'])) {
8037  $fieldstoshow = 't.ref';
8038  } else {
8039  $langs->load("errors");
8040  $this->error = $langs->trans("ErrorNoFieldWithAttributeShowoncombobox");
8041  return $langs->trans('ErrorNoFieldWithAttributeShowoncombobox');
8042  }
8043  }
8044 
8045  $out = '';
8046  $outarray = array();
8047  $tmparray = array();
8048 
8049  $num = 0;
8050 
8051  // Search data
8052  $sql = "SELECT t.rowid, " . $fieldstoshow . " FROM " . $this->db->prefix() . $objecttmp->table_element . " as t";
8053  if (isset($objecttmp->ismultientitymanaged)) {
8054  if (!is_numeric($objecttmp->ismultientitymanaged)) {
8055  $tmparray = explode('@', $objecttmp->ismultientitymanaged);
8056  $sql .= " INNER JOIN " . $this->db->prefix() . $tmparray[1] . " as parenttable ON parenttable.rowid = t." . $tmparray[0];
8057  }
8058  if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
8059  if (empty($user->rights->societe->client->voir) && !$user->socid) {
8060  $sql .= ", " . $this->db->prefix() . "societe_commerciaux as sc";
8061  }
8062  }
8063  }
8064 
8065  // Add where from hooks
8066  $parameters = array(
8067  'object' => $objecttmp,
8068  'htmlname' => $htmlname,
8069  'filter' => $filter,
8070  'searchkey' => $searchkey
8071  );
8072 
8073  $reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook
8074  if (!empty($hookmanager->resPrint)) {
8075  $sql .= $hookmanager->resPrint;
8076  } else {
8077  $sql .= " WHERE 1=1";
8078  if (isset($objecttmp->ismultientitymanaged)) {
8079  if ($objecttmp->ismultientitymanaged == 1) {
8080  $sql .= " AND t.entity IN (" . getEntity($objecttmp->table_element) . ")";
8081  }
8082  if (!is_numeric($objecttmp->ismultientitymanaged)) {
8083  $sql .= " AND parenttable.entity = t." . $tmparray[0];
8084  }
8085  if ($objecttmp->ismultientitymanaged == 1 && !empty($user->socid)) {
8086  if ($objecttmp->element == 'societe') {
8087  $sql .= " AND t.rowid = " . ((int) $user->socid);
8088  } else {
8089  $sql .= " AND t.fk_soc = " . ((int) $user->socid);
8090  }
8091  }
8092  if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
8093  if (empty($user->rights->societe->client->voir) && !$user->socid) {
8094  $sql .= " AND t.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id);
8095  }
8096  }
8097  }
8098  if ($searchkey != '') {
8099  $sql .= natural_search(explode(',', $fieldstoshow), $searchkey);
8100  }
8101 
8102  if ($filter) { // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
8103  $errormessage = '';
8104  $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
8105  if ($errormessage) {
8106  return 'Error forging a SQL request from an universal criteria: ' . $errormessage;
8107  }
8108  }
8109  }
8110  $sql .= $this->db->order($sortfield ? $sortfield : $fieldstoshow, "ASC");
8111  //$sql.=$this->db->plimit($limit, 0);
8112  //print $sql;
8113 
8114  // Build output string
8115  $resql = $this->db->query($sql);
8116  if ($resql) {
8117  // Construct $out and $outarray
8118  $out .= '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ($moreparams ? ' ' . $moreparams : '') . ' name="' . $htmlname . '">' . "\n";
8119 
8120  // Warning: Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'. Seems it is no more true with selec2 v4
8121  $textifempty = '&nbsp;';
8122 
8123  //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
8124  if (!empty($conf->global->$confkeyforautocompletemode)) {
8125  if ($showempty && !is_numeric($showempty)) {
8126  $textifempty = $langs->trans($showempty);
8127  } else {
8128  $textifempty .= $langs->trans("All");
8129  }
8130  }
8131  if ($showempty) {
8132  $out .= '<option value="-1">' . $textifempty . '</option>' . "\n";
8133  }
8134 
8135  $num = $this->db->num_rows($resql);
8136  $i = 0;
8137  if ($num) {
8138  while ($i < $num) {
8139  $obj = $this->db->fetch_object($resql);
8140  $label = '';
8141  $labelhtml = '';
8142  $tmparray = explode(',', $fieldstoshow);
8143  $oldvalueforshowoncombobox = 0;
8144  foreach ($tmparray as $key => $val) {
8145  $val = preg_replace('/t\./', '', $val);
8146  $label .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
8147  $labelhtml .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
8148  $label .= $obj->$val;
8149  $labelhtml .= $obj->$val;
8150 
8151  $oldvalueforshowoncombobox = empty($objecttmp->fields[$val]['showoncombobox']) ? 0 : $objecttmp->fields[$val]['showoncombobox'];
8152  }
8153  if (empty($outputmode)) {
8154  if ($preselectedvalue > 0 && $preselectedvalue == $obj->rowid) {
8155  $out .= '<option value="' . $obj->rowid . '" selected data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
8156  } else {
8157  $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
8158  }
8159  } else {
8160  array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label));
8161  }
8162 
8163  $i++;
8164  if (($i % 10) == 0) {
8165  $out .= "\n";
8166  }
8167  }
8168  }
8169 
8170  $out .= '</select>' . "\n";
8171 
8172  if (!$forcecombo) {
8173  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
8174  $out .= ajax_combobox($htmlname, null, (!empty($conf->global->$confkeyforautocompletemode) ? $conf->global->$confkeyforautocompletemode : 0));
8175  }
8176  } else {
8177  dol_print_error($this->db);
8178  }
8179 
8180  $this->result = array('nbofelement' => $num);
8181 
8182  if ($outputmode) {
8183  return $outarray;
8184  }
8185  return $out;
8186  }
8187 
8188 
8212  public static function selectarray($htmlname, $array, $id = '', $show_empty = 0, $key_in_label = 0, $value_as_key = 0, $moreparam = '', $translate = 0, $maxlen = 0, $disabled = 0, $sort = '', $morecss = 'minwidth75', $addjscombo = 1, $moreparamonempty = '', $disablebademail = 0, $nohtmlescape = 0)
8213  {
8214  global $conf, $langs;
8215 
8216  // Do we want a multiselect ?
8217  //$jsbeautify = 0;
8218  //if (preg_match('/^multi/',$htmlname)) $jsbeautify = 1;
8219  $jsbeautify = 1;
8220 
8221  if ($value_as_key) {
8222  $array = array_combine($array, $array);
8223  }
8224 
8225  $out = '';
8226 
8227  if ($addjscombo < 0) {
8228  if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
8229  $addjscombo = 1;
8230  } else {
8231  $addjscombo = 0;
8232  }
8233  }
8234 
8235  $idname = str_replace(array('[', ']'), array('', ''), $htmlname);
8236  $out .= '<select id="' . preg_replace('/^\./', '', $idname) . '" ' . ($disabled ? 'disabled="disabled" ' : '') . 'class="flat ' . (preg_replace('/^\./', '', $htmlname)) . ($morecss ? ' ' . $morecss : '') . '"';
8237  $out .= ' name="' . preg_replace('/^\./', '', $htmlname) . '" ' . ($moreparam ? $moreparam : '');
8238  $out .= '>';
8239 
8240  if ($show_empty) {
8241  $textforempty = ' ';
8242  if (!empty($conf->use_javascript_ajax)) {
8243  $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
8244  }
8245  if (!is_numeric($show_empty)) {
8246  $textforempty = $show_empty;
8247  }
8248  $out .= '<option class="optiongrey" ' . ($moreparamonempty ? $moreparamonempty . ' ' : '') . 'value="' . ($show_empty < 0 ? $show_empty : -1) . '"' . ($id == $show_empty ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
8249  }
8250 
8251  if (is_array($array)) {
8252  // Translate
8253  if ($translate) {
8254  foreach ($array as $key => $value) {
8255  if (!is_array($value)) {
8256  $array[$key] = $langs->trans($value);
8257  } else {
8258  $array[$key]['label'] = $langs->trans($value['label']);
8259  }
8260  }
8261  }
8262 
8263  // Sort
8264  if ($sort == 'ASC') {
8265  asort($array);
8266  } elseif ($sort == 'DESC') {
8267  arsort($array);
8268  }
8269 
8270  foreach ($array as $key => $tmpvalue) {
8271  if (is_array($tmpvalue)) {
8272  $value = $tmpvalue['label'];
8273  $disabled = empty($tmpvalue['disabled']) ? '' : ' disabled';
8274  $style = empty($tmpvalue['css']) ? '' : ' class="' . $tmpvalue['css'] . '"';
8275  } else {
8276  $value = $tmpvalue;
8277  $disabled = '';
8278  $style = '';
8279  }
8280  if (!empty($disablebademail)) {
8281  if (($disablebademail == 1 && !preg_match('/&lt;.+@.+&gt;/', $value))
8282  || ($disablebademail == 2 && preg_match('/---/', $value))) {
8283  $disabled = ' disabled';
8284  $style = ' class="warning"';
8285  }
8286  }
8287 
8288  if ($key_in_label) {
8289  if (empty($nohtmlescape)) {
8290  $selectOptionValue = dol_escape_htmltag($key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value));
8291  } else {
8292  $selectOptionValue = $key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value);
8293  }
8294  } else {
8295  if (empty($nohtmlescape)) {
8296  $selectOptionValue = dol_escape_htmltag($maxlen ? dol_trunc($value, $maxlen) : $value);
8297  } else {
8298  $selectOptionValue = $maxlen ? dol_trunc($value, $maxlen) : $value;
8299  }
8300  if ($value == '' || $value == '-') {
8301  $selectOptionValue = '&nbsp;';
8302  }
8303  }
8304 
8305  $out .= '<option value="' . $key . '"';
8306  $out .= $style . $disabled;
8307  if (is_array($id)) {
8308  if (in_array($key, $id) && !$disabled) {
8309  $out .= ' selected'; // To preselect a value
8310  }
8311  } else {
8312  $id = (string) $id; // if $id = 0, then $id = '0'
8313  if ($id != '' && ($id == $key || ($id == 'ifone' && count($array) == 1)) && !$disabled) {
8314  $out .= ' selected'; // To preselect a value
8315  }
8316  }
8317  if ($nohtmlescape) {
8318  $out .= ' data-html="' . dol_escape_htmltag($selectOptionValue) . '"';
8319  }
8320  if (is_array($tmpvalue)) {
8321  foreach ($tmpvalue as $keyforvalue => $valueforvalue) {
8322  if (preg_match('/^data-/', $keyforvalue)) {
8323  $out .= ' '.$keyforvalue.'="'.dol_escape_htmltag($valueforvalue).'"';
8324  }
8325  }
8326  }
8327  $out .= '>';
8328  //var_dump($selectOptionValue);
8329  $out .= $selectOptionValue;
8330  $out .= "</option>\n";
8331  }
8332  }
8333 
8334  $out .= "</select>";
8335 
8336  // Add code for jquery to use multiselect
8337  if ($addjscombo && $jsbeautify) {
8338  // Enhance with select2
8339  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
8340  $out .= ajax_combobox($idname, array(), 0, 0, 'resolve', ($show_empty < 0 ? (string) $show_empty : '-1'), $morecss);
8341  }
8342 
8343  return $out;
8344  }
8345 
8346 
8365  public static function selectArrayAjax($htmlname, $url, $id = '', $moreparam = '', $moreparamtourl = '', $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
8366  {
8367  global $conf, $langs;
8368  global $delayedhtmlcontent; // Will be used later outside of this function
8369 
8370  // TODO Use an internal dolibarr component instead of select2
8371  if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) {
8372  return '';
8373  }
8374 
8375  $out = '<select type="text" class="' . $htmlname . ($morecss ? ' ' . $morecss : '') . '" ' . ($moreparam ? $moreparam . ' ' : '') . 'name="' . $htmlname . '"></select>';
8376 
8377  $outdelayed = '';
8378  if (!empty($conf->use_javascript_ajax)) {
8379  $tmpplugin = 'select2';
8380  $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
8381  <script nonce="' . getNonce() . '">
8382  $(document).ready(function () {
8383 
8384  ' . ($callurlonselect ? 'var saveRemoteData = [];' : '') . '
8385 
8386  $(".' . $htmlname . '").select2({
8387  ajax: {
8388  dir: "ltr",
8389  url: "' . $url . '",
8390  dataType: \'json\',
8391  delay: 250,
8392  data: function (params) {
8393  return {
8394  q: params.term, // search term
8395  page: params.page
8396  }
8397  },
8398  processResults: function (data) {
8399  // parse the results into the format expected by Select2.
8400  // since we are using custom formatting functions we do not need to alter the remote JSON data
8401  //console.log(data);
8402  saveRemoteData = data;
8403  /* format json result for select2 */
8404  result = []
8405  $.each( data, function( key, value ) {
8406  result.push({id: key, text: value.text});
8407  });
8408  //return {results:[{id:\'none\', text:\'aa\'}, {id:\'rrr\', text:\'Red\'},{id:\'bbb\', text:\'Search a into projects\'}], more:false}
8409  //console.log(result);
8410  return {results: result, more: false}
8411  },
8412  cache: true
8413  },
8414  language: select2arrayoflanguage,
8415  containerCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
8416  placeholder: "' . dol_escape_js($placeholder) . '",
8417  escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
8418  minimumInputLength: ' . ((int) $minimumInputLength) . ',
8419  formatResult: function (result, container, query, escapeMarkup) {
8420  return escapeMarkup(result.text);
8421  },
8422  });
8423 
8424  ' . ($callurlonselect ? '
8425  /* Code to execute a GET when we select a value */
8426  $(".' . $htmlname . '").change(function() {
8427  var selected = $(".' . $htmlname . '").val();
8428  console.log("We select in selectArrayAjax the entry "+selected)
8429  $(".' . $htmlname . '").val(""); /* reset visible combo value */
8430  $.each( saveRemoteData, function( key, value ) {
8431  if (key == selected)
8432  {
8433  console.log("selectArrayAjax - Do a redirect to "+value.url)
8434  location.assign(value.url);
8435  }
8436  });
8437  });' : '') . '
8438 
8439  });
8440  </script>';
8441  }
8442 
8443  if ($acceptdelayedhtml) {
8444  $delayedhtmlcontent .= $outdelayed;
8445  } else {
8446  $out .= $outdelayed;
8447  }
8448  return $out;
8449  }
8450 
8470  public static function selectArrayFilter($htmlname, $array, $id = '', $moreparam = '', $disableFiltering = 0, $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0, $textfortitle = '')
8471  {
8472  global $conf, $langs;
8473  global $delayedhtmlcontent; // Will be used later outside of this function
8474 
8475  // TODO Use an internal dolibarr component instead of select2
8476  if (empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) && !defined('REQUIRE_JQUERY_MULTISELECT')) {
8477  return '';
8478  }
8479 
8480  $out = '<select type="text"'.($textfortitle? ' title="'.dol_escape_htmltag($textfortitle).'"' : '').' id="'.$htmlname.'" class="'.$htmlname.($morecss ? ' ' . $morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.'"><option></option></select>';
8481 
8482  $formattedarrayresult = array();
8483 
8484  foreach ($array as $key => $value) {
8485  $o = new stdClass();
8486  $o->id = $key;
8487  $o->text = $value['text'];
8488  $o->url = $value['url'];
8489  $formattedarrayresult[] = $o;
8490  }
8491 
8492  $outdelayed = '';
8493  if (!empty($conf->use_javascript_ajax)) {
8494  $tmpplugin = 'select2';
8495  $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
8496  <script nonce="' . getNonce() . '">
8497  $(document).ready(function () {
8498  var data = ' . json_encode($formattedarrayresult) . ';
8499 
8500  ' . ($callurlonselect ? 'var saveRemoteData = ' . json_encode($array) . ';' : '') . '
8501 
8502  $(".' . $htmlname . '").select2({
8503  data: data,
8504  language: select2arrayoflanguage,
8505  containerCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag */
8506  placeholder: "' . dol_escape_js($placeholder) . '",
8507  escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
8508  minimumInputLength: ' . $minimumInputLength . ',
8509  formatResult: function (result, container, query, escapeMarkup) {
8510  return escapeMarkup(result.text);
8511  },
8512  matcher: function (params, data) {
8513 
8514  if(! data.id) return null;';
8515 
8516  if ($callurlonselect) {
8517  // We forge the url with 'sall='
8518  $outdelayed .= '
8519 
8520  var urlBase = data.url;
8521  var separ = urlBase.indexOf("?") >= 0 ? "&" : "?";
8522  /* console.log("params.term="+params.term); */
8523  /* console.log("params.term encoded="+encodeURIComponent(params.term)); */
8524  saveRemoteData[data.id].url = urlBase + separ + "search_all=" + encodeURIComponent(params.term.replace(/\"/g, ""));';
8525  }
8526 
8527  if (!$disableFiltering) {
8528  $outdelayed .= '
8529 
8530  if(data.text.match(new RegExp(params.term))) {
8531  return data;
8532  }
8533 
8534  return null;';
8535  } else {
8536  $outdelayed .= '
8537 
8538  return data;';
8539  }
8540 
8541  $outdelayed .= '
8542  }
8543  });
8544 
8545  ' . ($callurlonselect ? '
8546  /* Code to execute a GET when we select a value */
8547  $(".' . $htmlname . '").change(function() {
8548  var selected = $(".' . $htmlname . '").val();
8549  console.log("We select "+selected)
8550 
8551  $(".' . $htmlname . '").val(""); /* reset visible combo value */
8552  $.each( saveRemoteData, function( key, value ) {
8553  if (key == selected)
8554  {
8555  console.log("selectArrayFilter - Do a redirect to "+value.url)
8556  location.assign(value.url);
8557  }
8558  });
8559  });' : '') . '
8560 
8561  });
8562  </script>';
8563  }
8564 
8565  if ($acceptdelayedhtml) {
8566  $delayedhtmlcontent .= $outdelayed;
8567  } else {
8568  $out .= $outdelayed;
8569  }
8570  return $out;
8571  }
8572 
8591  public static function multiselectarray($htmlname, $array, $selected = array(), $key_in_label = 0, $value_as_key = 0, $morecss = '', $translate = 0, $width = 0, $moreattrib = '', $elemtype = '', $placeholder = '', $addjscombo = -1)
8592  {
8593  global $conf, $langs;
8594 
8595  $out = '';
8596 
8597  if ($addjscombo < 0) {
8598  if (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
8599  $addjscombo = 1;
8600  } else {
8601  $addjscombo = 0;
8602  }
8603  }
8604 
8605  $useenhancedmultiselect = 0;
8606  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT')) {
8607  $useenhancedmultiselect = 1;
8608  }
8609 
8610  // Output select component
8611  $out .= '<select id="' . $htmlname . '" class="multiselect' . ($useenhancedmultiselect ? ' multiselectononeline' : '') . ($morecss ? ' ' . $morecss : '') . '" multiple name="' . $htmlname . '[]"' . ($moreattrib ? ' ' . $moreattrib : '') . ($width ? ' style="width: ' . (preg_match('/%/', $width) ? $width : $width . 'px') . '"' : '') . '>' . "\n";
8612  if (is_array($array) && !empty($array)) {
8613  if ($value_as_key) {
8614  $array = array_combine($array, $array);
8615  }
8616 
8617  if (!empty($array)) {
8618  foreach ($array as $key => $value) {
8619  $tmpkey = $key;
8620  $tmpvalue = $value;
8621  $tmpcolor = '';
8622  $tmppicto = '';
8623  $tmplabelhtml = '';
8624  if (is_array($value) && array_key_exists('id', $value) && array_key_exists('label', $value)) {
8625  $tmpkey = $value['id'];
8626  $tmpvalue = $value['label'];
8627  $tmpcolor = $value['color'];
8628  $tmppicto = $value['picto'];
8629  $tmplabelhtml = !empty($value['labelhtml']) ? $value['labelhtml'] : '';
8630  }
8631  $newval = ($translate ? $langs->trans($tmpvalue) : $tmpvalue);
8632  $newval = ($key_in_label ? $tmpkey . ' - ' . $newval : $newval);
8633 
8634  $out .= '<option value="' . $tmpkey . '"';
8635  if (is_array($selected) && !empty($selected) && in_array((string) $tmpkey, $selected) && ((string) $tmpkey != '')) {
8636  $out .= ' selected';
8637  }
8638  if (!empty($tmplabelhtml)) {
8639  $out .= ' data-html="' . dol_escape_htmltag($tmplabelhtml, 0, 0, '', 0, 1) . '"';
8640  } else {
8641  $tmplabelhtml = ($tmppicto ? img_picto('', $tmppicto, 'class="pictofixedwidth" style="color: #' . $tmpcolor . '"') : '') . $newval;
8642  $out .= ' data-html="' . dol_escape_htmltag($tmplabelhtml, 0, 0, '', 0, 1) . '"';
8643  }
8644  $out .= '>';
8645  $out .= dol_htmlentitiesbr($newval);
8646  $out .= '</option>' . "\n";
8647  }
8648  }
8649  }
8650  $out .= '</select>' . "\n";
8651 
8652  // Add code for jquery to use multiselect
8653  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) || defined('REQUIRE_JQUERY_MULTISELECT')) {
8654  $out .= "\n" . '<!-- JS CODE TO ENABLE select for id ' . $htmlname . ', addjscombo=' . $addjscombo . ' -->';
8655  $out .= "\n" . '<script nonce="' . getNonce() . '">' . "\n";
8656  if ($addjscombo == 1) {
8657  $tmpplugin = empty($conf->global->MAIN_USE_JQUERY_MULTISELECT) ? constant('REQUIRE_JQUERY_MULTISELECT') : $conf->global->MAIN_USE_JQUERY_MULTISELECT;
8658  $out .= 'function formatResult(record, container) {' . "\n";
8659  // If property html set, we decode html entities and use this.
8660  // Note that HTML content must have been sanitized from js with dol_escape_htmltag(xxx, 0, 0, '', 0, 1) when building the select option.
8661  $out .= ' if ($(record.element).attr("data-html") != undefined) { return htmlEntityDecodeJs($(record.element).attr("data-html")); }'."\n";
8662  $out .= ' return record.text;';
8663  $out .= '}' . "\n";
8664  $out .= 'function formatSelection(record) {' . "\n";
8665  if ($elemtype == 'category') {
8666  $out .= 'return \'<span><img src="' . DOL_URL_ROOT . '/theme/eldy/img/object_category.png"> \'+record.text+\'</span>\';';
8667  } else {
8668  $out .= 'return record.text;';
8669  }
8670  $out .= '}' . "\n";
8671  $out .= '$(document).ready(function () {
8672  $(\'#' . $htmlname . '\').' . $tmpplugin . '({';
8673  if ($placeholder) {
8674  $out .= '
8675  placeholder: {
8676  id: \'-1\',
8677  text: \'' . dol_escape_js($placeholder) . '\'
8678  },';
8679  }
8680  $out .= ' dir: \'ltr\',
8681  containerCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag (ko with multiselect) */
8682  dropdownCssClass: \'' . $morecss . '\', /* Line to add class on the new <span class="select2-selection...> tag (ok with multiselect) */
8683  // Specify format function for dropdown item
8684  formatResult: formatResult,
8685  templateResult: formatResult, /* For 4.0 */
8686  escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
8687  // Specify format function for selected item
8688  formatSelection: formatSelection,
8689  templateSelection: formatSelection /* For 4.0 */
8690  });
8691 
8692  /* Add also morecss to the css .select2 that is after the #htmlname, for component that are show dynamically after load, because select2 set
8693  the size only if component is not hidden by default on load */
8694  $(\'#' . $htmlname . ' + .select2\').addClass(\'' . $morecss . '\');
8695  });' . "\n";
8696  } elseif ($addjscombo == 2 && !defined('DISABLE_MULTISELECT')) {
8697  // Add other js lib
8698  // TODO external lib multiselect/jquery.multi-select.js must have been loaded to use this multiselect plugin
8699  // ...
8700  $out .= 'console.log(\'addjscombo=2 for htmlname=' . $htmlname . '\');';
8701  $out .= '$(document).ready(function () {
8702  $(\'#' . $htmlname . '\').multiSelect({
8703  containerHTML: \'<div class="multi-select-container">\',
8704  menuHTML: \'<div class="multi-select-menu">\',
8705  buttonHTML: \'<span class="multi-select-button ' . $morecss . '">\',
8706  menuItemHTML: \'<label class="multi-select-menuitem">\',
8707  activeClass: \'multi-select-container--open\',
8708  noneText: \'' . $placeholder . '\'
8709  });
8710  })';
8711  }
8712  $out .= '</script>';
8713  }
8714 
8715  return $out;
8716  }
8717 
8718 
8729  public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage, $pos = '')
8730  {
8731  global $conf, $langs, $user, $extrafields;
8732 
8733  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
8734  return '';
8735  }
8736  if (empty($array)) {
8737  return '';
8738  }
8739 
8740  $tmpvar = "MAIN_SELECTEDFIELDS_" . $varpage; // To get list of saved selected fields to show
8741 
8742  if (!empty($user->conf->$tmpvar)) { // A list of fields was already customized for user
8743  $tmparray = explode(',', $user->conf->$tmpvar);
8744  foreach ($array as $key => $val) {
8745  //var_dump($key);
8746  //var_dump($tmparray);
8747  if (in_array($key, $tmparray)) {
8748  $array[$key]['checked'] = 1;
8749  } else {
8750  $array[$key]['checked'] = 0;
8751  }
8752  }
8753  } else { // There is no list of fields already customized for user
8754  foreach ($array as $key => $val) {
8755  if (!empty($array[$key]['checked']) && $array[$key]['checked'] < 0) {
8756  $array[$key]['checked'] = 0;
8757  }
8758  }
8759  }
8760 
8761  $listoffieldsforselection = '';
8762  $listcheckedstring = '';
8763 
8764  foreach ($array as $key => $val) {
8765  // var_dump($val);
8766  // var_dump(array_key_exists('enabled', $val));
8767  // var_dump(!$val['enabled']);
8768  if (array_key_exists('enabled', $val) && isset($val['enabled']) && !$val['enabled']) {
8769  unset($array[$key]); // We don't want this field
8770  continue;
8771  }
8772  if (!empty($val['type']) && $val['type'] == 'separate') {
8773  // Field remains in array but we don't add it into $listoffieldsforselection
8774  //$listoffieldsforselection .= '<li>-----</li>';
8775  continue;
8776  }
8777  if ($val['label']) {
8778  if (!empty($val['langfile']) && is_object($langs)) {
8779  $langs->load($val['langfile']);
8780  }
8781 
8782  // Note: $val['checked'] <> 0 means we must show the field into the combo list
8783  $listoffieldsforselection .= '<li><input type="checkbox" id="checkbox' . $key . '" value="' . $key . '"' . ((empty($val['checked']) || $val['checked'] == '-1') ? '' : ' checked="checked"') . '/><label for="checkbox' . $key . '">' . dol_escape_htmltag($langs->trans($val['label'])) . '</label></li>';
8784  $listcheckedstring .= (empty($val['checked']) ? '' : $key . ',');
8785  }
8786  }
8787 
8788  $out = '<!-- Component multiSelectArrayWithCheckbox ' . $htmlname . ' -->
8789 
8790  <dl class="dropdown">
8791  <dt>
8792  <a href="#' . $htmlname . '">
8793  ' . img_picto('', 'list') . '
8794  </a>
8795  <input type="hidden" class="' . $htmlname . '" name="' . $htmlname . '" value="' . $listcheckedstring . '">
8796  </dt>
8797  <dd class="dropdowndd">
8798  <div class="multiselectcheckbox'.$htmlname.'">
8799  <ul class="'.$htmlname.($pos == '1' ? 'left' : '').'">
8800  <li><input class="inputsearch_dropdownselectedfields width90p minwidth200imp" style="width:90%;" type="text" placeholder="'.$langs->trans('Search').'"></li>
8801  '.$listoffieldsforselection.'
8802  </ul>
8803  </div>
8804  </dd>
8805  </dl>
8806 
8807  <script nonce="' . getNonce() . '" type="text/javascript">
8808  jQuery(document).ready(function () {
8809  $(\'.multiselectcheckbox' . $htmlname . ' input[type="checkbox"]\').on(\'click\', function () {
8810  console.log("A new field was added/removed, we edit field input[name=formfilteraction]");
8811 
8812  $("input:hidden[name=formfilteraction]").val(\'listafterchangingselectedfields\'); // Update field so we know we changed something on selected fields after POST
8813 
8814  var title = $(this).val() + ",";
8815  if ($(this).is(\':checked\')) {
8816  $(\'.' . $htmlname . '\').val(title + $(\'.' . $htmlname . '\').val());
8817  }
8818  else {
8819  $(\'.' . $htmlname . '\').val( $(\'.' . $htmlname . '\').val().replace(title, \'\') )
8820  }
8821  // Now, we submit page
8822  //$(this).parents(\'form:first\').submit();
8823  });
8824  $("input.inputsearch_dropdownselectedfields").on("keyup", function() {
8825  var value = $(this).val().toLowerCase();
8826  $(\'.multiselectcheckbox'.$htmlname.' li > label\').filter(function() {
8827  $(this).parent().toggle($(this).text().toLowerCase().indexOf(value) > -1)
8828  });
8829  });
8830 
8831 
8832  });
8833  </script>
8834 
8835  ';
8836  return $out;
8837  }
8838 
8848  public function showCategories($id, $type, $rendermode = 0, $nolink = 0)
8849  {
8850  include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8851 
8852  $cat = new Categorie($this->db);
8853  $categories = $cat->containing($id, $type);
8854 
8855  if ($rendermode == 1) {
8856  $toprint = array();
8857  foreach ($categories as $c) {
8858  $ways = $c->print_all_ways(' &gt;&gt; ', ($nolink ? 'none' : ''), 0, 1); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formated text
8859  foreach ($ways as $way) {
8860  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #bbb"') . '>' . $way . '</li>';
8861  }
8862  }
8863  return '<div class="select2-container-multi-dolibarr"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
8864  }
8865 
8866  if ($rendermode == 0) {
8867  $arrayselected = array();
8868  $cate_arbo = $this->select_all_categories($type, '', 'parent', 64, 0, 1);
8869  foreach ($categories as $c) {
8870  $arrayselected[] = $c->id;
8871  }
8872 
8873  return $this->multiselectarray('categories', $cate_arbo, $arrayselected, '', 0, '', 0, '100%', 'disabled', 'category');
8874  }
8875 
8876  return 'ErrorBadValueForParameterRenderMode'; // Should not happened
8877  }
8878 
8888  public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleImportElementsList = false, $title = 'RelatedObjects')
8889  {
8890  global $conf, $langs, $hookmanager;
8891  global $bc, $action;
8892 
8893  $object->fetchObjectLinked();
8894 
8895  // Bypass the default method
8896  $hookmanager->initHooks(array('commonobject'));
8897  $parameters = array(
8898  'morehtmlright' => $morehtmlright,
8899  'compatibleImportElementsList' => &$compatibleImportElementsList,
8900  );
8901  $reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
8902 
8903  $nbofdifferenttypes = count($object->linkedObjects);
8904 
8905  if (empty($reshook)) {
8906  print '<!-- showLinkedObjectBlock -->';
8907  print load_fiche_titre($langs->trans($title), $morehtmlright, '', 0, 0, 'showlinkedobjectblock');
8908 
8909 
8910  print '<div class="div-table-responsive-no-min">';
8911  print '<table class="noborder allwidth" data-block="showLinkedObject" data-element="' . $object->element . '" data-elementid="' . $object->id . '" >';
8912 
8913  print '<tr class="liste_titre">';
8914  print '<td>' . $langs->trans("Type") . '</td>';
8915  print '<td>' . $langs->trans("Ref") . '</td>';
8916  print '<td class="center"></td>';
8917  print '<td class="center">' . $langs->trans("Date") . '</td>';
8918  print '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
8919  print '<td class="right">' . $langs->trans("Status") . '</td>';
8920  print '<td></td>';
8921  print '</tr>';
8922 
8923  $nboftypesoutput = 0;
8924 
8925  foreach ($object->linkedObjects as $objecttype => $objects) {
8926  $tplpath = $element = $subelement = $objecttype;
8927 
8928  // to display inport button on tpl
8929  $showImportButton = false;
8930  if (!empty($compatibleImportElementsList) && in_array($element, $compatibleImportElementsList)) {
8931  $showImportButton = true;
8932  }
8933 
8934  $regs = array();
8935  if ($objecttype != 'supplier_proposal' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
8936  $element = $regs[1];
8937  $subelement = $regs[2];
8938  $tplpath = $element . '/' . $subelement;
8939  }
8940  $tplname = 'linkedobjectblock';
8941 
8942  // To work with non standard path
8943  if ($objecttype == 'facture') {
8944  $tplpath = 'compta/' . $element;
8945  if (!isModEnabled('facture')) {
8946  continue; // Do not show if module disabled
8947  }
8948  } elseif ($objecttype == 'facturerec') {
8949  $tplpath = 'compta/facture';
8950  $tplname = 'linkedobjectblockForRec';
8951  if (!isModEnabled('facture')) {
8952  continue; // Do not show if module disabled
8953  }
8954  } elseif ($objecttype == 'propal') {
8955  $tplpath = 'comm/' . $element;
8956  if (!isModEnabled('propal')) {
8957  continue; // Do not show if module disabled
8958  }
8959  } elseif ($objecttype == 'supplier_proposal') {
8960  if (!isModEnabled('supplier_proposal')) {
8961  continue; // Do not show if module disabled
8962  }
8963  } elseif ($objecttype == 'shipping' || $objecttype == 'shipment' || $objecttype == 'expedition') {
8964  $tplpath = 'expedition';
8965  if (!isModEnabled('expedition')) {
8966  continue; // Do not show if module disabled
8967  }
8968  } elseif ($objecttype == 'reception') {
8969  $tplpath = 'reception';
8970  if (!isModEnabled('reception')) {
8971  continue; // Do not show if module disabled
8972  }
8973  } elseif ($objecttype == 'delivery') {
8974  $tplpath = 'delivery';
8975  if (!isModEnabled('expedition')) {
8976  continue; // Do not show if module disabled
8977  }
8978  } elseif ($objecttype == 'ficheinter') {
8979  $tplpath = 'fichinter';
8980  if (!isModEnabled('ficheinter')) {
8981  continue; // Do not show if module disabled
8982  }
8983  } elseif ($objecttype == 'invoice_supplier') {
8984  $tplpath = 'fourn/facture';
8985  } elseif ($objecttype == 'order_supplier') {
8986  $tplpath = 'fourn/commande';
8987  } elseif ($objecttype == 'expensereport') {
8988  $tplpath = 'expensereport';
8989  } elseif ($objecttype == 'subscription') {
8990  $tplpath = 'adherents';
8991  } elseif ($objecttype == 'conferenceorbooth') {
8992  $tplpath = 'eventorganization';
8993  } elseif ($objecttype == 'conferenceorboothattendee') {
8994  $tplpath = 'eventorganization';
8995  } elseif ($objecttype == 'mo') {
8996  $tplpath = 'mrp';
8997  if (!isModEnabled('mrp')) {
8998  continue; // Do not show if module disabled
8999  }
9000  }
9001 
9002  global $linkedObjectBlock;
9003  $linkedObjectBlock = $objects;
9004 
9005  // Output template part (modules that overwrite templates must declare this into descriptor)
9006  $dirtpls = array_merge($conf->modules_parts['tpl'], array('/' . $tplpath . '/tpl'));
9007  foreach ($dirtpls as $reldir) {
9008  if ($nboftypesoutput == ($nbofdifferenttypes - 1)) { // No more type to show after
9009  global $noMoreLinkedObjectBlockAfter;
9010  $noMoreLinkedObjectBlockAfter = 1;
9011  }
9012 
9013  $res = @include dol_buildpath($reldir . '/' . $tplname . '.tpl.php');
9014  if ($res) {
9015  $nboftypesoutput++;
9016  break;
9017  }
9018  }
9019  }
9020 
9021  if (!$nboftypesoutput) {
9022  print '<tr><td class="impair" colspan="7"><span class="opacitymedium">' . $langs->trans("None") . '</span></td></tr>';
9023  }
9024 
9025  print '</table>';
9026 
9027  if (!empty($compatibleImportElementsList)) {
9028  $res = @include dol_buildpath('core/tpl/objectlinked_lineimport.tpl.php');
9029  }
9030 
9031  print '</div>';
9032  }
9033 
9034  return $nbofdifferenttypes;
9035  }
9036 
9045  public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array())
9046  {
9047  global $conf, $langs, $hookmanager;
9048  global $action;
9049 
9050  $linktoelem = '';
9051  $linktoelemlist = '';
9052  $listofidcompanytoscan = '';
9053 
9054  if (!is_object($object->thirdparty)) {
9055  $object->fetch_thirdparty();
9056  }
9057 
9058  $possiblelinks = array();
9059  if (is_object($object->thirdparty) && !empty($object->thirdparty->id) && $object->thirdparty->id > 0) {
9060  $listofidcompanytoscan = $object->thirdparty->id;
9061  if (($object->thirdparty->parent > 0) && !empty($conf->global->THIRDPARTY_INCLUDE_PARENT_IN_LINKTO)) {
9062  $listofidcompanytoscan .= ',' . $object->thirdparty->parent;
9063  }
9064  if (($object->fk_project > 0) && !empty($conf->global->THIRDPARTY_INCLUDE_PROJECT_THIRDPARY_IN_LINKTO)) {
9065  include_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
9066  $tmpproject = new Project($this->db);
9067  $tmpproject->fetch($object->fk_project);
9068  if ($tmpproject->socid > 0 && ($tmpproject->socid != $object->thirdparty->id)) {
9069  $listofidcompanytoscan .= ',' . $tmpproject->socid;
9070  }
9071  unset($tmpproject);
9072  }
9073 
9074  $possiblelinks = array(
9075  'propal' => array(
9076  'enabled' => isModEnabled('propal'),
9077  'perms' => 1,
9078  'label' => 'LinkToProposal',
9079  'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "propal as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('propal') . ')'),
9080  'shipping' => array(
9081  'enabled' => isModEnabled('expedition'),
9082  'perms' => 1,
9083  'label' => 'LinkToExpedition',
9084  'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "expedition as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('shipping') . ')'),
9085  'order' => array(
9086  'enabled' => isModEnabled('commande'),
9087  'perms' => 1,
9088  'label' => 'LinkToOrder',
9089  'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "commande as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('commande') . ')'),
9090  'invoice' => array(
9091  'enabled' => isModEnabled('facture'),
9092  'perms' => 1,
9093  'label' => 'LinkToInvoice',
9094  'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "facture as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('invoice') . ')'),
9095  'invoice_template' => array(
9096  'enabled' => isModEnabled('facture'),
9097  'perms' => 1,
9098  'label' => 'LinkToTemplateInvoice',
9099  'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.titre as ref, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "facture_rec as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('invoice') . ')'),
9100  'contrat' => array(
9101  'enabled' => isModEnabled('contrat'),
9102  'perms' => 1,
9103  'label' => 'LinkToContract',
9104  'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_customer as ref_client, t.ref_supplier, SUM(td.total_ht) as total_ht
9105  FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "contrat as t, " . $this->db->prefix() . "contratdet as td WHERE t.fk_soc = s.rowid AND td.fk_contrat = t.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('contract') . ') GROUP BY s.rowid, s.nom, s.client, t.rowid, t.ref, t.ref_customer, t.ref_supplier'
9106  ),
9107  'fichinter' => array(
9108  'enabled' => isModEnabled('ficheinter'),
9109  'perms' => 1,
9110  'label' => 'LinkToIntervention',
9111  'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "fichinter as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('intervention') . ')'),
9112  'supplier_proposal' => array(
9113  'enabled' => (isModEnabled('supplier_proposal') ? $conf->supplier_proposal->enabled : 0),
9114  'perms' => 1,
9115  'label' => 'LinkToSupplierProposal',
9116  'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, '' as ref_supplier, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "supplier_proposal as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('supplier_proposal') . ')'),
9117  'order_supplier' => array(
9118  'enabled' => (isModEnabled("supplier_order") ? $conf->supplier_order->enabled : 0),
9119  'perms' => 1,
9120  'label' => 'LinkToSupplierOrder',
9121  'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "commande_fournisseur as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('commande_fournisseur') . ')'),
9122  'invoice_supplier' => array(
9123  'enabled' => (isModEnabled("supplier_invoice") ? $conf->supplier_invoice->enabled : 0),
9124  'perms' => 1, 'label' => 'LinkToSupplierInvoice',
9125  'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "facture_fourn as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('facture_fourn') . ')'),
9126  'ticket' => array(
9127  'enabled' => isModEnabled('ticket'),
9128  'perms' => 1,
9129  'label' => 'LinkToTicket',
9130  'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.track_id, '0' as total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "ticket as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('ticket') . ')'),
9131  'mo' => array(
9132  'enabled' => isModEnabled('mrp'),
9133  'perms' => 1,
9134  'label' => 'LinkToMo',
9135  'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.rowid, '0' as total_ht FROM " . $this->db->prefix() . "societe as s INNER JOIN " . $this->db->prefix() . "mrp_mo as t ON t.fk_soc = s.rowid WHERE t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('mo') . ')')
9136  );
9137  }
9138 
9139  if ($object->table_element == 'commande_fournisseur') {
9140  $possiblelinks['mo']['sql'] = "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.rowid, '0' as total_ht FROM ".$this->db->prefix()."societe as s INNER JOIN ".$this->db->prefix().'mrp_mo as t ON t.fk_soc = s.rowid WHERE t.entity IN ('.getEntity('mo').')';
9141  } elseif ($object->table_element == 'mrp_mo') {
9142  $possiblelinks['order_supplier']['sql'] = "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM ".$this->db->prefix()."societe as s, ".$this->db->prefix().'commande_fournisseur as t WHERE t.fk_soc = s.rowid AND t.entity IN ('.getEntity('commande_fournisseur').')';
9143  }
9144 
9145  if (!empty($listofidcompanytoscan)) { // If empty, we don't have criteria to scan the object we can link to
9146  // Can complete the possiblelink array
9147  $hookmanager->initHooks(array('commonobject'));
9148  $parameters = array('listofidcompanytoscan' => $listofidcompanytoscan, 'possiblelinks' => $possiblelinks);
9149  $reshook = $hookmanager->executeHooks('showLinkToObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
9150  }
9151 
9152  if (empty($reshook)) {
9153  if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
9154  $possiblelinks = array_merge($possiblelinks, $hookmanager->resArray);
9155  }
9156  } elseif ($reshook > 0) {
9157  if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
9158  $possiblelinks = $hookmanager->resArray;
9159  }
9160  }
9161 
9162  foreach ($possiblelinks as $key => $possiblelink) {
9163  $num = 0;
9164 
9165  if (empty($possiblelink['enabled'])) {
9166  continue;
9167  }
9168 
9169  if (!empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || !in_array($key, $excludelinksto))) {
9170  print '<div id="' . $key . 'list"' . (empty($conf->use_javascript_ajax) ? '' : ' style="display:none"') . '>';
9171 
9172  if (!empty($conf->global->MAIN_LINK_BY_REF_IN_LINKTO)) {
9173  print '<br>'."\n";
9174  print '<!-- form to add a link from anywhere -->'."\n";
9175  print '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinkedbyref' . $key . '">';
9176  print '<input type="hidden" name="id" value="' . $object->id . '">';
9177  print '<input type="hidden" name="action" value="addlinkbyref">';
9178  print '<input type="hidden" name="token" value="' . newToken() . '">';
9179  print '<input type="hidden" name="addlink" value="' . $key . '">';
9180  print '<table class="noborder">';
9181  print '<tr>';
9182  //print '<td>' . $langs->trans("Ref") . '</td>';
9183  print '<td class="center"><input type="text" placeholder="'.dol_escape_htmltag($langs->trans("Ref")).'" name="reftolinkto" value="' . dol_escape_htmltag(GETPOST('reftolinkto', 'alpha')) . '">&nbsp;';
9184  print '<input type="submit" class="button small valignmiddle" value="' . $langs->trans('ToLink') . '">&nbsp;';
9185  print '<input type="submit" class="button small" name="cancel" value="' . $langs->trans('Cancel') . '"></td>';
9186  print '</tr>';
9187  print '</table>';
9188  print '</form>';
9189  }
9190 
9191  $sql = $possiblelink['sql'];
9192 
9193  $resqllist = $this->db->query($sql);
9194  if ($resqllist) {
9195  $num = $this->db->num_rows($resqllist);
9196  $i = 0;
9197 
9198  print '<br>';
9199  print '<!-- form to add a link from object to same thirdparty -->'."\n";
9200  print '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinked' . $key . '">';
9201  print '<input type="hidden" name="action" value="addlink">';
9202  print '<input type="hidden" name="token" value="' . newToken() . '">';
9203  print '<input type="hidden" name="id" value="' . $object->id . '">';
9204  print '<input type="hidden" name="addlink" value="' . $key . '">';
9205  print '<table class="noborder">';
9206  print '<tr class="liste_titre">';
9207  print '<td class="nowrap"></td>';
9208  print '<td class="center">' . $langs->trans("Ref") . '</td>';
9209  print '<td class="left">' . $langs->trans("RefCustomer") . '</td>';
9210  print '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
9211  print '<td class="left">' . $langs->trans("Company") . '</td>';
9212  print '</tr>';
9213  while ($i < $num) {
9214  $objp = $this->db->fetch_object($resqllist);
9215 
9216  print '<tr class="oddeven">';
9217  print '<td class="left">';
9218  print '<input type="radio" name="idtolinkto" id="' . $key . '_' . $objp->rowid . '" value="' . $objp->rowid . '">';
9219  print '</td>';
9220  print '<td class="center"><label for="' . $key . '_' . $objp->rowid . '">' . $objp->ref . '</label></td>';
9221  print '<td>' . (!empty($objp->ref_client) ? $objp->ref_client : (!empty($objp->ref_supplier) ? $objp->ref_supplier : '')) . '</td>';
9222  print '<td class="right">';
9223  if ($possiblelink['label'] == 'LinkToContract') {
9224  $form = new Form($this->db);
9225  print $form->textwithpicto('', $langs->trans("InformationOnLinkToContract")) . ' ';
9226  }
9227  print '<span class="amount">' . (isset($objp->total_ht) ? price($objp->total_ht) : '') . '</span>';
9228  print '</td>';
9229  print '<td>' . $objp->name . '</td>';
9230  print '</tr>';
9231  $i++;
9232  }
9233  print '</table>';
9234  print '<div class="center">';
9235  if ($num) {
9236  print '<input type="submit" class="button valignmiddle marginleftonly marginrightonly small" value="' . $langs->trans('ToLink') . '">';
9237  }
9238  if (empty($conf->use_javascript_ajax)) {
9239  print '<input type="submit" class="button button-cancel marginleftonly marginrightonly small" name="cancel" value="' . $langs->trans("Cancel") . '"></div>';
9240  } else {
9241  print '<input type="submit" onclick="jQuery(\'#' . $key . 'list\').toggle(); return false;" class="button button-cancel marginleftonly marginrightonly small" name="cancel" value="' . $langs->trans("Cancel") . '"></div>';
9242  }
9243  print '</form>';
9244  $this->db->free($resqllist);
9245  } else {
9246  dol_print_error($this->db);
9247  }
9248  print '</div>';
9249 
9250  //$linktoelem.=($linktoelem?' &nbsp; ':'');
9251  if ($num > 0 || !empty($conf->global->MAIN_LINK_BY_REF_IN_LINKTO)) {
9252  $linktoelemlist .= '<li><a href="#linkto' . $key . '" class="linkto dropdowncloseonclick" rel="' . $key . '">' . $langs->trans($possiblelink['label']) . ' (' . $num . ')</a></li>';
9253  // } else $linktoelem.=$langs->trans($possiblelink['label']);
9254  } else {
9255  $linktoelemlist .= '<li><span class="linktodisabled">' . $langs->trans($possiblelink['label']) . ' (0)</span></li>';
9256  }
9257  }
9258  }
9259 
9260  if ($linktoelemlist) {
9261  $linktoelem = '
9262  <dl class="dropdown" id="linktoobjectname">
9263  ';
9264  if (!empty($conf->use_javascript_ajax)) {
9265  $linktoelem .= '<dt><a href="#linktoobjectname"><span class="fas fa-link paddingrightonly"></span>' . $langs->trans("LinkTo") . '...</a></dt>';
9266  }
9267  $linktoelem .= '<dd>
9268  <div class="multiselectlinkto">
9269  <ul class="ulselectedfields">' . $linktoelemlist . '
9270  </ul>
9271  </div>
9272  </dd>
9273  </dl>';
9274  } else {
9275  $linktoelem = '';
9276  }
9277 
9278  if (!empty($conf->use_javascript_ajax)) {
9279  print '<!-- Add js to show linkto box -->
9280  <script nonce="' . getNonce() . '">
9281  jQuery(document).ready(function() {
9282  jQuery(".linkto").click(function() {
9283  console.log("We choose to show/hide links for rel="+jQuery(this).attr(\'rel\')+" so #"+jQuery(this).attr(\'rel\')+"list");
9284  jQuery("#"+jQuery(this).attr(\'rel\')+"list").toggle();
9285  });
9286  });
9287  </script>
9288  ';
9289  }
9290 
9291  return $linktoelem;
9292  }
9293 
9308  public function selectyesno($htmlname, $value = '', $option = 0, $disabled = false, $useempty = 0, $addjscombo = 0, $morecss = '', $labelyes = 'Yes', $labelno = 'No')
9309  {
9310  global $langs;
9311 
9312  $yes = "yes";
9313  $no = "no";
9314  if ($option) {
9315  $yes = "1";
9316  $no = "0";
9317  }
9318 
9319  $disabled = ($disabled ? ' disabled' : '');
9320 
9321  $resultyesno = '<select class="flat width75' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '"' . $disabled . '>' . "\n";
9322  if ($useempty) {
9323  $resultyesno .= '<option value="-1"' . (($value < 0) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
9324  }
9325  if (("$value" == 'yes') || ($value == 1)) {
9326  $resultyesno .= '<option value="' . $yes . '" selected>' . $langs->trans($labelyes) . '</option>' . "\n";
9327  $resultyesno .= '<option value="' . $no . '">' . $langs->trans($labelno) . '</option>' . "\n";
9328  } else {
9329  $selected = (($useempty && $value != '0' && $value != 'no') ? '' : ' selected');
9330  $resultyesno .= '<option value="' . $yes . '">' . $langs->trans($labelyes) . '</option>' . "\n";
9331  $resultyesno .= '<option value="' . $no . '"' . $selected . '>' . $langs->trans($labelno) . '</option>' . "\n";
9332  }
9333  $resultyesno .= '</select>' . "\n";
9334 
9335  if ($addjscombo) {
9336  $resultyesno .= ajax_combobox($htmlname, array(), 0, 0, 'resolve', ($useempty < 0 ? (string) $useempty : '-1'), $morecss);
9337  }
9338 
9339  return $resultyesno;
9340  }
9341 
9342  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
9343 
9353  public function select_export_model($selected = '', $htmlname = 'exportmodelid', $type = '', $useempty = 0)
9354  {
9355  // phpcs:enable
9356  $sql = "SELECT rowid, label";
9357  $sql .= " FROM " . $this->db->prefix() . "export_model";
9358  $sql .= " WHERE type = '" . $this->db->escape($type) . "'";
9359  $sql .= " ORDER BY rowid";
9360  $result = $this->db->query($sql);
9361  if ($result) {
9362  print '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
9363  if ($useempty) {
9364  print '<option value="-1">&nbsp;</option>';
9365  }
9366 
9367  $num = $this->db->num_rows($result);
9368  $i = 0;
9369  while ($i < $num) {
9370  $obj = $this->db->fetch_object($result);
9371  if ($selected == $obj->rowid) {
9372  print '<option value="' . $obj->rowid . '" selected>';
9373  } else {
9374  print '<option value="' . $obj->rowid . '">';
9375  }
9376  print $obj->label;
9377  print '</option>';
9378  $i++;
9379  }
9380  print "</select>";
9381  } else {
9382  dol_print_error($this->db);
9383  }
9384  }
9385 
9404  public function showrefnav($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $morehtmlright = '')
9405  {
9406  global $conf, $langs, $hookmanager, $extralanguages;
9407 
9408  $ret = '';
9409  if (empty($fieldid)) {
9410  $fieldid = 'rowid';
9411  }
9412  if (empty($fieldref)) {
9413  $fieldref = 'ref';
9414  }
9415 
9416  // Preparing gender's display if there is one
9417  $addgendertxt = '';
9418  if (property_exists($object, 'gender') && !empty($object->gender)) {
9419  $addgendertxt = ' ';
9420  switch ($object->gender) {
9421  case 'man':
9422  $addgendertxt .= '<i class="fas fa-mars"></i>';
9423  break;
9424  case 'woman':
9425  $addgendertxt .= '<i class="fas fa-venus"></i>';
9426  break;
9427  case 'other':
9428  $addgendertxt .= '<i class="fas fa-transgender"></i>';
9429  break;
9430  }
9431  }
9432 
9433  /*
9434  $addadmin = '';
9435  if (property_exists($object, 'admin')) {
9436  if (isModEnabled('multicompany') && !empty($object->admin) && empty($object->entity)) {
9437  $addadmin .= img_picto($langs->trans("SuperAdministratorDesc"), "redstar", 'class="paddingleft"');
9438  } elseif (!empty($object->admin)) {
9439  $addadmin .= img_picto($langs->trans("AdministratorDesc"), "star", 'class="paddingleft"');
9440  }
9441  }*/
9442 
9443  // Add where from hooks
9444  if (is_object($hookmanager)) {
9445  $parameters = array('showrefnav' => true);
9446  $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
9447  $object->next_prev_filter .= $hookmanager->resPrint;
9448  }
9449  $previous_ref = $next_ref = '';
9450  if ($shownav) {
9451  //print "paramid=$paramid,morehtml=$morehtml,shownav=$shownav,$fieldid,$fieldref,$morehtmlref,$moreparam";
9452  $object->load_previous_next_ref((isset($object->next_prev_filter) ? $object->next_prev_filter : ''), $fieldid, $nodbprefix);
9453 
9454  $navurl = $_SERVER["PHP_SELF"];
9455  // Special case for project/task page
9456  if ($paramid == 'project_ref') {
9457  if (preg_match('/\/tasks\/(task|contact|note|document)\.php/', $navurl)) { // TODO Remove this when nav with project_ref on task pages are ok
9458  $navurl = preg_replace('/\/tasks\/(task|contact|time|note|document)\.php/', '/tasks.php', $navurl);
9459  $paramid = 'ref';
9460  }
9461  }
9462 
9463  // accesskey is for Windows or Linux: ALT + key for chrome, ALT + SHIFT + KEY for firefox
9464  // accesskey is for Mac: CTRL + key for all browsers
9465  $stringforfirstkey = $langs->trans("KeyboardShortcut");
9466  if ($conf->browser->name == 'chrome') {
9467  $stringforfirstkey .= ' ALT +';
9468  } elseif ($conf->browser->name == 'firefox') {
9469  $stringforfirstkey .= ' ALT + SHIFT +';
9470  } else {
9471  $stringforfirstkey .= ' CTL +';
9472  }
9473 
9474  $previous_ref = $object->ref_previous ? '<a accesskey="p" title="' . $stringforfirstkey . ' p" class="classfortooltip" href="' . $navurl . '?' . $paramid . '=' . urlencode($object->ref_previous) . $moreparam . '"><i class="fa fa-chevron-left"></i></a>' : '<span class="inactive"><i class="fa fa-chevron-left opacitymedium"></i></span>';
9475  $next_ref = $object->ref_next ? '<a accesskey="n" title="' . $stringforfirstkey . ' n" class="classfortooltip" href="' . $navurl . '?' . $paramid . '=' . urlencode($object->ref_next) . $moreparam . '"><i class="fa fa-chevron-right"></i></a>' : '<span class="inactive"><i class="fa fa-chevron-right opacitymedium"></i></span>';
9476  }
9477 
9478  //print "xx".$previous_ref."x".$next_ref;
9479  $ret .= '<!-- Start banner content --><div style="vertical-align: middle">';
9480 
9481  // Right part of banner
9482  if ($morehtmlright) {
9483  $ret .= '<div class="inline-block floatleft">' . $morehtmlright . '</div>';
9484  }
9485 
9486  if ($previous_ref || $next_ref || $morehtml) {
9487  $ret .= '<div class="pagination paginationref"><ul class="right">';
9488  }
9489  if ($morehtml) {
9490  $ret .= '<li class="noborder litext' . (($shownav && $previous_ref && $next_ref) ? ' clearbothonsmartphone' : '') . '">' . $morehtml . '</li>';
9491  }
9492  if ($shownav && ($previous_ref || $next_ref)) {
9493  $ret .= '<li class="pagination">' . $previous_ref . '</li>';
9494  $ret .= '<li class="pagination">' . $next_ref . '</li>';
9495  }
9496  if ($previous_ref || $next_ref || $morehtml) {
9497  $ret .= '</ul></div>';
9498  }
9499 
9500  $parameters = array();
9501  $reshook = $hookmanager->executeHooks('moreHtmlStatus', $parameters, $object); // Note that $action and $object may have been modified by hook
9502  if (empty($reshook)) {
9503  $morehtmlstatus .= $hookmanager->resPrint;
9504  } else {
9505  $morehtmlstatus = $hookmanager->resPrint;
9506  }
9507  if ($morehtmlstatus) {
9508  $ret .= '<div class="statusref">' . $morehtmlstatus . '</div>';
9509  }
9510 
9511  $parameters = array();
9512  $reshook = $hookmanager->executeHooks('moreHtmlRef', $parameters, $object); // Note that $action and $object may have been modified by hook
9513  if (empty($reshook)) {
9514  $morehtmlref .= $hookmanager->resPrint;
9515  } elseif ($reshook > 0) {
9516  $morehtmlref = $hookmanager->resPrint;
9517  }
9518 
9519  // Left part of banner
9520  if ($morehtmlleft) {
9521  if ($conf->browser->layout == 'phone') {
9522  $ret .= '<!-- morehtmlleft --><div class="floatleft">' . $morehtmlleft . '</div>';
9523  } else {
9524  $ret .= '<!-- morehtmlleft --><div class="inline-block floatleft">' . $morehtmlleft . '</div>';
9525  }
9526  }
9527 
9528  //if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
9529  $ret .= '<div class="inline-block floatleft valignmiddle maxwidth750 marginbottomonly refid' . (($shownav && ($previous_ref || $next_ref)) ? ' refidpadding' : '') . '">';
9530 
9531  // For thirdparty, contact, user, member, the ref is the id, so we show something else
9532  if ($object->element == 'societe') {
9533  $ret .= dol_htmlentities($object->name);
9534 
9535  // List of extra languages
9536  $arrayoflangcode = array();
9537  if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) {
9538  $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
9539  }
9540 
9541  if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
9542  if (!is_object($extralanguages)) {
9543  include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
9544  $extralanguages = new ExtraLanguages($this->db);
9545  }
9546  $extralanguages->fetch_name_extralanguages('societe');
9547 
9548  if (!empty($extralanguages->attributes['societe']['name'])) {
9549  $object->fetchValuesForExtraLanguages();
9550 
9551  $htmltext = '';
9552  // If there is extra languages
9553  foreach ($arrayoflangcode as $extralangcode) {
9554  $htmltext .= picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
9555  if ($object->array_languages['name'][$extralangcode]) {
9556  $htmltext .= $object->array_languages['name'][$extralangcode];
9557  } else {
9558  $htmltext .= '<span class="opacitymedium">' . $langs->trans("SwitchInEditModeToAddTranslation") . '</span>';
9559  }
9560  }
9561  $ret .= '<!-- Show translations of name -->' . "\n";
9562  $ret .= $this->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
9563  }
9564  }
9565  } elseif ($object->element == 'member') {
9566  $ret .= $object->ref . '<br>';
9567  $fullname = $object->getFullName($langs);
9568  if ($object->morphy == 'mor' && $object->societe) {
9569  $ret .= dol_htmlentities($object->societe) . ((!empty($fullname) && $object->societe != $fullname) ? ' (' . dol_htmlentities($fullname) . $addgendertxt . ')' : '');
9570  } else {
9571  $ret .= dol_htmlentities($fullname) . $addgendertxt . ((!empty($object->societe) && $object->societe != $fullname) ? ' (' . dol_htmlentities($object->societe) . ')' : '');
9572  }
9573  } elseif (in_array($object->element, array('contact', 'user'))) {
9574  $ret .= dol_htmlentities($object->getFullName($langs)) . $addgendertxt;
9575  } elseif ($object->element == 'usergroup') {
9576  $ret .= dol_htmlentities($object->name);
9577  } elseif (in_array($object->element, array('action', 'agenda'))) {
9578  $ret .= $object->ref . '<br>' . $object->label;
9579  } elseif (in_array($object->element, array('adherent_type'))) {
9580  $ret .= $object->label;
9581  } elseif ($object->element == 'ecm_directories') {
9582  $ret .= '';
9583  } elseif ($fieldref != 'none') {
9584  $ret .= dol_htmlentities(!empty($object->$fieldref) ? $object->$fieldref : "");
9585  }
9586  if ($morehtmlref) {
9587  // don't add a additional space, when "$morehtmlref" starts with a HTML div tag
9588  if (substr($morehtmlref, 0, 4) != '<div') {
9589  $ret .= ' ';
9590  }
9591 
9592  $ret .= $morehtmlref;
9593  }
9594 
9595  $ret .= '</div>';
9596 
9597  $ret .= '</div><!-- End banner content -->';
9598 
9599  return $ret;
9600  }
9601 
9602 
9611  public function showbarcode(&$object, $width = 100, $morecss = '')
9612  {
9613  global $conf;
9614 
9615  //Check if barcode is filled in the card
9616  if (empty($object->barcode)) {
9617  return '';
9618  }
9619 
9620  // Complete object if not complete
9621  if (empty($object->barcode_type_code) || empty($object->barcode_type_coder)) {
9622  $result = $object->fetch_barcode();
9623  //Check if fetch_barcode() failed
9624  if ($result < 1) {
9625  return '<!-- ErrorFetchBarcode -->';
9626  }
9627  }
9628 
9629  // Barcode image
9630  $url = DOL_URL_ROOT . '/viewimage.php?modulepart=barcode&generator=' . urlencode($object->barcode_type_coder) . '&code=' . urlencode($object->barcode) . '&encoding=' . urlencode($object->barcode_type_code);
9631  $out = '<!-- url barcode = ' . $url . ' -->';
9632  $out .= '<img src="' . $url . '"' . ($morecss ? ' class="' . $morecss . '"' : '') . '>';
9633 
9634  return $out;
9635  }
9636 
9653  public static function showphoto($modulepart, $object, $width = 100, $height = 0, $caneditfield = 0, $cssclass = 'photowithmargin', $imagesize = '', $addlinktofullsize = 1, $cache = 0, $forcecapture = '', $noexternsourceoverwrite = 0)
9654  {
9655  global $conf, $langs;
9656 
9657  $entity = (!empty($object->entity) ? $object->entity : $conf->entity);
9658  $id = (!empty($object->id) ? $object->id : $object->rowid);
9659 
9660  $ret = '';
9661  $dir = '';
9662  $file = '';
9663  $originalfile = '';
9664  $altfile = '';
9665  $email = '';
9666  $capture = '';
9667  if ($modulepart == 'societe') {
9668  $dir = $conf->societe->multidir_output[$entity];
9669  if (!empty($object->logo)) {
9670  if (dolIsAllowedForPreview($object->logo)) {
9671  if ((string) $imagesize == 'mini') {
9672  $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_mini'); // getImageFileNameForSize include the thumbs
9673  } elseif ((string) $imagesize == 'small') {
9674  $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_small');
9675  } else {
9676  $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
9677  }
9678  $originalfile = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
9679  }
9680  }
9681  $email = $object->email;
9682  } elseif ($modulepart == 'contact') {
9683  $dir = $conf->societe->multidir_output[$entity] . '/contact';
9684  if (!empty($object->photo)) {
9685  if (dolIsAllowedForPreview($object->photo)) {
9686  if ((string) $imagesize == 'mini') {
9687  $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($object->photo, '_mini');
9688  } elseif ((string) $imagesize == 'small') {
9689  $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($object->photo, '_small');
9690  } else {
9691  $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $object->photo;
9692  }
9693  $originalfile = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $object->photo;
9694  }
9695  }
9696  $email = $object->email;
9697  $capture = 'user';
9698  } elseif ($modulepart == 'userphoto') {
9699  $dir = $conf->user->dir_output;
9700  if (!empty($object->photo)) {
9701  if (dolIsAllowedForPreview($object->photo)) {
9702  if ((string) $imagesize == 'mini') {
9703  $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($object->photo, '_mini');
9704  } elseif ((string) $imagesize == 'small') {
9705  $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($object->photo, '_small');
9706  } else {
9707  $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $object->photo;
9708  }
9709  $originalfile = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $object->photo;
9710  }
9711  }
9712  if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
9713  $altfile = $object->id . ".jpg"; // For backward compatibility
9714  }
9715  $email = $object->email;
9716  $capture = 'user';
9717  } elseif ($modulepart == 'memberphoto') {
9718  $dir = $conf->adherent->dir_output;
9719  if (!empty($object->photo)) {
9720  if (dolIsAllowedForPreview($object->photo)) {
9721  if ((string) $imagesize == 'mini') {
9722  $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($object->photo, '_mini');
9723  } elseif ((string) $imagesize == 'small') {
9724  $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($object->photo, '_small');
9725  } else {
9726  $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $object->photo;
9727  }
9728  $originalfile = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $object->photo;
9729  }
9730  }
9731  if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
9732  $altfile = $object->id . ".jpg"; // For backward compatibility
9733  }
9734  $email = $object->email;
9735  $capture = 'user';
9736  } else {
9737  // Generic case to show photos
9738  $dir = $conf->$modulepart->dir_output;
9739  if (!empty($object->photo)) {
9740  if (dolIsAllowedForPreview($object->photo)) {
9741  if ((string) $imagesize == 'mini') {
9742  $file = get_exdir($id, 2, 0, 0, $object, $modulepart) . 'photos/' . getImageFileNameForSize($object->photo, '_mini');
9743  } elseif ((string) $imagesize == 'small') {
9744  $file = get_exdir($id, 2, 0, 0, $object, $modulepart) . 'photos/' . getImageFileNameForSize($object->photo, '_small');
9745  } else {
9746  $file = get_exdir($id, 2, 0, 0, $object, $modulepart) . 'photos/' . $object->photo;
9747  }
9748  $originalfile = get_exdir($id, 2, 0, 0, $object, $modulepart) . 'photos/' . $object->photo;
9749  }
9750  }
9751  if (!empty($conf->global->MAIN_OLD_IMAGE_LINKS)) {
9752  $altfile = $object->id . ".jpg"; // For backward compatibility
9753  }
9754  $email = $object->email;
9755  }
9756 
9757  if ($forcecapture) {
9758  $capture = $forcecapture;
9759  }
9760 
9761  if ($dir) {
9762  if ($file && file_exists($dir . "/" . $file)) {
9763  if ($addlinktofullsize) {
9764  $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
9765  if ($urladvanced) {
9766  $ret .= '<a href="' . $urladvanced . '">';
9767  } else {
9768  $ret .= '<a href="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
9769  }
9770  }
9771  $ret .= '<img alt="Photo" class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . ' photologo' . (preg_replace('/[^a-z]/i', '_', $file)) . '" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($file) . '&cache=' . $cache . '">';
9772  if ($addlinktofullsize) {
9773  $ret .= '</a>';
9774  }
9775  } elseif ($altfile && file_exists($dir . "/" . $altfile)) {
9776  if ($addlinktofullsize) {
9777  $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
9778  if ($urladvanced) {
9779  $ret .= '<a href="' . $urladvanced . '">';
9780  } else {
9781  $ret .= '<a href="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
9782  }
9783  }
9784  $ret .= '<img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="Photo alt" id="photologo' . (preg_replace('/[^a-z]/i', '_', $file)) . '" class="' . $cssclass . '" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($altfile) . '&cache=' . $cache . '">';
9785  if ($addlinktofullsize) {
9786  $ret .= '</a>';
9787  }
9788  } else {
9789  $nophoto = '/public/theme/common/nophoto.png';
9790  $defaultimg = 'identicon'; // For gravatar
9791  if (in_array($modulepart, array('societe', 'userphoto', 'contact', 'memberphoto'))) { // For modules that need a special image when photo not found
9792  if ($modulepart == 'societe' || ($modulepart == 'memberphoto' && !empty($object->morphy) && strpos($object->morphy, 'mor')) !== false) {
9793  $nophoto = 'company';
9794  } else {
9795  $nophoto = '/public/theme/common/user_anonymous.png';
9796  if (!empty($object->gender) && $object->gender == 'man') {
9797  $nophoto = '/public/theme/common/user_man.png';
9798  }
9799  if (!empty($object->gender) && $object->gender == 'woman') {
9800  $nophoto = '/public/theme/common/user_woman.png';
9801  }
9802  }
9803  }
9804 
9805  if (isModEnabled('gravatar') && $email && empty($noexternsourceoverwrite)) {
9806  // see https://gravatar.com/site/implement/images/php/
9807  $ret .= '<!-- Put link to gravatar -->';
9808  $ret .= '<img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" title="' . $email . ' Gravatar avatar" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="https://www.gravatar.com/avatar/' . md5(strtolower(trim($email))) . '?s=' . $width . '&d=' . $defaultimg . '">'; // gravatar need md5 hash
9809  } else {
9810  if ($nophoto == 'company') {
9811  $ret .= '<div class="divforspanimg photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . '>' . img_picto('', 'company') . '</div>';
9812  $ret .= '<div class="difforspanimgright"></div>';
9813  } else {
9814  $ret .= '<img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . DOL_URL_ROOT . $nophoto . '">';
9815  }
9816  }
9817  }
9818 
9819  if ($caneditfield) {
9820  if ($object->photo) {
9821  $ret .= "<br>\n";
9822  }
9823  $ret .= '<table class="nobordernopadding centpercent">';
9824  if ($object->photo) {
9825  $ret .= '<tr><td><input type="checkbox" class="flat photodelete" name="deletephoto" id="photodelete"> <label for="photodelete">' . $langs->trans("Delete") . '</label><br><br></td></tr>';
9826  }
9827  $ret .= '<tr><td class="tdoverflow">';
9828  $maxfilesizearray = getMaxFileSizeArray();
9829  $maxmin = $maxfilesizearray['maxmin'];
9830  if ($maxmin > 0) {
9831  $ret .= '<input type="hidden" name="MAX_FILE_SIZE" value="' . ($maxmin * 1024) . '">'; // MAX_FILE_SIZE must precede the field type=file
9832  }
9833  $ret .= '<input type="file" class="flat maxwidth200onsmartphone" name="photo" id="photoinput" accept="image/*"' . ($capture ? ' capture="' . $capture . '"' : '') . '>';
9834  $ret .= '</td></tr>';
9835  $ret .= '</table>';
9836  }
9837  } else {
9838  dol_print_error('', 'Call of showphoto with wrong parameters modulepart=' . $modulepart);
9839  }
9840 
9841  return $ret;
9842  }
9843 
9844  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
9845 
9862  public function select_dolgroups($selected = '', $htmlname = 'groupid', $show_empty = 0, $exclude = '', $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $multiple = false, $morecss = '')
9863  {
9864  // phpcs:enable
9865  global $conf, $user, $langs;
9866 
9867  // Permettre l'exclusion de groupes
9868  $excludeGroups = null;
9869  if (is_array($exclude)) {
9870  $excludeGroups = implode(",", $exclude);
9871  }
9872  // Permettre l'inclusion de groupes
9873  $includeGroups = null;
9874  if (is_array($include)) {
9875  $includeGroups = implode(",", $include);
9876  }
9877 
9878  if (!is_array($selected)) {
9879  $selected = array($selected);
9880  }
9881 
9882  $out = '';
9883 
9884  // On recherche les groupes
9885  $sql = "SELECT ug.rowid, ug.nom as name";
9886  if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
9887  $sql .= ", e.label";
9888  }
9889  $sql .= " FROM " . $this->db->prefix() . "usergroup as ug ";
9890  if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
9891  $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid=ug.entity";
9892  if ($force_entity) {
9893  $sql .= " WHERE ug.entity IN (0, " . $force_entity . ")";
9894  } else {
9895  $sql .= " WHERE ug.entity IS NOT NULL";
9896  }
9897  } else {
9898  $sql .= " WHERE ug.entity IN (0, " . $conf->entity . ")";
9899  }
9900  if (is_array($exclude) && $excludeGroups) {
9901  $sql .= " AND ug.rowid NOT IN (" . $this->db->sanitize($excludeGroups) . ")";
9902  }
9903  if (is_array($include) && $includeGroups) {
9904  $sql .= " AND ug.rowid IN (" . $this->db->sanitize($includeGroups) . ")";
9905  }
9906  $sql .= " ORDER BY ug.nom ASC";
9907 
9908  dol_syslog(get_class($this) . "::select_dolgroups", LOG_DEBUG);
9909  $resql = $this->db->query($sql);
9910  if ($resql) {
9911  // Enhance with select2
9912  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9913 
9914  $out .= '<select class="flat minwidth200' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
9915 
9916  $num = $this->db->num_rows($resql);
9917  $i = 0;
9918  if ($num) {
9919  if ($show_empty && !$multiple) {
9920  $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
9921  }
9922 
9923  while ($i < $num) {
9924  $obj = $this->db->fetch_object($resql);
9925  $disableline = 0;
9926  if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
9927  $disableline = 1;
9928  }
9929 
9930  $out .= '<option value="' . $obj->rowid . '"';
9931  if ($disableline) {
9932  $out .= ' disabled';
9933  }
9934  if ((isset($selected[0]) && is_object($selected[0]) && $selected[0]->id == $obj->rowid) || ((!isset($selected[0]) || !is_object($selected[0])) && !empty($selected) && in_array($obj->rowid, $selected))) {
9935  $out .= ' selected';
9936  }
9937  $out .= '>';
9938 
9939  $out .= $obj->name;
9940  if (isModEnabled('multicompany') && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1) {
9941  $out .= " (" . $obj->label . ")";
9942  }
9943 
9944  $out .= '</option>';
9945  $i++;
9946  }
9947  } else {
9948  if ($show_empty) {
9949  $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '></option>' . "\n";
9950  }
9951  $out .= '<option value="" disabled>' . $langs->trans("NoUserGroupDefined") . '</option>';
9952  }
9953  $out .= '</select>';
9954 
9955  $out .= ajax_combobox($htmlname);
9956  } else {
9957  dol_print_error($this->db);
9958  }
9959 
9960  return $out;
9961  }
9962 
9963 
9970  public function showFilterButtons($pos = '')
9971  {
9972  $out = '<div class="nowraponall">';
9973  if ($pos == 'left') {
9974  $out .= '<button type="submit" class="liste_titre button_search reposition" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
9975  $out .= '<button type="submit" class="liste_titre button_removefilter reposition" name="button_removefilter_x" value="x"><span class="fa fa-remove"></span></button>';
9976  } else {
9977  $out .= '<button type="submit" class="liste_titre button_search reposition" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
9978  $out .= '<button type="submit" class="liste_titre button_removefilter reposition" name="button_removefilter_x" value="x"><span class="fa fa-remove"></span></button>';
9979  }
9980  $out .= '</div>';
9981 
9982  return $out;
9983  }
9984 
9993  public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
9994  {
9995  global $conf, $langs;
9996 
9997  $out = '';
9998 
9999  if (!empty($conf->use_javascript_ajax)) {
10000  $out .= '<div class="inline-block checkallactions"><input type="checkbox" id="' . $cssclass . 's" name="' . $cssclass . 's" class="checkallactions"></div>';
10001  }
10002  $out .= '<script nonce="' . getNonce() . '">
10003  $(document).ready(function() {
10004  $("#' . $cssclass . 's").click(function() {
10005  if($(this).is(\':checked\')){
10006  console.log("We check all ' . $cssclass . ' and trigger the change method");
10007  $(".' . $cssclass . '").prop(\'checked\', true).trigger(\'change\');
10008  }
10009  else
10010  {
10011  console.log("We uncheck all");
10012  $(".' . $cssclass . '").prop(\'checked\', false).trigger(\'change\');
10013  }' . "\n";
10014  if ($calljsfunction) {
10015  $out .= 'if (typeof initCheckForSelect == \'function\') { initCheckForSelect(0, "' . $massactionname . '", "' . $cssclass . '"); } else { console.log("No function initCheckForSelect found. Call won\'t be done."); }';
10016  }
10017  $out .= ' });
10018  $(".' . $cssclass . '").change(function() {
10019  $(this).closest("tr").toggleClass("highlight", this.checked);
10020  });
10021  });
10022  </script>';
10023 
10024  return $out;
10025  }
10026 
10036  public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
10037  {
10038  $out = $this->showFilterButtons();
10039  if ($addcheckuncheckall) {
10040  $out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname);
10041  }
10042  return $out;
10043  }
10044 
10058  public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array(), $info_admin = 1)
10059  {
10060  global $langs, $user;
10061 
10062  $out = '';
10063  $sql = "SELECT rowid, label FROM " . $this->db->prefix() . "c_exp_tax_cat WHERE active = 1";
10064  $sql .= " AND entity IN (0," . getEntity('exp_tax_cat') . ")";
10065  if (!empty($excludeid)) {
10066  $sql .= " AND rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeid)) . ")";
10067  }
10068  $sql .= " ORDER BY label";
10069 
10070  $resql = $this->db->query($sql);
10071  if ($resql) {
10072  $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp maxwidth200">';
10073  if ($useempty) {
10074  $out .= '<option value="0">&nbsp;</option>';
10075  }
10076 
10077  while ($obj = $this->db->fetch_object($resql)) {
10078  $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . $langs->trans($obj->label) . '</option>';
10079  }
10080  $out .= '</select>';
10081  $out .= ajax_combobox('select_' . $htmlname);
10082 
10083  if (!empty($htmlname) && $user->admin && $info_admin) {
10084  $out .= ' ' . info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
10085  }
10086 
10087  if (!empty($target)) {
10088  $sql = "SELECT c.id FROM " . $this->db->prefix() . "c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1";
10089  $resql = $this->db->query($sql);
10090  if ($resql) {
10091  if ($this->db->num_rows($resql) > 0) {
10092  $obj = $this->db->fetch_object($resql);
10093  $out .= '<script nonce="' . getNonce() . '">
10094  $(function() {
10095  $("select[name=' . $target . ']").on("change", function() {
10096  var current_val = $(this).val();
10097  if (current_val == ' . $obj->id . ') {';
10098  if (!empty($default_selected) || !empty($selected)) {
10099  $out .= '$("select[name=' . $htmlname . ']").val("' . ($default_selected > 0 ? $default_selected : $selected) . '");';
10100  }
10101 
10102  $out .= '
10103  $("select[name=' . $htmlname . ']").change();
10104  }
10105  });
10106 
10107  $("select[name=' . $htmlname . ']").change(function() {
10108 
10109  if ($("select[name=' . $target . ']").val() == ' . $obj->id . ') {
10110  // get price of kilometer to fill the unit price
10111  $.ajax({
10112  method: "POST",
10113  dataType: "json",
10114  data: { fk_c_exp_tax_cat: $(this).val(), token: \'' . currentToken() . '\' },
10115  url: "' . (DOL_URL_ROOT . '/expensereport/ajax/ajaxik.php?' . join('&', $params)) . '",
10116  }).done(function( data, textStatus, jqXHR ) {
10117  console.log(data);
10118  if (typeof data.up != "undefined") {
10119  $("input[name=value_unit]").val(data.up);
10120  $("select[name=' . $htmlname . ']").attr("title", data.title);
10121  } else {
10122  $("input[name=value_unit]").val("");
10123  $("select[name=' . $htmlname . ']").attr("title", "");
10124  }
10125  });
10126  }
10127  });
10128  });
10129  </script>';
10130  }
10131  }
10132  }
10133  } else {
10134  dol_print_error($this->db);
10135  }
10136 
10137  return $out;
10138  }
10139 
10148  public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0)
10149  {
10150  global $conf, $langs;
10151 
10152  $out = '';
10153  $sql = "SELECT rowid, range_ik FROM " . $this->db->prefix() . "c_exp_tax_range";
10154  $sql .= " WHERE entity = " . $conf->entity . " AND active = 1";
10155 
10156  $resql = $this->db->query($sql);
10157  if ($resql) {
10158  $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
10159  if ($useempty) {
10160  $out .= '<option value="0"></option>';
10161  }
10162 
10163  while ($obj = $this->db->fetch_object($resql)) {
10164  $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . price($obj->range_ik, 0, $langs, 1, 0) . '</option>';
10165  }
10166  $out .= '</select>';
10167  } else {
10168  dol_print_error($this->db);
10169  }
10170 
10171  return $out;
10172  }
10173 
10184  public function selectExpense($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0)
10185  {
10186  global $langs;
10187 
10188  $out = '';
10189  $sql = "SELECT id, code, label FROM " . $this->db->prefix() . "c_type_fees";
10190  $sql .= " WHERE active = 1";
10191 
10192  $resql = $this->db->query($sql);
10193  if ($resql) {
10194  $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
10195  if ($useempty) {
10196  $out .= '<option value="0"></option>';
10197  }
10198  if ($allchoice) {
10199  $out .= '<option value="-1">' . $langs->trans('AllExpenseReport') . '</option>';
10200  }
10201 
10202  $field = 'code';
10203  if ($useid) {
10204  $field = 'id';
10205  }
10206 
10207  while ($obj = $this->db->fetch_object($resql)) {
10208  $key = $langs->trans($obj->code);
10209  $out .= '<option ' . ($selected == $obj->{$field} ? 'selected="selected"' : '') . ' value="' . $obj->{$field} . '">' . ($key != $obj->code ? $key : $obj->label) . '</option>';
10210  }
10211  $out .= '</select>';
10212  } else {
10213  dol_print_error($this->db);
10214  }
10215 
10216  return $out;
10217  }
10218 
10237  public function selectInvoice($socid = -1, $selected = '', $htmlname = 'invoiceid', $maxlength = 24, $option_only = 0, $show_empty = '1', $discard_closed = 0, $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500', $projectsListId = '', $showproject = 'all', $usertofilter = null)
10238  {
10239  global $user, $conf, $langs;
10240 
10241  require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
10242 
10243  if (is_null($usertofilter)) {
10244  $usertofilter = $user;
10245  }
10246 
10247  $out = '';
10248 
10249  $hideunselectables = false;
10250  if (!empty($conf->global->PROJECT_HIDE_UNSELECTABLES)) {
10251  $hideunselectables = true;
10252  }
10253 
10254  if (empty($projectsListId)) {
10255  if (empty($usertofilter->rights->projet->all->lire)) {
10256  $projectstatic = new Project($this->db);
10257  $projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
10258  }
10259  }
10260 
10261  // Search all projects
10262  $sql = "SELECT f.rowid, f.ref as fref, 'nolabel' as flabel, p.rowid as pid, f.ref,
10263  p.title, p.fk_soc, p.fk_statut, p.public,";
10264  $sql .= ' s.nom as name';
10265  $sql .= ' FROM ' . $this->db->prefix() . 'projet as p';
10266  $sql .= ' LEFT JOIN ' . $this->db->prefix() . 'societe as s ON s.rowid = p.fk_soc,';
10267  $sql .= ' ' . $this->db->prefix() . 'facture as f';
10268  $sql .= " WHERE p.entity IN (" . getEntity('project') . ")";
10269  $sql .= " AND f.fk_projet = p.rowid AND f.fk_statut=0"; //Brouillons seulement
10270  //if ($projectsListId) $sql.= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
10271  //if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
10272  //if ($socid > 0) $sql.= " AND (p.fk_soc=".((int) $socid)." OR p.fk_soc IS NULL)";
10273  $sql .= " ORDER BY p.ref, f.ref ASC";
10274 
10275  $resql = $this->db->query($sql);
10276  if ($resql) {
10277  // Use select2 selector
10278  if (!empty($conf->use_javascript_ajax)) {
10279  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10280  $comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus);
10281  $out .= $comboenhancement;
10282  $morecss = 'minwidth200imp maxwidth500';
10283  }
10284 
10285  if (empty($option_only)) {
10286  $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
10287  }
10288  if (!empty($show_empty)) {
10289  $out .= '<option value="0" class="optiongrey">';
10290  if (!is_numeric($show_empty)) {
10291  $out .= $show_empty;
10292  } else {
10293  $out .= '&nbsp;';
10294  }
10295  $out .= '</option>';
10296  }
10297  $num = $this->db->num_rows($resql);
10298  $i = 0;
10299  if ($num) {
10300  while ($i < $num) {
10301  $obj = $this->db->fetch_object($resql);
10302  // If we ask to filter on a company and user has no permission to see all companies and project is linked to another company, we hide project.
10303  if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && empty($usertofilter->rights->societe->lire)) {
10304  // Do nothing
10305  } else {
10306  if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED) {
10307  $i++;
10308  continue;
10309  }
10310 
10311  $labeltoshow = '';
10312 
10313  if ($showproject == 'all') {
10314  $labeltoshow .= dol_trunc($obj->ref, 18); // Invoice ref
10315  if ($obj->name) {
10316  $labeltoshow .= ' - ' . $obj->name; // Soc name
10317  }
10318 
10319  $disabled = 0;
10320  if ($obj->fk_statut == Project::STATUS_DRAFT) {
10321  $disabled = 1;
10322  $labeltoshow .= ' - ' . $langs->trans("Draft");
10323  } elseif ($obj->fk_statut == Project::STATUS_CLOSED) {
10324  if ($discard_closed == 2) {
10325  $disabled = 1;
10326  }
10327  $labeltoshow .= ' - ' . $langs->trans("Closed");
10328  } elseif ($socid > 0 && (!empty($obj->fk_soc) && $obj->fk_soc != $socid)) {
10329  $disabled = 1;
10330  $labeltoshow .= ' - ' . $langs->trans("LinkedToAnotherCompany");
10331  }
10332  }
10333 
10334  if (!empty($selected) && $selected == $obj->rowid) {
10335  $out .= '<option value="' . $obj->rowid . '" selected';
10336  //if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
10337  $out .= '>' . $labeltoshow . '</option>';
10338  } else {
10339  if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
10340  $resultat = '';
10341  } else {
10342  $resultat = '<option value="' . $obj->rowid . '"';
10343  if ($disabled) {
10344  $resultat .= ' disabled';
10345  }
10346  //if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
10347  //else $labeltoshow.=' ('.$langs->trans("Private").')';
10348  $resultat .= '>';
10349  $resultat .= $labeltoshow;
10350  $resultat .= '</option>';
10351  }
10352  $out .= $resultat;
10353  }
10354  }
10355  $i++;
10356  }
10357  }
10358  if (empty($option_only)) {
10359  $out .= '</select>';
10360  }
10361 
10362  $this->db->free($resql);
10363 
10364  return $out;
10365  } else {
10366  dol_print_error($this->db);
10367  return '';
10368  }
10369  }
10370 
10384  public function selectInvoiceRec($selected = '', $htmlname = 'facrecid', $maxlength = 24, $option_only = 0, $show_empty = '1', $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500')
10385  {
10386  global $user, $conf, $langs;
10387 
10388  $out = '';
10389 
10390  dol_syslog('FactureRec::fetch', LOG_DEBUG);
10391 
10392  $sql = 'SELECT f.rowid, f.entity, f.titre as title, f.suspended, f.fk_soc';
10393  //$sql.= ', el.fk_source';
10394  $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_rec as f';
10395  $sql .= " WHERE f.entity IN (" . getEntity('invoice') . ")";
10396  $sql .= " ORDER BY f.titre ASC";
10397 
10398  $resql = $this->db->query($sql);
10399  if ($resql) {
10400  // Use select2 selector
10401  if (!empty($conf->use_javascript_ajax)) {
10402  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10403  $comboenhancement = ajax_combobox($htmlname, '', 0, $forcefocus);
10404  $out .= $comboenhancement;
10405  $morecss = 'minwidth200imp maxwidth500';
10406  }
10407 
10408  if (empty($option_only)) {
10409  $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
10410  }
10411  if (!empty($show_empty)) {
10412  $out .= '<option value="0" class="optiongrey">';
10413  if (!is_numeric($show_empty)) {
10414  $out .= $show_empty;
10415  } else {
10416  $out .= '&nbsp;';
10417  }
10418  $out .= '</option>';
10419  }
10420  $num = $this->db->num_rows($resql);
10421  if ($num) {
10422  while ($obj = $this->db->fetch_object($resql)) {
10423  $labeltoshow = dol_trunc($obj->title, 18); // Invoice ref
10424 
10425  $disabled = 0;
10426  if (!empty($obj->suspended)) {
10427  $disabled = 1;
10428  $labeltoshow .= ' - ' . $langs->trans("Closed");
10429  }
10430 
10431 
10432  if (!empty($selected) && $selected == $obj->rowid) {
10433  $out .= '<option value="' . $obj->rowid . '" selected';
10434  //if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
10435  $out .= '>' . $labeltoshow . '</option>';
10436  } else {
10437  if ($disabled && ($selected != $obj->rowid)) {
10438  $resultat = '';
10439  } else {
10440  $resultat = '<option value="' . $obj->rowid . '"';
10441  if ($disabled) {
10442  $resultat .= ' disabled';
10443  }
10444  $resultat .= '>';
10445  $resultat .= $labeltoshow;
10446  $resultat .= '</option>';
10447  }
10448  $out .= $resultat;
10449  }
10450  }
10451  }
10452  if (empty($option_only)) {
10453  $out .= '</select>';
10454  }
10455 
10456  print $out;
10457 
10458  $this->db->free($resql);
10459  return $num;
10460  } else {
10461  $this->errors[] = $this->db->lasterror;
10462  return -1;
10463  }
10464  }
10465 
10475  public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array(), $search_component_params_hidden = '')
10476  {
10477  global $langs;
10478 
10479  if ($search_component_params_hidden != '' && !preg_match('/^\‍(.*\‍)$/', $search_component_params_hidden)) { // If $search_component_params_hidden does not start and end with ()
10480  $search_component_params_hidden = '(' . $search_component_params_hidden . ')';
10481  }
10482 
10483  $ret = '';
10484 
10485  $ret .= '<div class="divadvancedsearchfieldcomp inline-block">';
10486  $ret .= '<a href="#" class="dropdownsearch-toggle unsetcolor">';
10487  $ret .= '<span class="fas fa-filter linkobject boxfilter paddingright pictofixedwidth" title="' . dol_escape_htmltag($langs->trans("Filters")) . '" id="idsubimgproductdistribution"></span>';
10488  $ret .= '</a>';
10489 
10490  $ret .= '<div class="divadvancedsearchfieldcompinput inline-block minwidth500 maxwidth300onsmartphone">';
10491 
10492  // Show select fields as tags.
10493  $ret .= '<div name="divsearch_component_params" class="noborderbottom search_component_params inline-block valignmiddle">';
10494 
10495  if ($search_component_params_hidden) {
10496  // Split the criteria on each AND
10497  //var_dump($search_component_params_hidden);
10498 
10499  $nbofchars = dol_strlen($search_component_params_hidden);
10500  $arrayofandtags = array();
10501  $i = 0; $s = '';
10502  $countparenthesis = 0;
10503  while ($i < $nbofchars) {
10504  $char = dol_substr($search_component_params_hidden, $i, 1);
10505 
10506  if ($char == '(') {
10507  $countparenthesis++;
10508  } elseif ($char == ')') {
10509  $countparenthesis--;
10510  }
10511 
10512  if ($countparenthesis == 0) {
10513  $char2 = dol_substr($search_component_params_hidden, $i+1, 1);
10514  $char3 = dol_substr($search_component_params_hidden, $i+2, 1);
10515  if ($char == 'A' && $char2 == 'N' && $char3 == 'D') {
10516  // We found a AND
10517  $arrayofandtags[] = trim($s);
10518  $s = '';
10519  $i+=2;
10520  } else {
10521  $s .= $char;
10522  }
10523  } else {
10524  $s .= $char;
10525  }
10526  $i++;
10527  }
10528  if ($s) {
10529  $arrayofandtags[] = trim($s);
10530  }
10531 
10532  // Show each AND part
10533  foreach ($arrayofandtags as $tmpkey => $tmpval) {
10534  $errormessage = '';
10535  $searchtags = forgeSQLFromUniversalSearchCriteria($tmpval, $errormessage, 1, 1);
10536  if ($errormessage) {
10537  $this->error = 'ERROR in parsing search string: '.$errormessage;
10538  }
10539  // Remove first and last parenthesis but only if first is the opening and last the closing of the same group
10540  include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
10541  $searchtags = removeGlobalParenthesis($searchtags);
10542 
10543  $ret .= '<span class="marginleftonlyshort valignmiddle tagsearch" data-ufilterid="'.($tmpkey+1).'" data-ufilter="'.dol_escape_htmltag($tmpval).'">';
10544  $ret .= '<span class="tagsearchdelete select2-selection__choice__remove" data-ufilterid="'.($tmpkey+1).'">x</span> ';
10545  $ret .= dol_escape_htmltag($searchtags);
10546  $ret .= '</span>';
10547  }
10548  }
10549 
10550  //$ret .= '<button type="submit" class="liste_titre button_search paddingleftonly" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
10551 
10552  //$ret .= search_component_params
10553  //$texttoshow = '<div class="opacitymedium inline-block search_component_searchtext">'.$langs->trans("Search").'</div>';
10554  //$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
10555 
10556  $show_search_component_params_hidden = 1;
10557  if ($show_search_component_params_hidden) {
10558  $ret .= '<input type="hidden" name="show_search_component_params_hidden" value="1">';
10559  }
10560  $ret .= "<!-- We store the full Universal Search String into this field. For example: (t.ref:like:'SO-%') AND ((t.ref:like:'CO-%') OR (t.ref:like:'AA%')) -->";
10561  $ret .= '<input type="hidden" name="search_component_params_hidden" value="' . dol_escape_htmltag($search_component_params_hidden) . '">';
10562  // $ret .= "<!-- sql= ".forgeSQLFromUniversalSearchCriteria($search_component_params_hidden, $errormessage)." -->";
10563 
10564  // For compatibility with forms that show themself the search criteria in addition of this component, we output these fields
10565  foreach ($arrayofcriterias as $criterias) {
10566  foreach ($criterias as $criteriafamilykey => $criteriafamilyval) {
10567  if (in_array('search_' . $criteriafamilykey, $arrayofinputfieldsalreadyoutput)) {
10568  continue;
10569  }
10570  if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) {
10571  continue;
10572  }
10573  if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) {
10574  $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_start">';
10575  $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startyear">';
10576  $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startmonth">';
10577  $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startday">';
10578  $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_end">';
10579  $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endyear">';
10580  $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endmonth">';
10581  $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endday">';
10582  } else {
10583  $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '">';
10584  }
10585  }
10586  }
10587 
10588  $ret .= '</div>';
10589 
10590  $ret .= "<!-- Field to enter a generic filter string: t.ref:like:'SO-%', t.date_creation:<:'20160101', t.date_creation:<:'2016-01-01 12:30:00', t.nature:is:NULL, t.field2:isnot:NULL -->\n";
10591  $ret .= '<input type="text" placeholder="' . $langs->trans("Search") . '" name="search_component_params_input" class="noborderbottom search_component_input" value="">';
10592 
10593  $ret .= '</div>';
10594  $ret .= '</div>';
10595 
10596  $ret .= '<script>
10597  jQuery(".tagsearchdelete").click(function() {
10598  var filterid = $(this).parents().data("ufilterid");
10599  console.log("We click to delete a criteria nb "+filterid);
10600  // TODO Update the search_component_params_hidden with all data-ufilter except the one delete and post page
10601 
10602  });
10603  </script>
10604  ';
10605 
10606 
10607  return $ret;
10608  }
10609 
10619  public function selectModelMail($prefix, $modelType = '', $default = 0, $addjscombo = 0)
10620  {
10621  global $langs, $user;
10622 
10623  $retstring = '';
10624 
10625  $TModels = array();
10626 
10627  include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php';
10628  $formmail = new FormMail($this->db);
10629  $result = $formmail->fetchAllEMailTemplate($modelType, $user, $langs);
10630 
10631  if ($default) {
10632  $TModels[0] = $langs->trans('DefaultMailModel');
10633  }
10634  if ($result > 0) {
10635  foreach ($formmail->lines_model as $model) {
10636  $TModels[$model->id] = $model->label;
10637  }
10638  }
10639 
10640  $retstring .= '<select class="flat" id="select_' . $prefix . 'model_mail" name="' . $prefix . 'model_mail">';
10641 
10642  foreach ($TModels as $id_model => $label_model) {
10643  $retstring .= '<option value="' . $id_model . '"';
10644  $retstring .= ">" . $label_model . "</option>";
10645  }
10646 
10647  $retstring .= "</select>";
10648 
10649  if ($addjscombo) {
10650  $retstring .= ajax_combobox('select_' . $prefix . 'model_mail');
10651  }
10652 
10653  return $retstring;
10654  }
10655 
10667  public function buttonsSaveCancel($save_label = 'Save', $cancel_label = 'Cancel', $morebuttons = array(), $withoutdiv = 0, $morecss = '', $dol_openinpopup = '')
10668  {
10669  global $langs;
10670 
10671  $buttons = array();
10672 
10673  $save = array(
10674  'name' => 'save',
10675  'label_key' => $save_label,
10676  );
10677 
10678  if ($save_label == 'Create' || $save_label == 'Add') {
10679  $save['name'] = 'add';
10680  } elseif ($save_label == 'Modify') {
10681  $save['name'] = 'edit';
10682  }
10683 
10684  $cancel = array(
10685  'name' => 'cancel',
10686  'label_key' => 'Cancel',
10687  );
10688 
10689  !empty($save_label) ? $buttons[] = $save : '';
10690 
10691  if (!empty($morebuttons)) {
10692  $buttons[] = $morebuttons;
10693  }
10694 
10695  !empty($cancel_label) ? $buttons[] = $cancel : '';
10696 
10697  $retstring = $withoutdiv ? '' : '<div class="center">';
10698 
10699  foreach ($buttons as $button) {
10700  $addclass = empty($button['addclass']) ? '' : $button['addclass'];
10701  $retstring .= '<input type="submit" class="button button-' . $button['name'] . ($morecss ? ' ' . $morecss : '') . ' ' . $addclass . '" name="' . $button['name'] . '" value="' . dol_escape_htmltag($langs->trans($button['label_key'])) . '">';
10702  }
10703  $retstring .= $withoutdiv ? '' : '</div>';
10704 
10705  if ($dol_openinpopup) {
10706  $retstring .= '<!-- buttons are shown into a $dol_openinpopup=' . $dol_openinpopup . ' context, so we enable the close of dialog on cancel -->' . "\n";
10707  $retstring .= '<script nonce="' . getNonce() . '">';
10708  $retstring .= 'jQuery(".button-cancel").click(function(e) {
10709  e.preventDefault(); console.log(\'We click on cancel in iframe popup ' . $dol_openinpopup . '\');
10710  window.parent.jQuery(\'#idfordialog' . $dol_openinpopup . '\').dialog(\'close\');
10711  });';
10712  $retstring .= '</script>';
10713  }
10714 
10715  return $retstring;
10716  }
10717 }
ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLength=2, $autoselect=0, $ajaxoptions=array(), $moreparams='')
Generic function that return javascript to add to a page to transform a common input field into an au...
Definition: ajax.lib.php:47
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition: ajax.lib.php:449
ajax_multiautocompleter($htmlname, $fields, $url, $option='', $minLength=2, $autoselect=0)
Generic function that return javascript to add to a page to transform a common input field into an au...
Definition: ajax.lib.php:297
ajax_event($htmlname, $events)
Add event management script.
Definition: ajax.lib.php:550
Class to manage bank accounts.
Class to manage categories.
Class to manage contact/addresses.
Class to manage a WYSIWYG editor.
const STATUS_OPEN_INTERNAL
Warehouse open and only operations for stock transfers/corrections allowed (not for customer shipping...
const STATUS_OPEN_ALL
Warehouse open and any operations are allowed (customer shipping, supplier dispatch,...
const STATUS_CLOSED
Warehouse closed, inactive.
Class to manage standard extra languages.
Class to manage invoices.
Class to manage generation of HTML components Only common components must be here.
selectModelMail($prefix, $modelType='', $default=0, $addjscombo=0)
selectModelMail
showFilterButtons($pos='')
Return HTML to show the search and clear seach button.
selectyesno($htmlname, $value='', $option=0, $disabled=false, $useempty=0, $addjscombo=0, $morecss='', $labelyes='Yes', $labelno='No')
Return an html string with a select combo box to choose yes or no.
load_cache_vatrates($country_code)
Load into the cache vat rates of a country.
select_type_of_lines($selected='', $htmlname='type', $showempty=0, $hidetext=0, $forceall=0)
Return list of types of lines (product or service) Example: 0=product, 1=service, 9=other (for extern...
select_address($selected, $socid, $htmlname='address_id', $showempty=0)
Return list of delivery address.
formInputReason($page, $selected='', $htmlname='demandreason', $addempty=0)
Output HTML form to select list of input reason (events that triggered an object creation,...
editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata='string', $editvalue='', $extObject=null, $custommsg=null, $moreparam='', $notabletag=1, $formatfunc='', $paramid='id', $gm='auto', $moreoptions=array(), $editaction='')
Output value of a field for an editable field.
select_contacts($socid, $selected='', $htmlname='contactid', $showempty=0, $exclude='', $limitto='', $showfunction=0, $morecss='', $showsoc=0, $forcecombo=0, $events=array(), $options_only=false, $moreparam='', $htmlid='')
Return list of all contacts (for a third party or all)
form_availability($page, $selected='', $htmlname='availability', $addempty=0)
Show a form to select a delivery delay.
select_produits_fournisseurs($socid, $selected='', $htmlname='productid', $filtertype='', $filtre='', $ajaxoptions=array(), $hidelabel=0, $alsoproductwithnosupplierprice=0, $morecss='', $placeholder='')
Return list of products for customer (in Ajax if Ajax activated or go to select_produits_fournisseurs...
selectMassAction($selected, $arrayofaction, $alwaysvisible=0, $name='massaction', $cssclass='checkforselect')
Generate select HTML to choose massaction.
formconfirm($page, $title, $question, $action, $formquestion='', $selectedchoice='', $useajax=0, $height=0, $width=500, $disableformtag=0, $labelbuttonyes='Yes', $labelbuttonno='No')
form_multicurrency_code($page, $selected='', $htmlname='multicurrency_code')
Show form with multicurrency code.
selectcontacts($socid, $selected='', $htmlname='contactid', $showempty=0, $exclude='', $limitto='', $showfunction=0, $morecss='', $options_only=false, $showsoc=0, $forcecombo=0, $events=array(), $moreparam='', $htmlid='', $multiple=false, $disableifempty=0)
Return HTML code of the SELECT of list of all contacts (for a third party or all).
showFilterAndCheckAddButtons($addcheckuncheckall=0, $cssclass='checkforaction', $calljsfunction=0, $massactionname="massaction")
Return HTML to show the search and clear seach button.
select_company($selected='', $htmlname='socid', $filter='', $showempty='', $showtype=0, $forcecombo=0, $events=array(), $limit=0, $morecss='minwidth100', $moreparam='', $selected_input_value='', $hidelabel=1, $ajaxoptions=array(), $multiple=false, $excludeids=array(), $showcode=0)
Output html form to select a third party.
form_multicurrency_rate($page, $rate='', $htmlname='multicurrency_tx', $currency='')
Show form with multicurrency rate.
textwithpicto($text, $htmltext, $direction=1, $type='help', $extracss='', $noencodehtmltext=0, $notabs=3, $tooltiptrigger='', $forcenowrap=0)
Show a text with a picto and a tooltip on picto.
select_users($selected='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include='', $enableonly='', $force_entity='0')
Return the HTML select list of users.
selectInputReason($selected='', $htmlname='demandreasonid', $exclude='', $addempty=0, $morecss='', $notooltip=0)
Return list of input reason (events that triggered an object creation, like after sending an emailing...
select_incoterms($selected='', $location_incoterms='', $page='', $htmlname='incoterm_id', $htmloption='', $forcecombo=1, $events=array(), $disableautocomplete=0)
Return select list of incoterms.
select_dolgroups($selected='', $htmlname='groupid', $show_empty=0, $exclude='', $disabled=0, $include='', $enableonly='', $force_entity='0', $multiple=false, $morecss='')
Return select list of groups.
selectDate($set_time='', $prefix='re', $h=0, $m=0, $empty=0, $form_name="", $d=1, $addnowlink=0, $disabled=0, $fullday='', $addplusone='', $adddateof='', $openinghours='', $stepminutes=1, $labeladddateof='', $placeholder='', $gm='auto')
Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minute...
select_types_paiements($selected='', $htmlname='paiementtype', $filtertype='', $format=0, $empty=1, $noadmininfo=0, $maxlength=0, $active=1, $morecss='', $nooutput=0)
select_currency($selected='', $htmlname='currency_id')
Retourne la liste des devises, dans la langue de l'utilisateur.
formSelectTransportMode($page, $selected='', $htmlname='transport_mode_id', $active=1, $addempty=0)
Show form with transport mode.
selectShippingMethod($selected='', $htmlname='shipping_method_id', $filtre='', $useempty=0, $moreattrib='', $noinfoadmin=0, $morecss='')
Return a HTML select list of shipping mode.
form_users($page, $selected='', $htmlname='userid', $exclude='', $include='')
Show a select form to choose a user.
formSelectShippingMethod($page, $selected='', $htmlname='shipping_method_id', $addempty=0)
Display form to select shipping mode.
form_contacts($page, $societe, $selected='', $htmlname='contactid')
Show forms to select a contact.
buttonsSaveCancel($save_label='Save', $cancel_label='Cancel', $morebuttons=array(), $withoutdiv=0, $morecss='', $dol_openinpopup='')
Output the buttons to submit a creation/edit form.
selectMultiCurrency($selected='', $htmlname='multicurrency_code', $useempty=0, $filter='', $excludeConfCurrency=false, $morecss='')
Return array of currencies in user language.
load_cache_availability()
select_bom($selected='', $htmlname='bom_id', $limit=0, $status=1, $type=0, $showempty='1', $morecss='', $nooutput='', $forcecombo=0, $TProducts=[])
Return list of BOM for customer in Ajax if Ajax activated or go to select_produits_list.
static selectarray($htmlname, $array, $id='', $show_empty=0, $key_in_label=0, $value_as_key=0, $moreparam='', $translate=0, $maxlen=0, $disabled=0, $sort='', $morecss='minwidth75', $addjscombo=1, $moreparamonempty='', $disablebademail=0, $nohtmlescape=0)
Return a HTML select string, built from an array of key+value.
selectMembersList($selected='', $htmlname='adherentid', $filtertype='', $limit=20, $filterkey='', $status=1, $outputmode=0, $showempty='1', $forcecombo=0, $morecss='')
Return list of adherents.
select_type_fees($selected='', $htmlname='type', $showempty=0)
Return list of types of notes.
selectInvoiceRec($selected='', $htmlname='facrecid', $maxlength=24, $option_only=0, $show_empty='1', $forcefocus=0, $disabled=0, $morecss='maxwidth500')
Output a combo list with invoices qualified for a third party.
editInPlace($object, $value, $htmlname, $condition, $inputType='textarea', $editvalue=null, $extObject=null, $custommsg=null)
Output edit in place form.
selectUnits($selected='', $htmlname='units', $showempty=0, $unit_type='')
select_produits_fournisseurs_list($socid, $selected='', $htmlname='productid', $filtertype='', $filtre='', $filterkey='', $statut=-1, $outputmode=0, $limit=100, $alsoproductwithnosupplierprice=0, $morecss='', $showstockinlist=0, $placeholder='')
Return list of suppliers products.
showLinkedObjectBlock($object, $morehtmlright='', $compatibleImportElementsList=false, $title='RelatedObjects')
Show linked object block.
selectTicketsList($selected='', $htmlname='ticketid', $filtertype='', $limit=20, $filterkey='', $status=1, $outputmode=0, $showempty='1', $forcecombo=0, $morecss='')
Return list of tickets.
form_modes_reglement($page, $selected='', $htmlname='mode_reglement_id', $filtertype='', $active=1, $addempty=0, $type='', $nooutput=0)
Show form with payment mode.
constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel=0, $filterkey='', $novirtualstock=0)
Function to forge the string with OPTIONs of SELECT.
select_conditions_paiements($selected=0, $htmlname='condid', $filtertype=-1, $addempty=0, $noinfoadmin=0, $morecss='', $deposit_percent=-1)
print list of payment modes.
select_product_fourn_price($productid, $htmlname='productfournpriceid', $selected_supplier='')
Return list of suppliers prices for a product.
showLinkToObjectBlock($object, $restrictlinksto=array(), $excludelinksto=array())
Show block with links to link to other objects.
form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter='', $maxvalue=0, $more='', $hidelist=0, $discount_type=0)
Show a select box with available absolute discounts.
select_all_categories($type, $selected='', $htmlname="parent", $maxlength=64, $markafterid=0, $outputmode=0, $include=0, $morecss='')
Return list of categories having choosed type.
selectTransportMode($selected='', $htmlname='transportmode', $format=0, $empty=1, $noadmininfo=0, $maxlength=0, $active=1, $morecss='')
static showphoto($modulepart, $object, $width=100, $height=0, $caneditfield=0, $cssclass='photowithmargin', $imagesize='', $addlinktofullsize=1, $cache=0, $forcecapture='', $noexternsourceoverwrite=0)
selectProjectsList($selected='', $htmlname='projectid', $filtertype='', $limit=20, $filterkey='', $status=1, $outputmode=0, $showempty='1', $forcecombo=0, $morecss='')
Return list of projects.
form_conditions_reglement($page, $selected='', $htmlname='cond_reglement_id', $addempty=0, $type='', $filtertype=-1, $deposit_percent=-1, $nooutput=0)
Show a form to select payment conditions.
selectSituationInvoices($selected='', $socid=0)
Creates HTML last in cycle situation invoices selector.
loadCacheInputReason()
selectPriceBaseType($selected='', $htmlname='price_base_type', $addjscombo=0)
Selection HT or TTC.
load_cache_transport_mode()
select_remises($selected, $htmlname, $filter, $socid, $maxvalue=0)
Return HTML combo list of absolute discounts.
showbarcode(&$object, $width=100, $morecss='')
Return HTML code to output a barcode.
select_dolusers_forevent($action='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include='', $enableonly='', $force_entity='0', $maxlength=0, $showstatus=0, $morefilter='', $showproperties=0, $listofuserid=array(), $listofcontactid=array(), $listofotherid=array())
Return select list of users.
form_confirm($page, $title, $question, $action, $formquestion='', $selectedchoice="", $useajax=0, $height=170, $width=500)
load_cache_conditions_paiements()
form_project($page, $socid, $selected='', $htmlname='projectid', $discard_closed=0, $maxlength=20, $forcefocus=0, $nooutput=0, $textifnoproject='', $morecss='')
Show a form to select a project.
selectExpenseCategories($selected='', $htmlname='fk_c_exp_tax_cat', $useempty=0, $excludeid=array(), $target='', $default_selected=0, $params=array(), $info_admin=1)
Return HTML to show the select of expense categories.
select_produits_list($selected='', $htmlname='productid', $filtertype='', $limit=20, $price_level=0, $filterkey='', $status=1, $finished=2, $outputmode=0, $socid=0, $showempty='1', $forcecombo=0, $morecss='', $hidepriceinlabel=0, $warehouseStatus='', $status_purchase=-1)
Return list of products for a customer.
select_country($selected='', $htmlname='country_id', $htmloption='', $maxlength=0, $morecss='minwidth300', $usecodeaskey='', $showempty=1, $disablefavorites=0, $addspecialentries=0, $exclude_country_code=array(), $hideflags=0)
Return combo list of activated countries, into language of user.
form_date($page, $selected, $htmlname, $displayhour=0, $displaymin=0, $nooutput=0, $type='')
Show a form + html select a date.
showCheckAddButtons($cssclass='checkforaction', $calljsfunction=0, $massactionname="massaction")
Return HTML to show the search and clear search button.
__construct($db)
Constructor.
select_thirdparty_list($selected='', $htmlname='socid', $filter='', $showempty='', $showtype=0, $forcecombo=0, $events=array(), $filterkey='', $outputmode=0, $limit=0, $morecss='minwidth100', $moreparam='', $multiple=false, $excludeids=array(), $showcode=0)
Output html form to select a third party.
select_export_model($selected='', $htmlname='exportmodelid', $type='', $useempty=0)
Return list of export templates.
selectDateToDate($set_time='', $set_time_end='', $prefix='re', $empty=0, $forcenewline=0)
Show 2 HTML widget to input a date or combo list for day, month, years and optionaly hours and minute...
textwithtooltip($text, $htmltext, $tooltipon=1, $direction=0, $img='', $extracss='', $notabs=3, $incbefore='', $noencodehtmltext=0, $tooltiptrigger='', $forcenowrap=0)
Show a text and picto with tooltip on text or picto.
searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput=array(), $search_component_params_hidden='')
Output the component to make advanced search criteries.
getSelectConditionsPaiements($selected=0, $htmlname='condid', $filtertype=-1, $addempty=0, $noinfoadmin=0, $morecss='', $deposit_percent=-1)
Return list of payment modes.
widgetForTranslation($fieldname, $object, $perm, $typeofdata='string', $check='', $morecss='')
Output edit in place form.
select_produits($selected='', $htmlname='productid', $filtertype='', $limit=0, $price_level=0, $status=1, $finished=2, $selected_input_value='', $hidelabel=0, $ajaxoptions=array(), $socid=0, $showempty='1', $forcecombo=0, $morecss='', $hidepriceinlabel=0, $warehouseStatus='', $selected_combinations=null, $nooutput=0, $status_purchase=-1)
Return list of products for customer in Ajax if Ajax activated or go to select_produits_list.
load_cache_types_fees()
Load into cache cache_types_fees, array of types of fees.
static multiselectarray($htmlname, $array, $selected=array(), $key_in_label=0, $value_as_key=0, $morecss='', $translate=0, $width=0, $moreattrib='', $elemtype='', $placeholder='', $addjscombo=-1)
Show a multiselect form from an array.
load_tva($htmlname='tauxtva', $selectedrate='', $societe_vendeuse='', $societe_acheteuse='', $idprod=0, $info_bits=0, $type='', $options_only=false, $mode=0)
Output an HTML select vat rate.
form_thirdparty($page, $selected='', $htmlname='socid', $filter='', $showempty=0, $showtype=0, $forcecombo=0, $events=array(), $nooutput=0, $excludeids=array(), $textifnothirdparty='')
Output html select to select thirdparty.
selectEstablishments($selected='', $htmlname='entity', $status=0, $filtre='', $useempty=0, $moreattrib='')
Return a HTML select list of establishment.
formSelectAccount($page, $selected='', $htmlname='fk_account', $addempty=0)
Display form to select bank account.
editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata='string', $moreparam='', $fieldrequired=0, $notabletag=0, $paramid='id', $help='')
Output key field for an editable field.
select_dolusers($selected='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include='', $enableonly='', $force_entity='0', $maxlength=0, $showstatus=0, $morefilter='', $show_every=0, $enableonlytext='', $morecss='', $notdisabled=0, $outputmode=0, $multiple=false, $forcecombo=0)
Return select list of users.
showCategories($id, $type, $rendermode=0, $nolink=0)
Render list of categories linked to object with id $id and type $type.
load_cache_types_paiements()
selectForForms($objectdesc, $htmlname, $preselectedvalue, $showempty='', $searchkey='', $placeholder='', $morecss='', $moreparams='', $forcecombo=0, $disabled=0, $selected_input_value='')
Generic method to select a component from a combo list.
selectAvailabilityDelay($selected='', $htmlname='availid', $filtertype='', $addempty=0, $morecss='')
selectCurrency($selected='', $htmlname='currency_id', $mode=0, $useempty='')
Retourne la liste des devises, dans la langue de l'utilisateur.
select_date($set_time='', $prefix='re', $h=0, $m=0, $empty=0, $form_name="", $d=1, $addnowlink=0, $nooutput=0, $disabled=0, $fullday='', $addplusone='', $adddateof='')
Show a HTML widget to input a date or combo list for day, month, years and optionaly hours and minute...
select_comptes($selected='', $htmlname='accountid', $status=0, $filtre='', $useempty=0, $moreattrib='', $showcurrency=0, $morecss='', $nooutput=0)
Return a HTML select list of bank accounts.
showrefnav($object, $paramid, $morehtml='', $shownav=1, $fieldid='rowid', $fieldref='ref', $morehtmlref='', $moreparam='', $nodbprefix=0, $morehtmlleft='', $morehtmlstatus='', $morehtmlright='')
Return a HTML area with the reference of object and a navigation bar for a business object Note: To c...
Classe permettant la generation du formulaire html d'envoi de mail unitaire Usage: $formail = new For...
Class to manage building of HTML components.
Class to manage hooks.
Class to parse product price expressions.
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
const TYPE_SERVICE
Service.
Class to manage projects.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
Definition: user.class.php:48
currency_name($code_iso, $withcode='', $outputlangs=null)
Return label of currency or code+label.
getCountry($searchkey, $withcode='', $dbtouse=0, $outputlangs='', $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
isInEEC($object)
Return if a country of an object is inside the EEC (European Economic Community)
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
Definition: card.php:143
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition: date.lib.php:83
removeGlobalParenthesis($string)
Remove first and last parenthesis but only if first is the opening and last the closing of the same g...
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formated for view output Used into pdf and HTML pages.
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
load_fiche_titre($titre, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
img_help($usehelpcursor=1, $usealttitle=1)
Show help logo with cursor "?".
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
currentToken()
Return the value of token currently saved into session with name 'token'.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0)
Show EMail link formatted for HTML output.
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0)
Format phone numbers according to country.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='')
Return an id or code from a code or id.
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array(), $allowlink=0)
Clean a string to keep only desirable HTML tags.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by second index function, which produces ascending (default) or descending output...
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags=array('textarea'), $cleanalsosomestyles=0)
Clean a string from some undesirable HTML tags.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
getImageFileNameForSize($file, $extName, $extImgTarget='')
Return the filename of file to get the thumbs.
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
Return URL we can use for advanced preview links.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dolIsAllowedForPreview($file)
Return if a file is qualified for preview.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='')
Show information for admin users or standard users.
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='float')
Show Url link.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
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.
getNonce()
Return a random string to be used as a nonce value for js.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
img_edit($titlealt='default', $float=0, $other='')
Show logo editer/modifier fiche.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
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...
$formconfirm
if ($action == 'delbookkeepingyear') {
testSqlAndScriptInject($val, $type)
Security: WAF layer for SQL Injection and XSS Injection (scripts) protection (Filters on GET,...
Definition: main.inc.php:89
table tableforfield button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
Definition: style.css.php:848
measuringUnitString($unit, $measuring_style='', $scale='', $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:120
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:123
getMaxFileSizeArray()
Return the max allowed for file upload.
print *****$script_file(".$version.") pid code
! Closing after partial payment: discount_vat, badcustomer or badsupplier, bankcharge,...