dolibarr  21.0.0-alpha
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-2023 Alexandre Spangaro <aspangaro@open-dsi.fr>
19  * Copyright (C) 2018-2022 Ferran Marcet <fmarcet@2byte.es>
20  * Copyright (C) 2018-2024 Frédéric France <frederic.france@free.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  * Copyright (C) 2023 Nick Fragoulis
26  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
27  *
28  * This program is free software; you can redistribute it and/or modify
29  * it under the terms of the GNU General Public License as published by
30  * the Free Software Foundation; either version 3 of the License, or
31  * (at your option) any later version.
32  *
33  * This program is distributed in the hope that it will be useful,
34  * but WITHOUT ANY WARRANTY; without even the implied warranty of
35  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36  * GNU General Public License for more details.
37  *
38  * You should have received a copy of the GNU General Public License
39  * along with this program. If not, see <https://www.gnu.org/licenses/>.
40  */
41 
55 class Form
56 {
60  public $db;
61 
65  public $error = '';
66 
70  public $errors = array();
71 
72  // Some properties used to return data by some methods
74  public $result;
76  public $num;
77 
78  // Cache arrays
79  public $cache_types_paiements = array();
80  public $cache_conditions_paiements = array();
81  public $cache_transport_mode = array();
82  public $cache_availability = array();
83  public $cache_demand_reason = array();
84  public $cache_types_fees = array();
85  public $cache_vatrates = array();
86  public $cache_invoice_subtype = array();
87 
88 
94  public function __construct($db)
95  {
96  $this->db = $db;
97  }
98 
115  public function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata = 'string', $moreparam = '', $fieldrequired = 0, $notabletag = 0, $paramid = 'id', $help = '')
116  {
117  global $langs;
118 
119  $ret = '';
120 
121  // TODO change for compatibility
122  if (getDolGlobalString('MAIN_USE_JQUERY_JEDITABLE') && !preg_match('/^select;/', $typeofdata)) {
123  if (!empty($perm)) {
124  $tmp = explode(':', $typeofdata);
125  $ret .= '<div class="editkey_' . $tmp[0] . (!empty($tmp[1]) ? ' ' . $tmp[1] : '') . '" id="' . $htmlname . '">';
126  if ($fieldrequired) {
127  $ret .= '<span class="fieldrequired">';
128  }
129  if ($help) {
130  $ret .= $this->textwithpicto($langs->trans($text), $help);
131  } else {
132  $ret .= $langs->trans($text);
133  }
134  if ($fieldrequired) {
135  $ret .= '</span>';
136  }
137  $ret .= '</div>' . "\n";
138  } else {
139  if ($fieldrequired) {
140  $ret .= '<span class="fieldrequired">';
141  }
142  if ($help) {
143  $ret .= $this->textwithpicto($langs->trans($text), $help);
144  } else {
145  $ret .= $langs->trans($text);
146  }
147  if ($fieldrequired) {
148  $ret .= '</span>';
149  }
150  }
151  } else {
152  if (empty($notabletag) && $perm) {
153  $ret .= '<table class="nobordernopadding centpercent"><tr><td class="nowrap">';
154  }
155  if ($fieldrequired) {
156  $ret .= '<span class="fieldrequired">';
157  }
158  if ($help) {
159  $ret .= $this->textwithpicto($langs->trans($text), $help);
160  } else {
161  $ret .= $langs->trans($text);
162  }
163  if ($fieldrequired) {
164  $ret .= '</span>';
165  }
166  if (!empty($notabletag)) {
167  $ret .= ' ';
168  }
169  if (empty($notabletag) && $perm) {
170  $ret .= '</td>';
171  }
172  if (empty($notabletag) && $perm) {
173  $ret .= '<td class="right">';
174  }
175  if ($htmlname && GETPOST('action', 'aZ09') != 'edit' . $htmlname && $perm) {
176  $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>';
177  }
178  if (!empty($notabletag) && $notabletag == 1) {
179  if ($text) {
180  $ret .= ' : ';
181  } else {
182  $ret .= ' ';
183  }
184  }
185  if (!empty($notabletag) && $notabletag == 3) {
186  $ret .= ' ';
187  }
188  if (empty($notabletag) && $perm) {
189  $ret .= '</td>';
190  }
191  if (empty($notabletag) && $perm) {
192  $ret .= '</tr></table>';
193  }
194  }
195 
196  return $ret;
197  }
198 
222  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 = '')
223  {
224  global $conf, $langs;
225 
226  $ret = '';
227 
228  // Check parameters
229  if (empty($typeofdata)) {
230  return 'ErrorBadParameter typeofdata is empty';
231  }
232  // Clean parameter $typeofdata
233  if ($typeofdata == 'datetime') {
234  $typeofdata = 'dayhour';
235  }
236  $reg = array();
237  if (preg_match('/^(\w+)\‍((\d+)\‍)$/', $typeofdata, $reg)) {
238  if ($reg[1] == 'varchar') {
239  $typeofdata = 'string';
240  } elseif ($reg[1] == 'int') {
241  $typeofdata = 'numeric';
242  } else {
243  return 'ErrorBadParameter ' . $typeofdata;
244  }
245  }
246 
247  // When option to edit inline is activated
248  if (getDolGlobalString('MAIN_USE_JQUERY_JEDITABLE') && !preg_match('/^select;|day|datepicker|dayhour|datehourpicker/', $typeofdata)) { // TODO add jquery timepicker and support select
249  $ret .= $this->editInPlace($object, $value, $htmlname, $perm, $typeofdata, $editvalue, $extObject, $custommsg);
250  } else {
251  if ($editaction == '') {
252  $editaction = GETPOST('action', 'aZ09');
253  }
254  $editmode = ($editaction == 'edit' . $htmlname);
255  if ($editmode) { // edit mode
256  $ret .= "\n";
257  $ret .= '<form method="post" action="' . $_SERVER["PHP_SELF"] . ($moreparam ? '?' . $moreparam : '') . '">';
258  $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
259  $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
260  $ret .= '<input type="hidden" name="' . $paramid . '" value="' . $object->id . '">';
261  if (empty($notabletag)) {
262  $ret .= '<table class="nobordernopadding centpercent">';
263  }
264  if (empty($notabletag)) {
265  $ret .= '<tr><td>';
266  }
267  if (preg_match('/^(string|safehtmlstring|email|phone|url)/', $typeofdata)) {
268  $tmp = explode(':', $typeofdata);
269  $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($editvalue ? $editvalue : $value) . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
270  } elseif (preg_match('/^(integer)/', $typeofdata)) {
271  $tmp = explode(':', $typeofdata);
272  $valuetoshow = price2num($editvalue ? $editvalue : $value, 0);
273  $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . $valuetoshow . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
274  } elseif (preg_match('/^(numeric|amount)/', $typeofdata)) {
275  $tmp = explode(':', $typeofdata);
276  $valuetoshow = price2num($editvalue ? $editvalue : $value);
277  $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($valuetoshow != '' ? price($valuetoshow) : '') . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
278  } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
279  $tmp = explode(':', $typeofdata);
280  $ret .= '<input type="checkbox" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($value ? $value : 'on') . '"' . ($value ? ' checked' : '') . (empty($tmp[1]) ? '' : $tmp[1]) . '/>';
281  } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) { // if wysiwyg is enabled $typeofdata = 'ckeditor'
282  $tmp = explode(':', $typeofdata);
283  $cols = (empty($tmp[2]) ? '' : $tmp[2]);
284  $morealt = '';
285  if (preg_match('/%/', $cols)) {
286  $morealt = ' style="width: ' . $cols . '"';
287  $cols = '';
288  }
289  $valuetoshow = ($editvalue ? $editvalue : $value);
290  $ret .= '<textarea id="' . $htmlname . '" name="' . $htmlname . '" wrap="soft" rows="' . (empty($tmp[1]) ? '20' : $tmp[1]) . '"' . ($cols ? ' cols="' . $cols . '"' : 'class="quatrevingtpercent"') . $morealt . '" autofocus>';
291  // textarea convert automatically entities chars into simple chars.
292  // 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 wysiwyg is off.
293  $valuetoshow = str_replace('&', '&amp;', $valuetoshow);
294  $ret .= dol_htmlwithnojs(dol_string_neverthesehtmltags($valuetoshow, array('textarea')));
295  $ret .= '</textarea>';
296  } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
297  $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
298  $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
299  $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
300  $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
301  } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
302  $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
303  $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
304  $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
305  $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
306  } elseif (preg_match('/^select;/', $typeofdata)) {
307  $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
308  $arraylist = array();
309  foreach ($arraydata as $val) {
310  $tmp = explode(':', $val);
311  $tmpkey = str_replace('|', ':', $tmp[0]);
312  $arraylist[$tmpkey] = $tmp[1];
313  }
314  $ret .= $this->selectarray($htmlname, $arraylist, $value);
315  } elseif (preg_match('/^link/', $typeofdata)) {
316  // TODO Not yet implemented. See code for extrafields
317  } elseif (preg_match('/^ckeditor/', $typeofdata)) {
318  $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser
319  require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
320  $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]));
321  $ret .= $doleditor->Create(1);
322  } elseif ($typeofdata == 'asis') {
323  $ret .= ($editvalue ? $editvalue : $value);
324  }
325  if (empty($notabletag)) {
326  $ret .= '</td>';
327  }
328 
329  // Button save-cancel
330  if (empty($notabletag)) {
331  $ret .= '<td>';
332  }
333  //else $ret.='<div class="clearboth"></div>';
334  $ret .= '<input type="submit" class="smallpaddingimp button' . (empty($notabletag) ? '' : ' ') . '" name="modify" value="' . $langs->trans("Modify") . '">';
335  if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) {
336  $ret .= '<br>' . "\n";
337  }
338  $ret .= '<input type="submit" class="smallpaddingimp button button-cancel' . (empty($notabletag) ? '' : ' ') . '" name="cancel" value="' . $langs->trans("Cancel") . '">';
339  if (empty($notabletag)) {
340  $ret .= '</td>';
341  }
342 
343  if (empty($notabletag)) {
344  $ret .= '</tr></table>' . "\n";
345  }
346  $ret .= '</form>' . "\n";
347  } else { // view mode
348  if (preg_match('/^email/', $typeofdata)) {
349  $ret .= dol_print_email($value, 0, 0, 0, 0, 1);
350  } elseif (preg_match('/^phone/', $typeofdata)) {
351  $ret .= dol_print_phone($value, '_blank', 32, 1);
352  } elseif (preg_match('/^url/', $typeofdata)) {
353  $ret .= dol_print_url($value, '_blank', 32, 1);
354  } elseif (preg_match('/^(amount|numeric)/', $typeofdata)) {
355  $ret .= ($value != '' ? price($value, 0, $langs, 0, -1, -1, $conf->currency) : '');
356  } elseif (preg_match('/^checkbox/', $typeofdata)) {
357  $tmp = explode(':', $typeofdata);
358  $ret .= '<input type="checkbox" disabled id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($value ? ' checked' : '') . ($tmp[1] ? $tmp[1] : '') . '/>';
359  } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {
361  } elseif (preg_match('/^(safehtmlstring|restricthtml)/', $typeofdata)) { // 'restricthtml' is not an allowed type for editfieldval. Value is 'safehtmlstring'
363  } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
364  $ret .= '<span class="valuedate">' . dol_print_date($value, 'day', $gm) . '</span>';
365  } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
366  $ret .= '<span class="valuedate">' . dol_print_date($value, 'dayhour', $gm) . '</span>';
367  } elseif (preg_match('/^select;/', $typeofdata)) {
368  $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
369  $arraylist = array();
370  foreach ($arraydata as $val) {
371  $tmp = explode(':', $val);
372  $arraylist[$tmp[0]] = $tmp[1];
373  }
374  $ret .= $arraylist[$value];
375  if ($htmlname == 'fk_product_type') {
376  if ($value == 0) {
377  $ret = img_picto($langs->trans("Product"), 'product', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
378  } else {
379  $ret = img_picto($langs->trans("Service"), 'service', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
380  }
381  }
382  } elseif (preg_match('/^ckeditor/', $typeofdata)) {
383  $tmpcontent = dol_htmlentitiesbr($value);
384  if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
385  $firstline = preg_replace('/<br>.*/', '', $tmpcontent);
386  $firstline = preg_replace('/[\n\r].*/', '', $firstline);
387  $tmpcontent = $firstline . ((strlen($firstline) != strlen($tmpcontent)) ? '...' : '');
388  }
389  // We don't use dol_escape_htmltag to get the html formatting active, but this need we must also
390  // clean data from some dangerous html
391  $ret .= dol_string_onlythesehtmltags(dol_htmlentitiesbr($tmpcontent));
392  } else {
393  if (empty($moreoptions['valuealreadyhtmlescaped'])) {
394  $ret .= dol_escape_htmltag($value);
395  } else {
396  $ret .= $value; // $value must be already html escaped.
397  }
398  }
399 
400  // Custom format if parameter $formatfunc has been provided
401  if ($formatfunc && method_exists($object, $formatfunc)) {
402  $ret = $object->$formatfunc($ret);
403  }
404  }
405  }
406  return $ret;
407  }
408 
420  public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '')
421  {
422  global $conf, $langs, $extralanguages;
423 
424  $result = '';
425 
426  // List of extra languages
427  $arrayoflangcode = array();
428  if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
429  $arrayoflangcode[] = getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE');
430  }
431 
432  if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
433  if (!is_object($extralanguages)) {
434  include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
435  $extralanguages = new ExtraLanguages($this->db);
436  }
437  $extralanguages->fetch_name_extralanguages('societe');
438 
439  if (!is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) {
440  return ''; // No extralang field to show
441  }
442 
443  $result .= '<!-- Widget for translation -->' . "\n";
444  $result .= '<div class="inline-block paddingleft image-' . $object->element . '-' . $fieldname . '">';
445  $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', false, 0, 0, '', 'fa-15 editfieldlang');
446  $result .= $s;
447  $result .= '</div>';
448 
449  $result .= '<div class="inline-block hidden field-' . $object->element . '-' . $fieldname . '">';
450 
451  $resultforextrlang = '';
452  foreach ($arrayoflangcode as $langcode) {
453  $valuetoshow = GETPOSTISSET('field-' . $object->element . "-" . $fieldname . "-" . $langcode) ? GETPOST('field-' . $object->element . '-' . $fieldname . "-" . $langcode, $check) : '';
454  if (empty($valuetoshow)) {
455  $object->fetchValuesForExtraLanguages();
456  //var_dump($object->array_languages);
457  $valuetoshow = $object->array_languages[$fieldname][$langcode];
458  }
459 
460  $s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"');
461  $resultforextrlang .= $s;
462 
463  // TODO Use the showInputField() method of ExtraLanguages object
464  if ($typeofdata == 'textarea') {
465  $resultforextrlang .= '<textarea name="field-' . $object->element . "-" . $fieldname . "-" . $langcode . '" id="' . $fieldname . "-" . $langcode . '" class="' . $morecss . '" rows="' . ROWS_2 . '" wrap="soft">';
466  $resultforextrlang .= $valuetoshow;
467  $resultforextrlang .= '</textarea>';
468  } else {
469  $resultforextrlang .= '<input type="text" class="inputfieldforlang ' . ($morecss ? ' ' . $morecss : '') . '" name="field-' . $object->element . '-' . $fieldname . '-' . $langcode . '" value="' . $valuetoshow . '">';
470  }
471  }
472  $result .= $resultforextrlang;
473 
474  $result .= '</div>';
475  $result .= '<script nonce="' . getNonce() . '">$(".image-' . $object->element . '-' . $fieldname . '").click(function() { console.log("Toggle lang widget"); jQuery(".field-' . $object->element . '-' . $fieldname . '").toggle(); });</script>';
476  }
477 
478  return $result;
479  }
480 
494  protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null)
495  {
496  $out = '';
497 
498  // Check parameters
499  if (preg_match('/^text/', $inputType)) {
500  $value = dol_nl2br($value);
501  } elseif (preg_match('/^numeric/', $inputType)) {
502  $value = price($value);
503  } elseif ($inputType == 'day' || $inputType == 'datepicker') {
504  $value = dol_print_date($value, 'day');
505  }
506 
507  if ($condition) {
508  $element = false;
509  $table_element = false;
510  $fk_element = false;
511  $loadmethod = false;
512  $savemethod = false;
513  $ext_element = false;
514  $button_only = false;
515  $inputOption = '';
516  $rows = '';
517  $cols = '';
518 
519  if (is_object($object)) {
520  $element = $object->element;
521  $table_element = $object->table_element;
522  $fk_element = $object->id;
523  }
524 
525  if (is_object($extObject)) {
526  $ext_element = $extObject->element;
527  }
528 
529  if (preg_match('/^(string|email|numeric)/', $inputType)) {
530  $tmp = explode(':', $inputType);
531  $inputType = $tmp[0];
532  if (!empty($tmp[1])) {
533  $inputOption = $tmp[1];
534  }
535  if (!empty($tmp[2])) {
536  $savemethod = $tmp[2];
537  }
538  $out .= '<input id="width_' . $htmlname . '" value="' . $inputOption . '" type="hidden"/>' . "\n";
539  } elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) {
540  $tmp = explode(':', $inputType);
541  $inputType = $tmp[0];
542  if (!empty($tmp[1])) {
543  $inputOption = $tmp[1];
544  }
545  if (!empty($tmp[2])) {
546  $savemethod = $tmp[2];
547  }
548 
549  $out .= '<input id="timestamp" type="hidden"/>' . "\n"; // Use for timestamp format
550  } elseif (preg_match('/^(select|autocomplete)/', $inputType)) {
551  $tmp = explode(':', $inputType);
552  $inputType = $tmp[0];
553  $loadmethod = $tmp[1];
554  if (!empty($tmp[2])) {
555  $savemethod = $tmp[2];
556  }
557  if (!empty($tmp[3])) {
558  $button_only = true;
559  }
560  } elseif (preg_match('/^textarea/', $inputType)) {
561  $tmp = explode(':', $inputType);
562  $inputType = $tmp[0];
563  $rows = (empty($tmp[1]) ? '8' : $tmp[1]);
564  $cols = (empty($tmp[2]) ? '80' : $tmp[2]);
565  } elseif (preg_match('/^ckeditor/', $inputType)) {
566  $tmp = explode(':', $inputType);
567  $inputType = $tmp[0];
568  $toolbar = $tmp[1];
569  if (!empty($tmp[2])) {
570  $width = $tmp[2];
571  }
572  if (!empty($tmp[3])) {
573  $height = $tmp[3];
574  }
575  if (!empty($tmp[4])) {
576  $savemethod = $tmp[4];
577  }
578 
579  if (isModEnabled('fckeditor')) {
580  $out .= '<input id="ckeditor_toolbar" value="' . $toolbar . '" type="hidden"/>' . "\n";
581  } else {
582  $inputType = 'textarea';
583  }
584  }
585 
586  $out .= '<input id="element_' . $htmlname . '" value="' . $element . '" type="hidden"/>' . "\n";
587  $out .= '<input id="table_element_' . $htmlname . '" value="' . $table_element . '" type="hidden"/>' . "\n";
588  $out .= '<input id="fk_element_' . $htmlname . '" value="' . $fk_element . '" type="hidden"/>' . "\n";
589  $out .= '<input id="loadmethod_' . $htmlname . '" value="' . $loadmethod . '" type="hidden"/>' . "\n";
590  if (!empty($savemethod)) {
591  $out .= '<input id="savemethod_' . $htmlname . '" value="' . $savemethod . '" type="hidden"/>' . "\n";
592  }
593  if (!empty($ext_element)) {
594  $out .= '<input id="ext_element_' . $htmlname . '" value="' . $ext_element . '" type="hidden"/>' . "\n";
595  }
596  if (!empty($custommsg)) {
597  if (is_array($custommsg)) {
598  if (!empty($custommsg['success'])) {
599  $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg['success'] . '" type="hidden"/>' . "\n";
600  }
601  if (!empty($custommsg['error'])) {
602  $out .= '<input id="errormsg_' . $htmlname . '" value="' . $custommsg['error'] . '" type="hidden"/>' . "\n";
603  }
604  } else {
605  $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg . '" type="hidden"/>' . "\n";
606  }
607  }
608  if ($inputType == 'textarea') {
609  $out .= '<input id="textarea_' . $htmlname . '_rows" value="' . $rows . '" type="hidden"/>' . "\n";
610  $out .= '<input id="textarea_' . $htmlname . '_cols" value="' . $cols . '" type="hidden"/>' . "\n";
611  }
612  $out .= '<span id="viewval_' . $htmlname . '" class="viewval_' . $inputType . ($button_only ? ' inactive' : ' active') . '">' . $value . '</span>' . "\n";
613  $out .= '<span id="editval_' . $htmlname . '" class="editval_' . $inputType . ($button_only ? ' inactive' : ' active') . ' hideobject">' . (!empty($editvalue) ? $editvalue : $value) . '</span>' . "\n";
614  } else {
615  $out = $value;
616  }
617 
618  return $out;
619  }
620 
639  public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
640  {
641  if ($incbefore) {
642  $text = $incbefore . $text;
643  }
644  if (!$htmltext) {
645  return $text;
646  }
647  $direction = (int) $direction; // For backward compatibility when $direction was set to '' instead of 0
648 
649  $tag = 'td';
650  if ($notabs == 2) {
651  $tag = 'div';
652  }
653  if ($notabs == 3) {
654  $tag = 'span';
655  }
656  // Sanitize tooltip
657  $htmltext = str_replace(array("\r", "\n"), '', $htmltext);
658 
659  $extrastyle = '';
660  if ($direction < 0) {
661  $extracss = ($extracss ? $extracss . ' ' : '') . ($notabs != 3 ? 'inline-block' : '');
662  $extrastyle = 'padding: 0px; padding-left: 3px;';
663  }
664  if ($direction > 0) {
665  $extracss = ($extracss ? $extracss . ' ' : '') . ($notabs != 3 ? 'inline-block' : '');
666  $extrastyle = 'padding: 0px; padding-right: 3px;';
667  }
668 
669  $classfortooltip = 'classfortooltip';
670 
671  $s = '';
672  $textfordialog = '';
673 
674  if ($tooltiptrigger == '') {
675  $htmltext = str_replace('"', '&quot;', $htmltext);
676  } else {
677  $classfortooltip = 'classfortooltiponclick';
678  $textfordialog .= '<div style="display: none;" id="idfortooltiponclick_' . $tooltiptrigger . '" class="classfortooltiponclicktext">' . $htmltext . '</div>';
679  }
680  if ($tooltipon == 2 || $tooltipon == 3) {
681  $paramfortooltipimg = ' class="' . $classfortooltip . ($notabs != 3 ? ' inline-block' : '') . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '"';
682  if ($tooltiptrigger == '') {
683  $paramfortooltipimg .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)) . '"'; // Attribute to put on img tag to store tooltip
684  } else {
685  $paramfortooltipimg .= ' dolid="' . $tooltiptrigger . '"';
686  }
687  } else {
688  $paramfortooltipimg = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribute to put on td text tag
689  }
690  if ($tooltipon == 1 || $tooltipon == 3) {
691  $paramfortooltiptd = ' class="' . ($tooltipon == 3 ? 'cursorpointer ' : '') . $classfortooltip . ' inline-block' . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '" ';
692  if ($tooltiptrigger == '') {
693  $paramfortooltiptd .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)) . '"'; // Attribute to put on td tag to store tooltip
694  } else {
695  $paramfortooltiptd .= ' dolid="' . $tooltiptrigger . '"';
696  }
697  } else {
698  $paramfortooltiptd = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribute to put on td text tag
699  }
700  if (empty($notabs)) {
701  $s .= '<table class="nobordernopadding"><tr style="height: auto;">';
702  } elseif ($notabs == 2) {
703  $s .= '<div class="inline-block' . ($forcenowrap ? ' nowrap' : '') . '">';
704  }
705  // Define value if value is before
706  if ($direction < 0) {
707  $s .= '<' . $tag . $paramfortooltipimg;
708  if ($tag == 'td') {
709  $s .= ' class="valigntop" width="14"';
710  }
711  $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
712  }
713  // Use another method to help avoid having a space in value in order to use this value with jquery
714  // Define label
715  if ((string) $text != '') {
716  $s .= '<' . $tag . $paramfortooltiptd . '>' . $text . '</' . $tag . '>';
717  }
718  // Define value if value is after
719  if ($direction > 0) {
720  $s .= '<' . $tag . $paramfortooltipimg;
721  if ($tag == 'td') {
722  $s .= ' class="valignmiddle" width="14"';
723  }
724  $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
725  }
726  if (empty($notabs)) {
727  $s .= '</tr></table>';
728  } elseif ($notabs == 2) {
729  $s .= '</div>';
730  }
731 
732  return $s;
733  }
734 
749  public function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
750  {
751  global $conf, $langs;
752 
753  //For backwards compatibility
754  if ($type == '0') {
755  $type = 'info';
756  } elseif ($type == '1') {
757  $type = 'help';
758  }
759  // Clean parameters
760  $tooltiptrigger = preg_replace('/[^a-z0-9]/i', '', $tooltiptrigger);
761 
762  if (preg_match('/onsmartphone$/', $tooltiptrigger) && empty($conf->dol_no_mouse_hover)) {
763  $tooltiptrigger = preg_replace('/^.*onsmartphone$/', '', $tooltiptrigger);
764  }
765  $alt = '';
766  if ($tooltiptrigger) {
767  $alt = $langs->transnoentitiesnoconv("ClickToShowHelp");
768  }
769 
770  // If info or help with no javascript, show only text
771  if (empty($conf->use_javascript_ajax)) {
772  if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
773  return $text;
774  } else {
775  $alt = $htmltext;
776  $htmltext = '';
777  }
778  }
779 
780  // If info or help with smartphone, show only text (tooltip hover can't works)
781  if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) {
782  if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
783  return $text;
784  }
785  }
786  // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone)
787  //if (!empty($conf->dol_no_mouse_hover) && !empty($tooltiptrigger))
788  //{
789  //if ($type == 'info' || $type == 'help') return '<a href="'..'">'.$text.'</a>';
790  //}
791 
792  $img = '';
793  if ($type == 'info') {
794  $img = img_help(0, $alt);
795  } elseif ($type == 'help') {
796  $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
797  } elseif ($type == 'helpclickable') {
798  $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
799  } elseif ($type == 'superadmin') {
800  // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
801  $img = img_picto($alt, 'redstar');
802  } elseif ($type == 'admin') {
803  // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
804  $img = img_picto($alt, 'star');
805  } elseif ($type == 'warning') {
806  $img = img_warning($alt);
807  } elseif ($type != 'none') {
808  // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
809  $img = img_picto($alt, $type); // $type can be an image path
810  }
811 
812  return $this->textwithtooltip($text, $htmltext, ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2), $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
813  }
814 
825  public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect')
826  {
827  global $conf, $langs, $hookmanager;
828 
829  $disabled = 0;
830  $ret = '<div class="centpercent center">';
831  $ret .= '<select class="flat' . (empty($conf->use_javascript_ajax) ? '' : ' hideobject') . ' ' . $name . ' ' . $name . 'select valignmiddle alignstart" id="' . $name . '" name="' . $name . '"' . ($disabled ? ' disabled="disabled"' : '') . '>';
832 
833  // 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.
834  $parameters = array();
835  $reshook = $hookmanager->executeHooks('addMoreMassActions', $parameters); // Note that $action and $object may have been modified by hook
836  // check if there is a mass action
837 
838  if (is_array($arrayofaction) && count($arrayofaction) == 0 && empty($hookmanager->resPrint)) {
839  return;
840  }
841  if (empty($reshook)) {
842  $ret .= '<option value="0"' . ($disabled ? ' disabled="disabled"' : '') . '>-- ' . $langs->trans("SelectAction") . ' --</option>';
843  if (is_array($arrayofaction)) {
844  foreach ($arrayofaction as $code => $label) {
845  $ret .= '<option value="' . $code . '"' . ($disabled ? ' disabled="disabled"' : '') . ' data-html="' . dol_escape_htmltag($label) . '">' . $label . '</option>';
846  }
847  }
848  }
849  $ret .= $hookmanager->resPrint;
850 
851  $ret .= '</select>';
852 
853  if (empty($conf->dol_optimize_smallscreen)) {
854  $ret .= ajax_combobox('.' . $name . 'select');
855  }
856 
857  // 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
858  $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.
859  $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")) . '">';
860  $ret .= '</div>';
861 
862  if (!empty($conf->use_javascript_ajax)) {
863  $ret .= '<!-- JS CODE TO ENABLE mass action select -->
864  <script nonce="' . getNonce() . '">
865  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 */
866  {
867  atleastoneselected=0;
868  jQuery("."+cssclass).each(function( index ) {
869  /* console.log( index + ": " + $( this ).text() ); */
870  if ($(this).is(\':checked\')) atleastoneselected++;
871  });
872 
873  console.log("initCheckForSelect mode="+mode+" name="+name+" cssclass="+cssclass+" atleastoneselected="+atleastoneselected);
874 
875  if (atleastoneselected || ' . $alwaysvisible . ')
876  {
877  jQuery("."+name).show();
878  ' . ($selected ? 'if (atleastoneselected) { jQuery("."+name+"select").val("' . $selected . '").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', false); }' : '') . '
879  ' . ($selected ? 'if (! atleastoneselected) { jQuery("."+name+"select").val("0").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', true); } ' : '') . '
880  }
881  else
882  {
883  jQuery("."+name).hide();
884  jQuery("."+name+"other").hide();
885  }
886  }
887 
888  jQuery(document).ready(function () {
889  initCheckForSelect(0, "' . $name . '", "' . $cssclass . '");
890  jQuery(".' . $cssclass . '").click(function() {
891  initCheckForSelect(1, "' . $name . '", "' . $cssclass . '");
892  });
893  jQuery(".' . $name . 'select").change(function() {
894  var massaction = $( this ).val();
895  var urlform = $( this ).closest("form").attr("action").replace("#show_files","");
896  if (massaction == "builddoc")
897  {
898  urlform = urlform + "#show_files";
899  }
900  $( this ).closest("form").attr("action", urlform);
901  console.log("we select a mass action name=' . $name . ' massaction="+massaction+" - "+urlform);
902  /* Warning: if you set submit button to disabled, post using Enter will no more work if there is no other button */
903  if ($(this).val() != \'0\')
904  {
905  jQuery(".' . $name . 'confirmed").prop(\'disabled\', false);
906  jQuery(".' . $name . 'other").hide(); /* To disable if another div was open */
907  jQuery(".' . $name . '"+massaction).show();
908  }
909  else
910  {
911  jQuery(".' . $name . 'confirmed").prop(\'disabled\', true);
912  jQuery(".' . $name . 'other").hide(); /* To disable any div open */
913  }
914  });
915  });
916  </script>
917  ';
918  }
919 
920  return $ret;
921  }
922 
923  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
924 
941  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)
942  {
943  // phpcs:enable
944  global $conf, $langs, $mysoc;
945 
946  $langs->load("dict");
947 
948  $out = '';
949  $countryArray = array();
950  $favorite = array();
951  $label = array();
952  $atleastonefavorite = 0;
953 
954  $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite, eec";
955  $sql .= " FROM " . $this->db->prefix() . "c_country";
956  $sql .= " WHERE active > 0";
957  //$sql.= " ORDER BY code ASC";
958 
959  dol_syslog(get_class($this) . "::select_country", LOG_DEBUG);
960  $resql = $this->db->query($sql);
961  if ($resql) {
962  $out .= '<select id="select' . $htmlname . '" class="flat maxwidth200onsmartphone selectcountry' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" ' . $htmloption . '>';
963  $num = $this->db->num_rows($resql);
964  $i = 0;
965  if ($num) {
966  while ($i < $num) {
967  $obj = $this->db->fetch_object($resql);
968 
969  $countryArray[$i]['rowid'] = $obj->rowid;
970  $countryArray[$i]['code_iso'] = $obj->code_iso;
971  $countryArray[$i]['code_iso3'] = $obj->code_iso3;
972  $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 : ''));
973  $countryArray[$i]['favorite'] = $obj->favorite;
974  $countryArray[$i]['eec'] = $obj->eec;
975  $favorite[$i] = $obj->favorite;
976  $label[$i] = dol_string_unaccent($countryArray[$i]['label']);
977  $i++;
978  }
979 
980  if (empty($disablefavorites)) {
981  $array1_sort_order = SORT_DESC;
982  $array2_sort_order = SORT_ASC;
983  array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $countryArray);
984  } else {
985  $countryArray = dol_sort_array($countryArray, 'label');
986  }
987 
988  if ($showempty) {
989  if (is_numeric($showempty)) {
990  $out .= '<option value="">&nbsp;</option>' . "\n";
991  } else {
992  $out .= '<option value="-1">' . $langs->trans($showempty) . '</option>' . "\n";
993  }
994  }
995 
996  if ($addspecialentries) { // Add dedicated entries for groups of countries
997  //if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
998  $out .= '<option value="special_allnotme"' . ($selected == 'special_allnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
999  $out .= '<option value="special_eec"' . ($selected == 'special_eec' ? ' selected' : '') . '>' . $langs->trans("CountriesInEEC") . '</option>';
1000  if ($mysoc->isInEEC()) {
1001  $out .= '<option value="special_eecnotme"' . ($selected == 'special_eecnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
1002  }
1003  $out .= '<option value="special_noteec"' . ($selected == 'special_noteec' ? ' selected' : '') . '>' . $langs->trans("CountriesNotInEEC") . '</option>';
1004  $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1005  }
1006 
1007  foreach ($countryArray as $row) {
1008  //if (empty($showempty) && empty($row['rowid'])) continue;
1009  if (empty($row['rowid'])) {
1010  continue;
1011  }
1012  if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
1013  continue; // exclude some countries
1014  }
1015 
1016  if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
1017  $atleastonefavorite++;
1018  }
1019  if (empty($row['favorite']) && $atleastonefavorite) {
1020  $atleastonefavorite = 0;
1021  $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1022  }
1023 
1024  $labeltoshow = '';
1025  if ($row['label']) {
1026  $labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
1027  } else {
1028  $labeltoshow .= '&nbsp;';
1029  }
1030  if ($row['code_iso']) {
1031  $labeltoshow .= ' <span class="opacitymedium">(' . $row['code_iso'] . ')</span>';
1032  if (empty($hideflags)) {
1033  $tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
1034  $labeltoshow = $tmpflag . ' ' . $labeltoshow;
1035  }
1036  }
1037 
1038  if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
1039  $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']) . '">';
1040  } else {
1041  $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']) . '">';
1042  }
1043  $out .= $labeltoshow;
1044  $out .= '</option>' . "\n";
1045  }
1046  }
1047  $out .= '</select>';
1048  } else {
1049  dol_print_error($this->db);
1050  }
1051 
1052  // Make select dynamic
1053  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1054  $out .= ajax_combobox('select' . $htmlname, array(), 0, 0, 'resolve');
1055 
1056  return $out;
1057  }
1058 
1059  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1060 
1074  public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array(), $disableautocomplete = 0)
1075  {
1076  // phpcs:enable
1077  global $conf, $langs;
1078 
1079  $langs->load("dict");
1080 
1081  $out = '';
1082  $moreattrib = '';
1083  $incotermArray = array();
1084 
1085  $sql = "SELECT rowid, code";
1086  $sql .= " FROM " . $this->db->prefix() . "c_incoterms";
1087  $sql .= " WHERE active > 0";
1088  $sql .= " ORDER BY code ASC";
1089 
1090  dol_syslog(get_class($this) . "::select_incoterm", LOG_DEBUG);
1091  $resql = $this->db->query($sql);
1092  if ($resql) {
1093  if ($conf->use_javascript_ajax && !$forcecombo) {
1094  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1095  $out .= ajax_combobox($htmlname, $events);
1096  }
1097 
1098  if (!empty($page)) {
1099  $out .= '<form method="post" action="' . $page . '">';
1100  $out .= '<input type="hidden" name="action" value="set_incoterms">';
1101  $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
1102  }
1103 
1104  $out .= '<select id="' . $htmlname . '" class="flat selectincoterm width75" name="' . $htmlname . '" ' . $htmloption . '>';
1105  $out .= '<option value="0">&nbsp;</option>';
1106  $num = $this->db->num_rows($resql);
1107  $i = 0;
1108  if ($num) {
1109  while ($i < $num) {
1110  $obj = $this->db->fetch_object($resql);
1111  $incotermArray[$i]['rowid'] = $obj->rowid;
1112  $incotermArray[$i]['code'] = $obj->code;
1113  $i++;
1114  }
1115 
1116  foreach ($incotermArray as $row) {
1117  if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1118  $out .= '<option value="' . $row['rowid'] . '" selected>';
1119  } else {
1120  $out .= '<option value="' . $row['rowid'] . '">';
1121  }
1122 
1123  if ($row['code']) {
1124  $out .= $row['code'];
1125  }
1126 
1127  $out .= '</option>';
1128  }
1129  }
1130  $out .= '</select>';
1131 
1132  if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
1133  $out .= ajax_multiautocompleter('location_incoterms', array(), DOL_URL_ROOT . '/core/ajax/locationincoterms.php') . "\n";
1134  $moreattrib .= ' autocomplete="off"';
1135  }
1136  $out .= '<input id="location_incoterms" class="maxwidthonsmartphone type="text" name="location_incoterms" value="' . $location_incoterms . '">' . "\n";
1137 
1138  if (!empty($page)) {
1139  $out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="' . $langs->trans("Modify") . '"></form>';
1140  }
1141  } else {
1142  dol_print_error($this->db);
1143  }
1144 
1145  return $out;
1146  }
1147 
1148  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1149 
1162  public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0, $morecss = "")
1163  {
1164  // phpcs:enable
1165  global $langs;
1166 
1167  // If product & services are enabled or both disabled.
1168  if ($forceall == 1 || (empty($forceall) && isModEnabled("product") && isModEnabled("service"))
1169  || (empty($forceall) && !isModEnabled('product') && !isModEnabled('service'))) {
1170  if (empty($hidetext)) {
1171  print $langs->trans("Type") . ': ';
1172  }
1173  print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $htmlname . '" name="' . $htmlname . '">';
1174  if ($showempty) {
1175  print '<option value="-1"';
1176  if ($selected == -1) {
1177  print ' selected';
1178  }
1179  print '>';
1180  if (is_numeric($showempty)) {
1181  print '&nbsp;';
1182  } else {
1183  print $showempty;
1184  }
1185  print '</option>';
1186  }
1187 
1188  print '<option value="0"';
1189  if (0 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'product')) {
1190  print ' selected';
1191  }
1192  print '>' . $langs->trans("Product");
1193 
1194  print '<option value="1"';
1195  if (1 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'service')) {
1196  print ' selected';
1197  }
1198  print '>' . $langs->trans("Service");
1199 
1200  print '</select>';
1201  print ajax_combobox('select_' . $htmlname);
1202  //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1203  }
1204  if ((empty($forceall) && !isModEnabled('product') && isModEnabled("service")) || $forceall == 3) {
1205  print $langs->trans("Service");
1206  print '<input type="hidden" name="' . $htmlname . '" value="1">';
1207  }
1208  if ((empty($forceall) && isModEnabled("product") && !isModEnabled('service')) || $forceall == 2) {
1209  print $langs->trans("Product");
1210  print '<input type="hidden" name="' . $htmlname . '" value="0">';
1211  }
1212  if ($forceall < 0) { // This should happened only for contracts when both predefined product and service are disabled.
1213  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
1214  }
1215  }
1216 
1217  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1218 
1224  public function load_cache_types_fees()
1225  {
1226  // phpcs:enable
1227  global $langs;
1228 
1229  $num = count($this->cache_types_fees);
1230  if ($num > 0) {
1231  return 0; // Cache already loaded
1232  }
1233 
1234  dol_syslog(__METHOD__, LOG_DEBUG);
1235 
1236  $langs->load("trips");
1237 
1238  $sql = "SELECT c.code, c.label";
1239  $sql .= " FROM " . $this->db->prefix() . "c_type_fees as c";
1240  $sql .= " WHERE active > 0";
1241 
1242  $resql = $this->db->query($sql);
1243  if ($resql) {
1244  $num = $this->db->num_rows($resql);
1245  $i = 0;
1246 
1247  while ($i < $num) {
1248  $obj = $this->db->fetch_object($resql);
1249 
1250  // Si traduction existe, on l'utilise, sinon on prend le libelle par default
1251  $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1252  $this->cache_types_fees[$obj->code] = $label;
1253  $i++;
1254  }
1255 
1256  asort($this->cache_types_fees);
1257 
1258  return $num;
1259  } else {
1260  dol_print_error($this->db);
1261  return -1;
1262  }
1263  }
1264 
1265  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1266 
1275  public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1276  {
1277  // phpcs:enable
1278  global $user, $langs;
1279 
1280  dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
1281 
1282  $this->load_cache_types_fees();
1283 
1284  print '<select id="select_' . $htmlname . '" class="flat" name="' . $htmlname . '">';
1285  if ($showempty) {
1286  print '<option value="-1"';
1287  if ($selected == -1) {
1288  print ' selected';
1289  }
1290  print '>&nbsp;</option>';
1291  }
1292 
1293  foreach ($this->cache_types_fees as $key => $value) {
1294  print '<option value="' . $key . '"';
1295  if ($key == $selected) {
1296  print ' selected';
1297  }
1298  print '>';
1299  print $value;
1300  print '</option>';
1301  }
1302 
1303  print '</select>';
1304  if ($user->admin) {
1305  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1306  }
1307  }
1308 
1309 
1310  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1311 
1334  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)
1335  {
1336  // phpcs:enable
1337  global $conf, $langs;
1338 
1339  $out = '';
1340 
1341  if (!empty($conf->use_javascript_ajax) && getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1342  if (is_null($ajaxoptions)) {
1343  $ajaxoptions = array();
1344  }
1345 
1346  require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1347 
1348  // No immediate load of all database
1349  $placeholder = '';
1350  if ($selected && empty($selected_input_value)) {
1351  require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1352  $societetmp = new Societe($this->db);
1353  $societetmp->fetch($selected);
1354  $selected_input_value = $societetmp->name;
1355  unset($societetmp);
1356  }
1357 
1358  // mode 1
1359  $urloption = 'htmlname=' . urlencode((string) (str_replace('.', '_', $htmlname))) . '&outjson=1&filter=' . urlencode((string) ($filter)) . (empty($excludeids) ? '' : '&excludeids=' . implode(',', $excludeids)) . ($showtype ? '&showtype=' . urlencode((string) ($showtype)) : '') . ($showcode ? '&showcode=' . urlencode((string) ($showcode)) : '');
1360 
1361  $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1362  if (empty($hidelabel)) {
1363  print $langs->trans("RefOrLabel") . ' : ';
1364  } elseif ($hidelabel > 1) {
1365  $placeholder = $langs->trans("RefOrLabel");
1366  if ($hidelabel == 2) {
1367  $out .= img_picto($langs->trans("Search"), 'search');
1368  }
1369  }
1370  $out .= '<input type="text" class="' . $morecss . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '') . ' ' . (getDolGlobalString('THIRDPARTY_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
1371  if ($hidelabel == 3) {
1372  $out .= img_picto($langs->trans("Search"), 'search');
1373  }
1374 
1375  $out .= ajax_event($htmlname, $events);
1376 
1377  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
1378  } else {
1379  // Immediate load of all database
1380  $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids, $showcode);
1381  }
1382 
1383  return $out;
1384  }
1385 
1386 
1387  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1388 
1414  public function select_contact($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $nokeyifsocid = true, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $selected_input_value = '', $filter = '')
1415  {
1416  // phpcs:enable
1417 
1418  global $conf, $langs;
1419 
1420  $out = '';
1421 
1422  $sav = getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT');
1423  if ($nokeyifsocid && $socid > 0) {
1424  $conf->global->CONTACT_USE_SEARCH_TO_SELECT = 0;
1425  }
1426 
1427  if (!empty($conf->use_javascript_ajax) && getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1428  if (is_null($events)) {
1429  $events = array();
1430  }
1431 
1432  require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1433 
1434  // No immediate load of all database
1435  $placeholder = '';
1436  if ($selected && empty($selected_input_value)) {
1437  require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
1438  $contacttmp = new Contact($this->db);
1439  $contacttmp->fetch($selected);
1440  $selected_input_value = $contacttmp->getFullName($langs);
1441  unset($contacttmp);
1442  }
1443  if (!is_numeric($showempty)) {
1444  $placeholder = $showempty;
1445  }
1446 
1447  // mode 1
1448  $urloption = 'htmlname=' . urlencode((string) (str_replace('.', '_', $htmlname))) . '&outjson=1&filter=' . urlencode((string) ($filter)) . (empty($exclude) ? '' : '&exclude=' . urlencode($exclude)) . ($showsoc ? '&showsoc=' . urlencode((string) ($showsoc)) : '');
1449 
1450  $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1451 
1452  $out .= '<input type="text" class="' . $morecss . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '') . ' ' . (getDolGlobalString('CONTACT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
1453 
1454  $out .= ajax_event($htmlname, $events);
1455 
1456  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/contact/ajax/contact.php', $urloption, getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT'), 0, $events);
1457  } else {
1458  // Immediate load of all database
1459  $multiple = false;
1460  $disableifempty = 0;
1461  $options_only = false;
1462  $limitto = '';
1463 
1464  $out .= $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $morecss, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid, $multiple, $disableifempty);
1465  }
1466 
1467  $conf->global->CONTACT_USE_SEARCH_TO_SELECT = $sav;
1468 
1469  return $out;
1470  }
1471 
1472 
1473  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1474 
1499  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)
1500  {
1501  // phpcs:enable
1502  global $user, $langs;
1503  global $hookmanager;
1504 
1505  $out = '';
1506  $num = 0;
1507  $outarray = array();
1508 
1509  if ($selected === '') {
1510  $selected = array();
1511  } elseif (!is_array($selected)) {
1512  $selected = array($selected);
1513  }
1514 
1515  // Clean $filter that may contains sql conditions so sql code
1516  if (function_exists('testSqlAndScriptInject')) {
1517  if (testSqlAndScriptInject($filter, 3) > 0) {
1518  $filter = '';
1519  return 'SQLInjectionTryDetected';
1520  }
1521  }
1522 
1523  if ($filter != '') { // If a filter was provided
1524  if (preg_match('/[\‍(\‍)]/', $filter)) {
1525  // If there is one parenthesis inside the criteria, we assume it is an Universal Filter Syntax.
1526  $errormsg = '';
1527  $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
1528 
1529  // Redo clean $filter that may contains sql conditions so sql code
1530  if (function_exists('testSqlAndScriptInject')) {
1531  if (testSqlAndScriptInject($filter, 3) > 0) {
1532  $filter = '';
1533  return 'SQLInjectionTryDetected';
1534  }
1535  }
1536  } else {
1537  // If not, we do nothing. We already know that there is no parenthesis
1538  // TODO Disallow this case in a future.
1539  dol_syslog("Warning, select_thirdparty_list was called with a filter criteria not using the Universal Search Syntax.", LOG_WARNING);
1540  }
1541  }
1542 
1543  // We search companies
1544  $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
1545  if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
1546  $sql .= ", s.address, s.zip, s.town";
1547  $sql .= ", dictp.code as country_code";
1548  }
1549  $sql .= " FROM " . $this->db->prefix() . "societe as s";
1550  if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
1551  $sql .= " LEFT JOIN " . $this->db->prefix() . "c_country as dictp ON dictp.rowid = s.fk_pays";
1552  }
1553  if (!$user->hasRight('societe', 'client', 'voir')) {
1554  $sql .= ", " . $this->db->prefix() . "societe_commerciaux as sc";
1555  }
1556  $sql .= " WHERE s.entity IN (" . getEntity('societe') . ")";
1557  if (!empty($user->socid)) {
1558  $sql .= " AND s.rowid = " . ((int) $user->socid);
1559  }
1560  if ($filter) {
1561  // $filter is safe because, if it contains '(' or ')', it has been sanitized by testSqlAndScriptInject() and forgeSQLFromUniversalSearchCriteria()
1562  // if not, by testSqlAndScriptInject() only.
1563  $sql .= " AND (" . $filter . ")";
1564  }
1565  if (!$user->hasRight('societe', 'client', 'voir')) {
1566  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id);
1567  }
1568  if (getDolGlobalString('COMPANY_HIDE_INACTIVE_IN_COMBOBOX')) {
1569  $sql .= " AND s.status <> 0";
1570  }
1571  if (!empty($excludeids)) {
1572  $sql .= " AND s.rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeids)) . ")";
1573  }
1574  // Add where from hooks
1575  $parameters = array();
1576  $reshook = $hookmanager->executeHooks('selectThirdpartyListWhere', $parameters); // Note that $action and $object may have been modified by hook
1577  $sql .= $hookmanager->resPrint;
1578  // Add criteria
1579  if ($filterkey && $filterkey != '') {
1580  $sql .= " AND (";
1581  $prefix = !getDolGlobalString('COMPANY_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
1582  // For natural search
1583  $search_crit = explode(' ', $filterkey);
1584  $i = 0;
1585  if (count($search_crit) > 1) {
1586  $sql .= "(";
1587  }
1588  foreach ($search_crit as $crit) {
1589  if ($i > 0) {
1590  $sql .= " AND ";
1591  }
1592  $sql .= "(s.nom LIKE '" . $this->db->escape($prefix . $crit) . "%')";
1593  $i++;
1594  }
1595  if (count($search_crit) > 1) {
1596  $sql .= ")";
1597  }
1598  if (isModEnabled('barcode')) {
1599  $sql .= " OR s.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
1600  }
1601  $sql .= " OR s.code_client LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.code_fournisseur LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
1602  $sql .= " OR s.name_alias LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.tva_intra LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
1603  $sql .= ")";
1604  }
1605  $sql .= $this->db->order("nom", "ASC");
1606  $sql .= $this->db->plimit($limit, 0);
1607 
1608  // Build output string
1609  dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
1610  $resql = $this->db->query($sql);
1611  if ($resql) {
1612  // Construct $out and $outarray
1613  $out .= '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($moreparam ? ' ' . $moreparam : '') . ' name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . '>' . "\n";
1614 
1615  $textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
1616  if (getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT')) {
1617  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
1618  //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
1619  if ($showempty && !is_numeric($showempty)) {
1620  $textifempty = $langs->trans($showempty);
1621  } else {
1622  $textifempty .= $langs->trans("All");
1623  }
1624  }
1625  if ($showempty) {
1626  $out .= '<option value="-1" data-html="' . dol_escape_htmltag('<span class="opacitymedium">' . ($textifempty ? $textifempty : '&nbsp;') . '</span>') . '">' . $textifempty . '</option>' . "\n";
1627  }
1628 
1629  $companytemp = new Societe($this->db);
1630 
1631  $num = $this->db->num_rows($resql);
1632  $i = 0;
1633  if ($num) {
1634  while ($i < $num) {
1635  $obj = $this->db->fetch_object($resql);
1636  $label = '';
1637  if ($showcode || getDolGlobalString('SOCIETE_ADD_REF_IN_LIST')) {
1638  if (($obj->client) && (!empty($obj->code_client))) {
1639  $label = $obj->code_client . ' - ';
1640  }
1641  if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
1642  $label .= $obj->code_fournisseur . ' - ';
1643  }
1644  $label .= ' ' . $obj->name;
1645  } else {
1646  $label = $obj->name;
1647  }
1648 
1649  if (!empty($obj->name_alias)) {
1650  $label .= ' (' . $obj->name_alias . ')';
1651  }
1652 
1653  if (getDolGlobalString('SOCIETE_SHOW_VAT_IN_LIST') && !empty($obj->tva_intra)) {
1654  $label .= ' - '.$obj->tva_intra;
1655  }
1656 
1657  $labelhtml = $label;
1658 
1659  if ($showtype) {
1660  $companytemp->id = $obj->rowid;
1661  $companytemp->client = $obj->client;
1662  $companytemp->fournisseur = $obj->fournisseur;
1663  $tmptype = $companytemp->getTypeUrl(1, '', 0, 'span');
1664  if ($tmptype) {
1665  $labelhtml .= ' ' . $tmptype;
1666  }
1667 
1668  if ($obj->client || $obj->fournisseur) {
1669  $label .= ' (';
1670  }
1671  if ($obj->client == 1 || $obj->client == 3) {
1672  $label .= $langs->trans("Customer");
1673  }
1674  if ($obj->client == 2 || $obj->client == 3) {
1675  $label .= ($obj->client == 3 ? ', ' : '') . $langs->trans("Prospect");
1676  }
1677  if ($obj->fournisseur) {
1678  $label .= ($obj->client ? ', ' : '') . $langs->trans("Supplier");
1679  }
1680  if ($obj->client || $obj->fournisseur) {
1681  $label .= ')';
1682  }
1683  }
1684 
1685  if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
1686  $s = ($obj->address ? ' - ' . $obj->address : '') . ($obj->zip ? ' - ' . $obj->zip : '') . ($obj->town ? ' ' . $obj->town : '');
1687  if (!empty($obj->country_code)) {
1688  $s .= ', ' . $langs->trans('Country' . $obj->country_code);
1689  }
1690  $label .= $s;
1691  $labelhtml .= $s;
1692  }
1693 
1694  if (empty($outputmode)) {
1695  if (in_array($obj->rowid, $selected)) {
1696  $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>';
1697  } else {
1698  $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
1699  }
1700  } else {
1701  array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label, 'labelhtml' => $labelhtml));
1702  }
1703 
1704  $i++;
1705  if (($i % 10) == 0) {
1706  $out .= "\n";
1707  }
1708  }
1709  }
1710  $out .= '</select>' . "\n";
1711  if (!$forcecombo) {
1712  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1713  $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("COMPANY_USE_SEARCH_TO_SELECT"));
1714  }
1715  } else {
1716  dol_print_error($this->db);
1717  }
1718 
1719  $this->result = array('nbofthirdparties' => $num);
1720 
1721  if ($outputmode) {
1722  return $outarray;
1723  }
1724  return $out;
1725  }
1726 
1727 
1754  public function selectcontacts($socid, $selected = array(), $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $options_only = 0, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false, $disableifempty = 0, $filter = '')
1755  {
1756  global $conf, $langs, $hookmanager, $action;
1757 
1758  $langs->load('companies');
1759 
1760  if (empty($htmlid)) {
1761  $htmlid = $htmlname;
1762  }
1763  $num = 0;
1764  $out = '';
1765  $outarray = array();
1766 
1767  if ($selected === '') {
1768  $selected = array();
1769  } elseif (!is_array($selected)) {
1770  $selected = array((int) $selected);
1771  }
1772 
1773  // Clean $filter that may contains sql conditions so sql code
1774  if (function_exists('testSqlAndScriptInject')) {
1775  if (testSqlAndScriptInject($filter, 3) > 0) {
1776  $filter = '';
1777  return 'SQLInjectionTryDetected';
1778  }
1779  }
1780 
1781  if ($filter != '') { // If a filter was provided
1782  if (preg_match('/[\‍(\‍)]/', $filter)) {
1783  // If there is one parenthesis inside the criteria, we assume it is an Universal Filter Syntax.
1784  $errormsg = '';
1785  $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
1786 
1787  // Redo clean $filter that may contains sql conditions so sql code
1788  if (function_exists('testSqlAndScriptInject')) {
1789  if (testSqlAndScriptInject($filter, 3) > 0) {
1790  $filter = '';
1791  return 'SQLInjectionTryDetected';
1792  }
1793  }
1794  } else {
1795  // If not, we do nothing. We already know that there is no parenthesis
1796  // TODO Disallow this case in a future.
1797  dol_syslog("Warning, select_thirdparty_list was called with a filter criteria not using the Universal Search Syntax.", LOG_WARNING);
1798  }
1799  }
1800 
1801  if (!is_object($hookmanager)) {
1802  include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
1803  $hookmanager = new HookManager($this->db);
1804  }
1805 
1806  // We search third parties
1807  $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";
1808  if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
1809  $sql .= ", s.nom as company, s.town AS company_town";
1810  }
1811  $sql .= " FROM " . $this->db->prefix() . "socpeople as sp";
1812  if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
1813  $sql .= " LEFT OUTER JOIN " . $this->db->prefix() . "societe as s ON s.rowid=sp.fk_soc";
1814  }
1815  $sql .= " WHERE sp.entity IN (" . getEntity('contact') . ")";
1816  if ($socid > 0 || $socid == -1) {
1817  $sql .= " AND sp.fk_soc = " . ((int) $socid);
1818  }
1819  if (getDolGlobalString('CONTACT_HIDE_INACTIVE_IN_COMBOBOX')) {
1820  $sql .= " AND sp.statut <> 0";
1821  }
1822  if ($filter) {
1823  // $filter is safe because, if it contains '(' or ')', it has been sanitized by testSqlAndScriptInject() and forgeSQLFromUniversalSearchCriteria()
1824  // if not, by testSqlAndScriptInject() only.
1825  $sql .= " AND (" . $filter . ")";
1826  }
1827  // Add where from hooks
1828  $parameters = array();
1829  $reshook = $hookmanager->executeHooks('selectContactListWhere', $parameters); // Note that $action and $object may have been modified by hook
1830  $sql .= $hookmanager->resPrint;
1831  $sql .= " ORDER BY sp.lastname ASC";
1832 
1833  dol_syslog(get_class($this) . "::selectcontacts", LOG_DEBUG);
1834  $resql = $this->db->query($sql);
1835  if ($resql) {
1836  $num = $this->db->num_rows($resql);
1837 
1838  if ($htmlname != 'none' && !$options_only) {
1839  $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlid . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . (($num || empty($disableifempty)) ? '' : ' disabled') . ($multiple ? 'multiple' : '') . ' ' . (!empty($moreparam) ? $moreparam : '') . '>';
1840  }
1841 
1842  if ($showempty && !is_numeric($showempty)) {
1843  $textforempty = $showempty;
1844  $out .= '<option class="optiongrey" value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>' . $textforempty . '</option>';
1845  } else {
1846  if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) {
1847  $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>&nbsp;</option>';
1848  }
1849  if ($showempty == 2) {
1850  $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>-- ' . $langs->trans("Internal") . ' --</option>';
1851  }
1852  }
1853 
1854  $i = 0;
1855  if ($num) {
1856  include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
1857  $contactstatic = new Contact($this->db);
1858 
1859  while ($i < $num) {
1860  $obj = $this->db->fetch_object($resql);
1861 
1862  // Set email (or phones) and town extended infos
1863  $extendedInfos = '';
1864  if (getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
1865  $extendedInfos = array();
1866  $email = trim($obj->email);
1867  if (!empty($email)) {
1868  $extendedInfos[] = $email;
1869  } else {
1870  $phone = trim($obj->phone);
1871  $phone_perso = trim($obj->phone_perso);
1872  $phone_mobile = trim($obj->phone_mobile);
1873  if (!empty($phone)) {
1874  $extendedInfos[] = $phone;
1875  }
1876  if (!empty($phone_perso)) {
1877  $extendedInfos[] = $phone_perso;
1878  }
1879  if (!empty($phone_mobile)) {
1880  $extendedInfos[] = $phone_mobile;
1881  }
1882  }
1883  $contact_town = trim($obj->contact_town);
1884  $company_town = trim($obj->company_town);
1885  if (!empty($contact_town)) {
1886  $extendedInfos[] = $contact_town;
1887  } elseif (!empty($company_town)) {
1888  $extendedInfos[] = $company_town;
1889  }
1890  $extendedInfos = implode(' - ', $extendedInfos);
1891  if (!empty($extendedInfos)) {
1892  $extendedInfos = ' - ' . $extendedInfos;
1893  }
1894  }
1895 
1896  $contactstatic->id = $obj->rowid;
1897  $contactstatic->lastname = $obj->lastname;
1898  $contactstatic->firstname = $obj->firstname;
1899  if ($obj->statut == 1) {
1900  $tmplabel = '';
1901  if ($htmlname != 'none') {
1902  $disabled = 0;
1903  if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
1904  $disabled = 1;
1905  }
1906  if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
1907  $disabled = 1;
1908  }
1909  if (!empty($selected) && in_array($obj->rowid, $selected)) {
1910  $out .= '<option value="' . $obj->rowid . '"';
1911  if ($disabled) {
1912  $out .= ' disabled';
1913  }
1914  $out .= ' selected>';
1915 
1916  $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
1917  if ($showfunction && $obj->poste) {
1918  $tmplabel .= ' (' . $obj->poste . ')';
1919  }
1920  if (($showsoc > 0) && $obj->company) {
1921  $tmplabel .= ' - (' . $obj->company . ')';
1922  }
1923 
1924  $out .= $tmplabel;
1925  $out .= '</option>';
1926  } else {
1927  $out .= '<option value="' . $obj->rowid . '"';
1928  if ($disabled) {
1929  $out .= ' disabled';
1930  }
1931  $out .= '>';
1932 
1933  $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
1934  if ($showfunction && $obj->poste) {
1935  $tmplabel .= ' (' . $obj->poste . ')';
1936  }
1937  if (($showsoc > 0) && $obj->company) {
1938  $tmplabel .= ' - (' . $obj->company . ')';
1939  }
1940 
1941  $out .= $tmplabel;
1942  $out .= '</option>';
1943  }
1944  } else {
1945  if (in_array($obj->rowid, $selected)) {
1946  $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
1947  if ($showfunction && $obj->poste) {
1948  $tmplabel .= ' (' . $obj->poste . ')';
1949  }
1950  if (($showsoc > 0) && $obj->company) {
1951  $tmplabel .= ' - (' . $obj->company . ')';
1952  }
1953 
1954  $out .= $tmplabel;
1955  }
1956  }
1957 
1958  if ($tmplabel != '') {
1959  array_push($outarray, array('key' => $obj->rowid, 'value' => $tmplabel, 'label' => $tmplabel, 'labelhtml' => $tmplabel));
1960  }
1961  }
1962  $i++;
1963  }
1964  } else {
1965  $labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
1966  $out .= '<option class="disabled" value="-1"' . (($showempty == 2 || $multiple) ? '' : ' selected') . ' disabled="disabled">';
1967  $out .= $labeltoshow;
1968  $out .= '</option>';
1969  }
1970 
1971  $parameters = array(
1972  'socid' => $socid,
1973  'htmlname' => $htmlname,
1974  'resql' => $resql,
1975  'out' => &$out,
1976  'showfunction' => $showfunction,
1977  'showsoc' => $showsoc,
1978  );
1979 
1980  $reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1981 
1982  if ($htmlname != 'none' && !$options_only) {
1983  $out .= '</select>';
1984  }
1985 
1986  if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
1987  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1988  $out .= ajax_combobox($htmlid, $events, getDolGlobalInt("CONTACT_USE_SEARCH_TO_SELECT"));
1989  }
1990 
1991  $this->num = $num;
1992 
1993  if ($options_only === 2) {
1994  // Return array of options
1995  return $outarray;
1996  } else {
1997  return $out;
1998  }
1999  } else {
2000  dol_print_error($this->db);
2001  return -1;
2002  }
2003  }
2004 
2005 
2006  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2007 
2018  public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
2019  {
2020  // phpcs:enable
2021  global $langs, $conf;
2022 
2023  // On recherche les remises
2024  $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
2025  $sql .= " re.description, re.fk_facture_source";
2026  $sql .= " FROM " . $this->db->prefix() . "societe_remise_except as re";
2027  $sql .= " WHERE re.fk_soc = " . (int) $socid;
2028  $sql .= " AND re.entity = " . $conf->entity;
2029  if ($filter) {
2030  $sql .= " AND " . $filter;
2031  }
2032  $sql .= " ORDER BY re.description ASC";
2033 
2034  dol_syslog(get_class($this) . "::select_remises", LOG_DEBUG);
2035  $resql = $this->db->query($sql);
2036  if ($resql) {
2037  print '<select id="select_' . $htmlname . '" class="flat maxwidthonsmartphone" name="' . $htmlname . '">';
2038  $num = $this->db->num_rows($resql);
2039 
2040  $qualifiedlines = $num;
2041 
2042  $i = 0;
2043  if ($num) {
2044  print '<option value="0">&nbsp;</option>';
2045  while ($i < $num) {
2046  $obj = $this->db->fetch_object($resql);
2047  $desc = dol_trunc($obj->description, 40);
2048  if (preg_match('/\‍(CREDIT_NOTE\‍)/', $desc)) {
2049  $desc = preg_replace('/\‍(CREDIT_NOTE\‍)/', $langs->trans("CreditNote"), $desc);
2050  }
2051  if (preg_match('/\‍(DEPOSIT\‍)/', $desc)) {
2052  $desc = preg_replace('/\‍(DEPOSIT\‍)/', $langs->trans("Deposit"), $desc);
2053  }
2054  if (preg_match('/\‍(EXCESS RECEIVED\‍)/', $desc)) {
2055  $desc = preg_replace('/\‍(EXCESS RECEIVED\‍)/', $langs->trans("ExcessReceived"), $desc);
2056  }
2057  if (preg_match('/\‍(EXCESS PAID\‍)/', $desc)) {
2058  $desc = preg_replace('/\‍(EXCESS PAID\‍)/', $langs->trans("ExcessPaid"), $desc);
2059  }
2060 
2061  $selectstring = '';
2062  if ($selected > 0 && $selected == $obj->rowid) {
2063  $selectstring = ' selected';
2064  }
2065 
2066  $disabled = '';
2067  if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
2068  $qualifiedlines--;
2069  $disabled = ' disabled';
2070  }
2071 
2072  if (getDolGlobalString('MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST') && !empty($obj->fk_facture_source)) {
2073  $tmpfac = new Facture($this->db);
2074  if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
2075  $desc = $desc . ' - ' . $tmpfac->ref;
2076  }
2077  }
2078 
2079  print '<option value="' . $obj->rowid . '"' . $selectstring . $disabled . '>' . $desc . ' (' . price($obj->amount_ht) . ' ' . $langs->trans("HT") . ' - ' . price($obj->amount_ttc) . ' ' . $langs->trans("TTC") . ')</option>';
2080  $i++;
2081  }
2082  }
2083  print '</select>';
2084  print ajax_combobox('select_' . $htmlname);
2085 
2086  return $qualifiedlines;
2087  } else {
2088  dol_print_error($this->db);
2089  return -1;
2090  }
2091  }
2092 
2093 
2094  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2095 
2111  public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0')
2112  {
2113  // phpcs:enable
2114  print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
2115  }
2116 
2117  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2118 
2143  public function select_dolusers($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '', $maxlength = 0, $showstatus = 0, $morefilter = '', $show_every = 0, $enableonlytext = '', $morecss = '', $notdisabled = 0, $outputmode = 0, $multiple = false, $forcecombo = 0)
2144  {
2145  // phpcs:enable
2146  global $conf, $user, $langs, $hookmanager;
2147  global $action;
2148 
2149  // If no preselected user defined, we take current user
2150  if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && !getDolGlobalString('SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE')) {
2151  $selected = $user->id;
2152  }
2153 
2154  if ($selected === '') {
2155  $selected = array();
2156  } elseif (!is_array($selected)) {
2157  $selected = array($selected);
2158  }
2159 
2160  $excludeUsers = null;
2161  $includeUsers = null;
2162 
2163  // Exclude some users
2164  if (is_array($exclude)) {
2165  $excludeUsers = implode(",", $exclude);
2166  }
2167  // Include some uses
2168  if (is_array($include)) {
2169  $includeUsers = implode(",", $include);
2170  } elseif ($include == 'hierarchy') {
2171  // Build list includeUsers to have only hierarchy
2172  $includeUsers = implode(",", $user->getAllChildIds(0));
2173  } elseif ($include == 'hierarchyme') {
2174  // Build list includeUsers to have only hierarchy and current user
2175  $includeUsers = implode(",", $user->getAllChildIds(1));
2176  }
2177 
2178  $num = 0;
2179 
2180  $out = '';
2181  $outarray = array();
2182  $outarray2 = array();
2183 
2184  // Do we want to show the label of entity into the combo list ?
2185  $showlabelofentity = isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1 && !empty($user->admin) && empty($user->entity);
2186  $userissuperadminentityone = isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && empty($user->entity);
2187 
2188  // Forge request to select users
2189  $sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.gender, u.photo";
2190  if ($showlabelofentity) {
2191  $sql .= ", e.label";
2192  }
2193  $sql .= " FROM " . $this->db->prefix() . "user as u";
2194  if ($showlabelofentity) {
2195  $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid = u.entity";
2196  }
2197  // Condition here should be the same than into societe->getSalesRepresentatives().
2198  if ($userissuperadminentityone && $force_entity != 'default') {
2199  if (!empty($force_entity)) {
2200  $sql .= " WHERE u.entity IN (0, " . $this->db->sanitize($force_entity) . ")";
2201  } else {
2202  $sql .= " WHERE u.entity IS NOT NULL";
2203  }
2204  } else {
2205  if (isModEnabled('multicompany') && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2206  $sql .= " WHERE u.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
2207  } else {
2208  $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
2209  }
2210  }
2211 
2212  if (!empty($user->socid)) {
2213  $sql .= " AND u.fk_soc = " . ((int) $user->socid);
2214  }
2215  if (is_array($exclude) && $excludeUsers) {
2216  $sql .= " AND u.rowid NOT IN (" . $this->db->sanitize($excludeUsers) . ")";
2217  }
2218  if ($includeUsers) {
2219  $sql .= " AND u.rowid IN (" . $this->db->sanitize($includeUsers) . ")";
2220  }
2221  if (getDolGlobalString('USER_HIDE_INACTIVE_IN_COMBOBOX') || $notdisabled) {
2222  $sql .= " AND u.statut <> 0";
2223  }
2224  if (getDolGlobalString('USER_HIDE_NONEMPLOYEE_IN_COMBOBOX') || $notdisabled) {
2225  $sql .= " AND u.employee <> 0";
2226  }
2227  if (getDolGlobalString('USER_HIDE_EXTERNAL_IN_COMBOBOX') || $notdisabled) {
2228  $sql .= " AND u.fk_soc IS NULL";
2229  }
2230  if (!empty($morefilter)) {
2231  $sql .= " " . $morefilter;
2232  }
2233 
2234  //Add hook to filter on user (for example on usergroup define in custom modules)
2235  $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
2236  if (!empty($reshook)) {
2237  $sql .= $hookmanager->resPrint;
2238  }
2239 
2240  if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) { // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
2241  $sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
2242  } else {
2243  $sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
2244  }
2245 
2246  dol_syslog(get_class($this) . "::select_dolusers", LOG_DEBUG);
2247 
2248  $resql = $this->db->query($sql);
2249  if ($resql) {
2250  $num = $this->db->num_rows($resql);
2251  $i = 0;
2252  if ($num) {
2253  // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
2254  $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : ' minwidth200') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
2255  if ($show_empty && !$multiple) {
2256  $textforempty = ' ';
2257  if (!empty($conf->use_javascript_ajax)) {
2258  $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
2259  }
2260  if (!is_numeric($show_empty)) {
2261  $textforempty = $show_empty;
2262  }
2263  $out .= '<option class="optiongrey" value="' . ($show_empty < 0 ? $show_empty : -1) . '"' . ((empty($selected) || in_array(-1, $selected)) ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
2264  }
2265  if ($show_every) {
2266  $out .= '<option value="-2"' . ((in_array(-2, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("Everybody") . ' --</option>' . "\n";
2267  }
2268 
2269  $userstatic = new User($this->db);
2270 
2271  while ($i < $num) {
2272  $obj = $this->db->fetch_object($resql);
2273 
2274  $userstatic->id = $obj->rowid;
2275  $userstatic->lastname = $obj->lastname;
2276  $userstatic->firstname = $obj->firstname;
2277  $userstatic->photo = $obj->photo;
2278  $userstatic->status = $obj->status;
2279  $userstatic->entity = $obj->entity;
2280  $userstatic->admin = $obj->admin;
2281  $userstatic->gender = $obj->gender;
2282 
2283  $disableline = '';
2284  if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
2285  $disableline = ($enableonlytext ? $enableonlytext : '1');
2286  }
2287 
2288  $labeltoshow = '';
2289  $labeltoshowhtml = '';
2290 
2291  // $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2292  $fullNameMode = 0;
2293  if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) {
2294  $fullNameMode = 1; //Firstname+lastname
2295  }
2296  $labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2297  $labeltoshowhtml .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2298  if (empty($obj->firstname) && empty($obj->lastname)) {
2299  $labeltoshow .= $obj->login;
2300  $labeltoshowhtml .= $obj->login;
2301  }
2302 
2303  // Complete name with a more info string like: ' (info1 - info2 - ...)'
2304  $moreinfo = '';
2305  $moreinfohtml = '';
2306  if (getDolGlobalString('MAIN_SHOW_LOGIN')) {
2307  $moreinfo .= ($moreinfo ? ' - ' : ' (');
2308  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(');
2309  $moreinfo .= $obj->login;
2310  $moreinfohtml .= $obj->login;
2311  }
2312  if ($showstatus >= 0) {
2313  if ($obj->status == 1 && $showstatus == 1) {
2314  $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Enabled');
2315  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Enabled');
2316  }
2317  if ($obj->status == 0 && $showstatus == 1) {
2318  $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Disabled');
2319  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Disabled');
2320  }
2321  }
2322  if ($showlabelofentity) {
2323  if (empty($obj->entity)) {
2324  $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans("AllEntities");
2325  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans("AllEntities");
2326  } else {
2327  if ($obj->entity != $conf->entity) {
2328  $moreinfo .= ($moreinfo ? ' - ' : ' (') . ($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2329  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2330  }
2331  }
2332  }
2333  $moreinfo .= (!empty($moreinfo) ? ')' : '');
2334  $moreinfohtml .= (!empty($moreinfohtml) ? ')</span>' : '');
2335  if (!empty($disableline) && $disableline != '1') {
2336  // Add text from $enableonlytext parameter
2337  $moreinfo .= ' - ' . $disableline;
2338  $moreinfohtml .= ' - ' . $disableline;
2339  }
2340  $labeltoshow .= $moreinfo;
2341  $labeltoshowhtml .= $moreinfohtml;
2342 
2343  $out .= '<option value="' . $obj->rowid . '"';
2344  if (!empty($disableline)) {
2345  $out .= ' disabled';
2346  }
2347  if ((!empty($selected[0]) && is_object($selected[0])) ? $selected[0]->id == $obj->rowid : in_array($obj->rowid, $selected)) {
2348  $out .= ' selected';
2349  }
2350  $out .= ' data-html="';
2351 
2352  $outhtml = $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1) . ' ';
2353  if ($showstatus >= 0 && $obj->status == 0) {
2354  $outhtml .= '<strike class="opacitymediumxxx">';
2355  }
2356  $outhtml .= $labeltoshowhtml;
2357  if ($showstatus >= 0 && $obj->status == 0) {
2358  $outhtml .= '</strike>';
2359  }
2360  $labeltoshowhtml = $outhtml;
2361 
2362  $out .= dol_escape_htmltag($outhtml);
2363  $out .= '">';
2364  $out .= $labeltoshow;
2365  $out .= '</option>';
2366 
2367  $outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength) . $moreinfo;
2368  $outarray2[$userstatic->id] = array(
2369  'id' => $userstatic->id,
2370  'label' => $labeltoshow,
2371  'labelhtml' => $labeltoshowhtml,
2372  'color' => '',
2373  'picto' => ''
2374  );
2375 
2376  $i++;
2377  }
2378  } else {
2379  $out .= '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '" disabled>';
2380  $out .= '<option value="">' . $langs->trans("None") . '</option>';
2381  }
2382  $out .= '</select>';
2383 
2384  if ($num && !$forcecombo) {
2385  // Enhance with select2
2386  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2387  $out .= ajax_combobox($htmlname);
2388  }
2389  } else {
2390  dol_print_error($this->db);
2391  }
2392 
2393  $this->num = $num;
2394 
2395  if ($outputmode == 2) {
2396  return $outarray2;
2397  } elseif ($outputmode) {
2398  return $outarray;
2399  }
2400 
2401  return $out;
2402  }
2403 
2404 
2405  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2428  public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = array(), $enableonly = array(), $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array())
2429  {
2430  // phpcs:enable
2431  global $langs;
2432 
2433  $userstatic = new User($this->db);
2434  $out = '';
2435 
2436  if (!empty($_SESSION['assignedtouser'])) {
2437  $assignedtouser = json_decode($_SESSION['assignedtouser'], true);
2438  if (!is_array($assignedtouser)) {
2439  $assignedtouser = array();
2440  }
2441  } else {
2442  $assignedtouser = array();
2443  }
2444  $nbassignetouser = count($assignedtouser);
2445 
2446  //if ($nbassignetouser && $action != 'view') $out .= '<br>';
2447  if ($nbassignetouser) {
2448  $out .= '<ul class="attendees">';
2449  }
2450  $i = 0;
2451  $ownerid = 0;
2452  foreach ($assignedtouser as $key => $value) {
2453  if ($value['id'] == $ownerid) {
2454  continue;
2455  }
2456 
2457  $out .= '<li>';
2458  $userstatic->fetch($value['id']);
2459  $out .= $userstatic->getNomUrl(-1);
2460  if ($i == 0) {
2461  $ownerid = $value['id'];
2462  $out .= ' (' . $langs->trans("Owner") . ')';
2463  }
2464  if ($nbassignetouser > 1 && $action != 'view') {
2465  $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 . '">';
2466  }
2467  // Show my availability
2468  if ($showproperties) {
2469  if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
2470  $out .= '<div class="myavailability inline-block">';
2471  $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>';
2472  $out .= '</div>';
2473  }
2474  }
2475  //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
2476  //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
2477 
2478  $out .= '</li>';
2479  $i++;
2480  }
2481  if ($nbassignetouser) {
2482  $out .= '</ul>';
2483  }
2484 
2485  // Method with no ajax
2486  if ($action != 'view') {
2487  $out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
2488  $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
2489  $out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
2490  $out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
2491  $out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#' . $action . 'assignedtouser").attr("disabled", false); }';
2492  $out .= ' else { jQuery("#' . $action . 'assignedtouser").attr("disabled", true); }';
2493  $out .= '});';
2494  $out .= '})</script>';
2495  $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
2496  $out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp reposition" id="' . $action . 'assignedtouser" name="' . $action . 'assignedtouser" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
2497  $out .= '<br>';
2498  }
2499 
2500  return $out;
2501  }
2502 
2503  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2523  public function select_dolresources_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = array(), $enableonly = array(), $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofresourceid = array())
2524  {
2525  // phpcs:enable
2526  global $langs;
2527 
2528  require_once DOL_DOCUMENT_ROOT.'/resource/class/html.formresource.class.php';
2529  require_once DOL_DOCUMENT_ROOT.'/resource/class/dolresource.class.php';
2530  $formresources = new FormResource($this->db);
2531  $resourcestatic = new Dolresource($this->db);
2532 
2533  $out = '';
2534  if (!empty($_SESSION['assignedtoresource'])) {
2535  $assignedtoresource = json_decode($_SESSION['assignedtoresource'], true);
2536  if (!is_array($assignedtoresource)) {
2537  $assignedtoresource = array();
2538  }
2539  } else {
2540  $assignedtoresource = array();
2541  }
2542  $nbassignetoresource = count($assignedtoresource);
2543 
2544  //if ($nbassignetoresource && $action != 'view') $out .= '<br>';
2545  if ($nbassignetoresource) {
2546  $out .= '<ul class="attendees">';
2547  }
2548  $i = 0;
2549 
2550  foreach ($assignedtoresource as $key => $value) {
2551  $out .= '<li>';
2552  $resourcestatic->fetch($value['id']);
2553  $out .= $resourcestatic->getNomUrl(-1);
2554  if ($nbassignetoresource > 1 && $action != 'view') {
2555  $out .= ' <input type="image" style="border: 0px;" src="' . img_picto($langs->trans("Remove"), 'delete', '', 0, 1) . '" value="' . $resourcestatic->id . '" class="removedassigned reposition" id="removedassignedresource_' . $resourcestatic->id . '" name="removedassignedresource_' . $resourcestatic->id . '">';
2556  }
2557  // Show my availability
2558  if ($showproperties) {
2559  if (is_array($listofresourceid) && count($listofresourceid)) {
2560  $out .= '<div class="myavailability inline-block">';
2561  $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;<span class="opacitymedium">' . $langs->trans("Availability") . ':</span> </span><input id="transparencyresource" class="paddingrightonly" ' . ($action == 'view' ? 'disabled' : '') . ' type="checkbox" name="transparency"' . ($listofresourceid[$value['id']]['transparency'] ? ' checked' : '') . '><label for="transparency">' . $langs->trans("Busy") . '</label>';
2562  $out .= '</div>';
2563  }
2564  }
2565  //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
2566  //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
2567 
2568  $out .= '</li>';
2569  $i++;
2570  }
2571  if ($nbassignetoresource) {
2572  $out .= '</ul>';
2573  }
2574 
2575  // Method with no ajax
2576  if ($action != 'view') {
2577  $out .= '<input type="hidden" class="removedassignedhidden" name="removedassignedresource" value="">';
2578  $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
2579  $out .= 'jQuery(".removedassignedresource").click(function() { jQuery(".removedassignedresourcehidden").val(jQuery(this).val()); });';
2580  $out .= 'jQuery(".assignedtoresource").change(function() { console.log(jQuery(".assignedtoresource option:selected").val());';
2581  $out .= ' if (jQuery(".assignedtoresource option:selected").val() > 0) { jQuery("#' . $action . 'assignedtoresource").attr("disabled", false); }';
2582  $out .= ' else { jQuery("#' . $action . 'assignedtoresource").attr("disabled", true); }';
2583  $out .= '});';
2584  $out .= '})</script>';
2585 
2586  $events = array();
2587  $out .= img_picto('', 'resource', 'class="pictofixedwidth"');
2588  $out .= $formresources->select_resource_list(0, $htmlname, [], 1, 1, 0, $events, array(), 2, 0);
2589  //$out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
2590  $out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp reposition" id="' . $action . 'assignedtoresource" name="' . $action . 'assignedtoresource" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
2591  $out .= '<br>';
2592  }
2593 
2594  return $out;
2595  }
2596 
2597  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2598 
2627  public function select_produits($selected = 0, $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)
2628  {
2629  // phpcs:enable
2630  global $langs, $conf;
2631 
2632  $out = '';
2633 
2634  // check parameters
2635  $price_level = (!empty($price_level) ? $price_level : 0);
2636  if (is_null($ajaxoptions)) {
2637  $ajaxoptions = array();
2638  }
2639 
2640  if (strval($filtertype) === '' && (isModEnabled("product") || isModEnabled("service"))) {
2641  if (isModEnabled("product") && !isModEnabled('service')) {
2642  $filtertype = '0';
2643  } elseif (!isModEnabled('product') && isModEnabled("service")) {
2644  $filtertype = '1';
2645  }
2646  }
2647 
2648  if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
2649  $placeholder = '';
2650 
2651  if ($selected && empty($selected_input_value)) {
2652  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
2653  $producttmpselect = new Product($this->db);
2654  $producttmpselect->fetch($selected);
2655  $selected_input_value = $producttmpselect->ref;
2656  unset($producttmpselect);
2657  }
2658  // handle case where product or service module is disabled + no filter specified
2659  if ($filtertype == '') {
2660  if (!isModEnabled('product')) { // when product module is disabled, show services only
2661  $filtertype = 1;
2662  } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
2663  $filtertype = 0;
2664  }
2665  }
2666  // mode=1 means customers products
2667  $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;
2668  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
2669 
2670  if (isModEnabled('variants') && is_array($selected_combinations)) {
2671  // Code to automatically insert with javascript the select of attributes under the select of product
2672  // when a parent of variant has been selected.
2673  $out .= '
2674  <!-- script to auto show attributes select tags if a variant was selected -->
2675  <script nonce="' . getNonce() . '">
2676  // auto show attributes fields
2677  selected = ' . json_encode($selected_combinations) . ';
2678  combvalues = {};
2679 
2680  jQuery(document).ready(function () {
2681 
2682  jQuery("input[name=\'prod_entry_mode\']").change(function () {
2683  if (jQuery(this).val() == \'free\') {
2684  jQuery(\'div#attributes_box\').empty();
2685  }
2686  });
2687 
2688  jQuery("input#' . $htmlname . '").change(function () {
2689 
2690  if (!jQuery(this).val()) {
2691  jQuery(\'div#attributes_box\').empty();
2692  return;
2693  }
2694 
2695  console.log("A change has started. We get variants fields to inject html select");
2696 
2697  jQuery.getJSON("' . DOL_URL_ROOT . '/variants/ajax/getCombinations.php", {
2698  id: jQuery(this).val()
2699  }, function (data) {
2700  jQuery(\'div#attributes_box\').empty();
2701 
2702  jQuery.each(data, function (key, val) {
2703 
2704  combvalues[val.id] = val.values;
2705 
2706  var span = jQuery(document.createElement(\'div\')).css({
2707  \'display\': \'table-row\'
2708  });
2709 
2710  span.append(
2711  jQuery(document.createElement(\'div\')).text(val.label).css({
2712  \'font-weight\': \'bold\',
2713  \'display\': \'table-cell\'
2714  })
2715  );
2716 
2717  var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
2718  \'margin-left\': \'15px\',
2719  \'white-space\': \'pre\'
2720  }).append(
2721  jQuery(document.createElement(\'option\')).val(\'\')
2722  );
2723 
2724  jQuery.each(combvalues[val.id], function (key, val) {
2725  var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
2726 
2727  if (selected[val.fk_product_attribute] == val.id) {
2728  tag.attr(\'selected\', \'selected\');
2729  }
2730 
2731  html.append(tag);
2732  });
2733 
2734  span.append(html);
2735  jQuery(\'div#attributes_box\').append(span);
2736  });
2737  })
2738  });
2739 
2740  ' . ($selected ? 'jQuery("input#' . $htmlname . '").change();' : '') . '
2741  });
2742  </script>
2743  ';
2744  }
2745 
2746  if (empty($hidelabel)) {
2747  $out .= $langs->trans("RefOrLabel") . ' : ';
2748  } elseif ($hidelabel > 1) {
2749  $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
2750  if ($hidelabel == 2) {
2751  $out .= img_picto($langs->trans("Search"), 'search');
2752  }
2753  }
2754  $out .= '<input type="text" class="minwidth100' . ($morecss ? ' ' . $morecss : '') . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
2755  if ($hidelabel == 3) {
2756  $out .= img_picto($langs->trans("Search"), 'search');
2757  }
2758  } else {
2759  $out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus, $status_purchase);
2760  }
2761 
2762  if (empty($nooutput)) {
2763  print $out;
2764  } else {
2765  return $out;
2766  }
2767  }
2768 
2769  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2770 
2786  public function select_bom($selected = '', $htmlname = 'bom_id', $limit = 0, $status = 1, $type = 0, $showempty = '1', $morecss = '', $nooutput = '', $forcecombo = 0, $TProducts = [])
2787  {
2788  // phpcs:enable
2789  global $db;
2790 
2791  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
2792 
2793  $error = 0;
2794  $out = '';
2795 
2796  if (!$forcecombo) {
2797  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2798  $events = array();
2799  $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
2800  }
2801 
2802  $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
2803 
2804  $sql = 'SELECT b.rowid, b.ref, b.label, b.fk_product';
2805  $sql .= ' FROM ' . MAIN_DB_PREFIX . 'bom_bom as b';
2806  $sql .= ' WHERE b.entity IN (' . getEntity('bom') . ')';
2807  if (!empty($status)) {
2808  $sql .= ' AND status = ' . (int) $status;
2809  }
2810  if (!empty($type)) {
2811  $sql .= ' AND bomtype = ' . (int) $type;
2812  }
2813  if (!empty($TProducts)) {
2814  $sql .= ' AND fk_product IN (' . $this->db->sanitize(implode(',', $TProducts)) . ')';
2815  }
2816  if (!empty($limit)) {
2817  $sql .= ' LIMIT ' . (int) $limit;
2818  }
2819  $resql = $db->query($sql);
2820  if ($resql) {
2821  if ($showempty) {
2822  $out .= '<option value="-1"';
2823  if (empty($selected)) {
2824  $out .= ' selected';
2825  }
2826  $out .= '>&nbsp;</option>';
2827  }
2828  while ($obj = $db->fetch_object($resql)) {
2829  $product = new Product($db);
2830  $res = $product->fetch($obj->fk_product);
2831  $out .= '<option value="' . $obj->rowid . '"';
2832  if ($obj->rowid == $selected) {
2833  $out .= 'selected';
2834  }
2835  $out .= '>' . $obj->ref . ' - ' . $product->label . ' - ' . $obj->label . '</option>';
2836  }
2837  } else {
2838  $error++;
2839  dol_print_error($db);
2840  }
2841  $out .= '</select>';
2842  if (empty($nooutput)) {
2843  print $out;
2844  } else {
2845  return $out;
2846  }
2847  }
2848 
2849  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2850 
2876  public function select_produits_list($selected = 0, $htmlname = 'productid', $filtertype = '', $limit = 20, $price_level = 0, $filterkey = '', $status = 1, $finished = 2, $outputmode = 0, $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = 'maxwidth500', $hidepriceinlabel = 0, $warehouseStatus = '', $status_purchase = -1)
2877  {
2878  // phpcs:enable
2879  global $langs;
2880  global $hookmanager;
2881 
2882  $out = '';
2883  $outarray = array();
2884 
2885  // Units
2886  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2887  $langs->load('other');
2888  }
2889 
2890  $warehouseStatusArray = array();
2891  if (!empty($warehouseStatus)) {
2892  require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
2893  if (preg_match('/warehouseclosed/', $warehouseStatus)) {
2894  $warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
2895  }
2896  if (preg_match('/warehouseopen/', $warehouseStatus)) {
2897  $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
2898  }
2899  if (preg_match('/warehouseinternal/', $warehouseStatus)) {
2900  $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
2901  }
2902  }
2903 
2904  $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";
2905  if (count($warehouseStatusArray)) {
2906  $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
2907  } else {
2908  $selectFieldsGrouped = ", " . $this->db->ifsql("p.stock IS NULL", 0, "p.stock") . " AS stock";
2909  }
2910 
2911  $sql = "SELECT ";
2912 
2913  // Add select from hooks
2914  $parameters = array();
2915  $reshook = $hookmanager->executeHooks('selectProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
2916  if (empty($reshook)) {
2917  $sql .= $selectFields.$selectFieldsGrouped.$hookmanager->resPrint;
2918  } else {
2919  $sql .= $hookmanager->resPrint;
2920  }
2921 
2922  if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
2923  //Product category
2924  $sql .= ", (SELECT " . $this->db->prefix() . "categorie_product.fk_categorie
2925  FROM " . $this->db->prefix() . "categorie_product
2926  WHERE " . $this->db->prefix() . "categorie_product.fk_product=p.rowid
2927  LIMIT 1
2928  ) AS categorie_product_id ";
2929  }
2930 
2931  //Price by customer
2932  if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') && !empty($socid)) {
2933  $sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
2934  $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';
2935  $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custdefault_vat_code, custref";
2936  }
2937  // Units
2938  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2939  $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";
2940  $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';
2941  }
2942 
2943  // Multilang : we add translation
2944  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2945  $sql .= ", pl.label as label_translated";
2946  $sql .= ", pl.description as description_translated";
2947  $selectFields .= ", label_translated";
2948  $selectFields .= ", description_translated";
2949  }
2950  // Price by quantity
2951  if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2952  $sql .= ", (SELECT pp.rowid FROM " . $this->db->prefix() . "product_price as pp WHERE pp.fk_product = p.rowid";
2953  if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2954  $sql .= " AND price_level = " . ((int) $price_level);
2955  }
2956  $sql .= " ORDER BY date_price";
2957  $sql .= " DESC LIMIT 1) as price_rowid";
2958  $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
2959  if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
2960  $sql .= " AND price_level = " . ((int) $price_level);
2961  }
2962  $sql .= " ORDER BY date_price";
2963  $sql .= " DESC LIMIT 1) as price_by_qty";
2964  $selectFields .= ", price_rowid, price_by_qty";
2965  }
2966 
2967  $sql .= " FROM ".$this->db->prefix()."product as p";
2968 
2969  if (getDolGlobalString('MAIN_SEARCH_PRODUCT_FORCE_INDEX')) {
2970  $sql .= " USE INDEX (" . $this->db->sanitize(getDolGlobalString('MAIN_PRODUCT_FORCE_INDEX')) . ")";
2971  }
2972 
2973  // Add from (left join) from hooks
2974  $parameters = array();
2975  $reshook = $hookmanager->executeHooks('selectProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
2976  $sql .= $hookmanager->resPrint;
2977 
2978  if (count($warehouseStatusArray)) {
2979  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_stock as ps on ps.fk_product = p.rowid";
2980  $sql .= " LEFT JOIN " . $this->db->prefix() . "entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (" . getEntity('stock') . ")";
2981  $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.
2982  }
2983 
2984  // include search in supplier ref
2985  if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
2986  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
2987  }
2988 
2989  //Price by customer
2990  if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') && !empty($socid)) {
2991  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_customer_price as pcp ON pcp.fk_soc=" . ((int) $socid) . " AND pcp.fk_product=p.rowid";
2992  }
2993  // Units
2994  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2995  $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units u ON u.rowid = p.fk_unit";
2996  }
2997  // Multilang : we add translation
2998  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2999  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_lang as pl ON pl.fk_product = p.rowid ";
3000  if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && !empty($socid)) {
3001  require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
3002  $soc = new Societe($this->db);
3003  $result = $soc->fetch($socid);
3004  if ($result > 0 && !empty($soc->default_lang)) {
3005  $sql .= " AND pl.lang = '" . $this->db->escape($soc->default_lang) . "'";
3006  } else {
3007  $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3008  }
3009  } else {
3010  $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3011  }
3012  }
3013 
3014  if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD')) {
3015  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_attribute_combination pac ON pac.fk_product_child = p.rowid";
3016  }
3017 
3018  $sql .= ' WHERE p.entity IN (' . getEntity('product') . ')';
3019 
3020  if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD')) {
3021  $sql .= " AND pac.rowid IS NULL";
3022  }
3023 
3024  if ($finished == 0) {
3025  $sql .= " AND p.finished = " . ((int) $finished);
3026  } elseif ($finished == 1) {
3027  $sql .= " AND p.finished = ".((int) $finished);
3028  }
3029  if ($status >= 0) {
3030  $sql .= " AND p.tosell = ".((int) $status);
3031  }
3032  if ($status_purchase >= 0) {
3033  $sql .= " AND p.tobuy = " . ((int) $status_purchase);
3034  }
3035  // Filter by product type
3036  if (strval($filtertype) != '') {
3037  $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
3038  } elseif (!isModEnabled('product')) { // when product module is disabled, show services only
3039  $sql .= " AND p.fk_product_type = 1";
3040  } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
3041  $sql .= " AND p.fk_product_type = 0";
3042  }
3043  // Add where from hooks
3044  $parameters = array();
3045  $reshook = $hookmanager->executeHooks('selectProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3046  $sql .= $hookmanager->resPrint;
3047  // Add criteria on ref/label
3048  if ($filterkey != '') {
3049  $sql .= ' AND (';
3050  $prefix = !getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3051  // For natural search
3052  $search_crit = explode(' ', $filterkey);
3053  $i = 0;
3054  if (count($search_crit) > 1) {
3055  $sql .= "(";
3056  }
3057  foreach ($search_crit as $crit) {
3058  if ($i > 0) {
3059  $sql .= " AND ";
3060  }
3061  $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3062  if (getDolGlobalInt('MAIN_MULTILANGS')) {
3063  $sql .= " OR pl.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3064  }
3065  if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') && !empty($socid)) {
3066  $sql .= " OR pcp.ref_customer LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3067  }
3068  if (getDolGlobalString('PRODUCT_AJAX_SEARCH_ON_DESCRIPTION')) {
3069  $sql .= " OR p.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3070  if (getDolGlobalInt('MAIN_MULTILANGS')) {
3071  $sql .= " OR pl.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3072  }
3073  }
3074  if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3075  $sql .= " OR pfp.ref_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3076  }
3077  $sql .= ")";
3078  $i++;
3079  }
3080  if (count($search_crit) > 1) {
3081  $sql .= ")";
3082  }
3083  if (isModEnabled('barcode')) {
3084  $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3085  }
3086  $sql .= ')';
3087  }
3088  if (count($warehouseStatusArray)) {
3089  $sql .= " GROUP BY " . $selectFields;
3090  }
3091 
3092  //Sort by category
3093  if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
3094  $sql .= " ORDER BY categorie_product_id ";
3095  //ASC OR DESC order
3096  (getDolGlobalInt('PRODUCT_SORT_BY_CATEGORY') == 1) ? $sql .= "ASC" : $sql .= "DESC";
3097  } else {
3098  $sql .= $this->db->order("p.ref");
3099  }
3100 
3101  $sql .= $this->db->plimit($limit, 0);
3102 
3103  // Build output string
3104  dol_syslog(get_class($this) . "::select_produits_list search products", LOG_DEBUG);
3105  $result = $this->db->query($sql);
3106  if ($result) {
3107  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3108  require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3109  require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
3110 
3111  $num = $this->db->num_rows($result);
3112 
3113  $events = array();
3114 
3115  if (!$forcecombo) {
3116  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3117  $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
3118  }
3119 
3120  $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
3121 
3122  $textifempty = '';
3123  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
3124  //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
3125  if (getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3126  if ($showempty && !is_numeric($showempty)) {
3127  $textifempty = $langs->trans($showempty);
3128  } else {
3129  $textifempty .= $langs->trans("All");
3130  }
3131  } else {
3132  if ($showempty && !is_numeric($showempty)) {
3133  $textifempty = $langs->trans($showempty);
3134  }
3135  }
3136  if ($showempty) {
3137  $out .= '<option value="-1" selected>' . ($textifempty ? $textifempty : '&nbsp;') . '</option>';
3138  }
3139 
3140  $i = 0;
3141  while ($num && $i < $num) {
3142  $opt = '';
3143  $optJson = array();
3144  $objp = $this->db->fetch_object($result);
3145 
3146  if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('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
3147  $sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
3148  $sql .= " FROM " . $this->db->prefix() . "product_price_by_qty";
3149  $sql .= " WHERE fk_product_price = " . ((int) $objp->price_rowid);
3150  $sql .= " ORDER BY quantity ASC";
3151 
3152  dol_syslog(get_class($this) . "::select_produits_list search prices by qty", LOG_DEBUG);
3153  $result2 = $this->db->query($sql);
3154  if ($result2) {
3155  $nb_prices = $this->db->num_rows($result2);
3156  $j = 0;
3157  while ($nb_prices && $j < $nb_prices) {
3158  $objp2 = $this->db->fetch_object($result2);
3159 
3160  $objp->price_by_qty_rowid = $objp2->rowid;
3161  $objp->price_by_qty_price_base_type = $objp2->price_base_type;
3162  $objp->price_by_qty_quantity = $objp2->quantity;
3163  $objp->price_by_qty_unitprice = $objp2->unitprice;
3164  $objp->price_by_qty_remise_percent = $objp2->remise_percent;
3165  // For backward compatibility
3166  $objp->quantity = $objp2->quantity;
3167  $objp->price = $objp2->price;
3168  $objp->unitprice = $objp2->unitprice;
3169  $objp->remise_percent = $objp2->remise_percent;
3170 
3171  //$objp->tva_tx is not overwritten by $objp2 value
3172  //$objp->default_vat_code is not overwritten by $objp2 value
3173 
3174  $this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
3175 
3176  $j++;
3177 
3178  // Add new entry
3179  // "key" value of json key array is used by jQuery automatically as selected value
3180  // "label" value of json key array is used by jQuery automatically as text for combo box
3181  $out .= $opt;
3182  array_push($outarray, $optJson);
3183  }
3184  }
3185  } else {
3186  if (isModEnabled('dynamicprices') && !empty($objp->fk_price_expression)) {
3187  $price_product = new Product($this->db);
3188  $price_product->fetch($objp->rowid, '', '', 1);
3189 
3190  require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3191  $priceparser = new PriceParser($this->db);
3192  $price_result = $priceparser->parseProduct($price_product);
3193  if ($price_result >= 0) {
3194  $objp->price = $price_result;
3195  $objp->unitprice = $price_result;
3196  //Calculate the VAT
3197  $objp->price_ttc = (float) price2num($objp->price) * (1 + ($objp->tva_tx / 100));
3198  $objp->price_ttc = price2num($objp->price_ttc, 'MU');
3199  }
3200  }
3201 
3202  $this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
3203  // Add new entry
3204  // "key" value of json key array is used by jQuery automatically as selected value
3205  // "label" value of json key array is used by jQuery automatically as text for combo box
3206  $out .= $opt;
3207  array_push($outarray, $optJson);
3208  }
3209 
3210  $i++;
3211  }
3212 
3213  $out .= '</select>';
3214 
3215  $this->db->free($result);
3216 
3217  if (empty($outputmode)) {
3218  return $out;
3219  }
3220 
3221  return $outarray;
3222  } else {
3223  dol_print_error($this->db);
3224  }
3225 
3226  return '';
3227  }
3228 
3244  protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
3245  {
3246  global $langs, $conf, $user;
3247  global $hookmanager;
3248 
3249  $outkey = '';
3250  $outval = '';
3251  $outref = '';
3252  $outlabel = '';
3253  $outlabel_translated = '';
3254  $outdesc = '';
3255  $outdesc_translated = '';
3256  $outbarcode = '';
3257  $outorigin = '';
3258  $outtype = '';
3259  $outprice_ht = '';
3260  $outprice_ttc = '';
3261  $outpricebasetype = '';
3262  $outtva_tx = '';
3263  $outdefault_vat_code = '';
3264  $outqty = 1;
3265  $outdiscount = 0;
3266 
3267  $maxlengtharticle = (!getDolGlobalString('PRODUCT_MAX_LENGTH_COMBO') ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3268 
3269  $label = $objp->label;
3270  if (!empty($objp->label_translated)) {
3271  $label = $objp->label_translated;
3272  }
3273  if (!empty($filterkey) && $filterkey != '') {
3274  $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
3275  }
3276 
3277  $outkey = $objp->rowid;
3278  $outref = $objp->ref;
3279  $outrefcust = empty($objp->custref) ? '' : $objp->custref;
3280  $outlabel = $objp->label;
3281  $outdesc = $objp->description;
3282  if (getDolGlobalInt('MAIN_MULTILANGS')) {
3283  $outlabel_translated = $objp->label_translated;
3284  $outdesc_translated = $objp->description_translated;
3285  }
3286  $outbarcode = $objp->barcode;
3287  $outorigin = $objp->fk_country;
3288  $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
3289 
3290  $outtype = $objp->fk_product_type;
3291  $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3292  $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
3293 
3294  if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
3295  require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
3296  }
3297 
3298  // Units
3299  $outvalUnits = '';
3300  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3301  if (!empty($objp->unit_short)) {
3302  $outvalUnits .= ' - ' . $objp->unit_short;
3303  }
3304  }
3305  if (getDolGlobalString('PRODUCT_SHOW_DIMENSIONS_IN_COMBO')) {
3306  if (!empty($objp->weight) && $objp->weight_units !== null) {
3307  $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3308  $outvalUnits .= ' - ' . $unitToShow;
3309  }
3310  if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3311  $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
3312  $outvalUnits .= ' - ' . $unitToShow;
3313  }
3314  if (!empty($objp->surface) && $objp->surface_units !== null) {
3315  $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3316  $outvalUnits .= ' - ' . $unitToShow;
3317  }
3318  if (!empty($objp->volume) && $objp->volume_units !== null) {
3319  $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3320  $outvalUnits .= ' - ' . $unitToShow;
3321  }
3322  }
3323  if ($outdurationvalue && $outdurationunit) {
3324  $da = array(
3325  'h' => $langs->trans('Hour'),
3326  'd' => $langs->trans('Day'),
3327  'w' => $langs->trans('Week'),
3328  'm' => $langs->trans('Month'),
3329  'y' => $langs->trans('Year')
3330  );
3331  if (isset($da[$outdurationunit])) {
3332  $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
3333  }
3334  }
3335 
3336  $opt = '<option value="' . $objp->rowid . '"';
3337  $opt .= ($objp->rowid == $selected) ? ' selected' : '';
3338  if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
3339  $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 . '"';
3340  }
3341  if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
3342  if ($user->hasRight('stock', 'lire')) {
3343  if ($objp->stock > 0) {
3344  $opt .= ' class="product_line_stock_ok"';
3345  } elseif ($objp->stock <= 0) {
3346  $opt .= ' class="product_line_stock_too_low"';
3347  }
3348  }
3349  }
3350  if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
3351  $opt .= ' data-labeltrans="' . $outlabel_translated . '"';
3352  $opt .= ' data-desctrans="' . dol_escape_htmltag($outdesc_translated) . '"';
3353  }
3354  $opt .= '>';
3355  $opt .= $objp->ref;
3356  if (!empty($objp->custref)) {
3357  $opt .= ' (' . $objp->custref . ')';
3358  }
3359  if ($outbarcode) {
3360  $opt .= ' (' . $outbarcode . ')';
3361  }
3362  $opt .= ' - ' . dol_trunc($label, $maxlengtharticle);
3363  if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
3364  $opt .= ' (' . getCountry($outorigin, 1) . ')';
3365  }
3366 
3367  $objRef = $objp->ref;
3368  if (!empty($objp->custref)) {
3369  $objRef .= ' (' . $objp->custref . ')';
3370  }
3371  if (!empty($filterkey) && $filterkey != '') {
3372  $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
3373  }
3374  $outval .= $objRef;
3375  if ($outbarcode) {
3376  $outval .= ' (' . $outbarcode . ')';
3377  }
3378  $outval .= ' - ' . dol_trunc($label, $maxlengtharticle);
3379  if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
3380  $outval .= ' (' . getCountry($outorigin, 1) . ')';
3381  }
3382 
3383  // Units
3384  $opt .= $outvalUnits;
3385  $outval .= $outvalUnits;
3386 
3387  $found = 0;
3388 
3389  // Multiprice
3390  // If we need a particular price level (from 1 to n)
3391  if (empty($hidepriceinlabel) && $price_level >= 1 && (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES'))) {
3392  $sql = "SELECT price, price_ttc, price_base_type, tva_tx, default_vat_code";
3393  $sql .= " FROM " . $this->db->prefix() . "product_price";
3394  $sql .= " WHERE fk_product = " . ((int) $objp->rowid);
3395  $sql .= " AND entity IN (" . getEntity('productprice') . ")";
3396  $sql .= " AND price_level = " . ((int) $price_level);
3397  $sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
3398  $sql .= " LIMIT 1";
3399 
3400  dol_syslog(get_class($this) . '::constructProductListOption search price for product ' . $objp->rowid . ' AND level ' . $price_level, LOG_DEBUG);
3401  $result2 = $this->db->query($sql);
3402  if ($result2) {
3403  $objp2 = $this->db->fetch_object($result2);
3404  if ($objp2) {
3405  $found = 1;
3406  if ($objp2->price_base_type == 'HT') {
3407  $opt .= ' - ' . price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
3408  $outval .= ' - ' . price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
3409  } else {
3410  $opt .= ' - ' . price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
3411  $outval .= ' - ' . price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
3412  }
3413  $outprice_ht = price($objp2->price);
3414  $outprice_ttc = price($objp2->price_ttc);
3415  $outpricebasetype = $objp2->price_base_type;
3416  if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
3417  $outtva_tx = $objp2->tva_tx; // We use the vat rate on line of multiprice
3418  $outdefault_vat_code = $objp2->default_vat_code; // We use the vat code on line of multiprice
3419  } else {
3420  $outtva_tx = $objp->tva_tx; // We use the vat rate of product, not the one on line of multiprice
3421  $outdefault_vat_code = $objp->default_vat_code; // We use the vat code or product, not the one on line of multiprice
3422  }
3423  }
3424  } else {
3425  dol_print_error($this->db);
3426  }
3427  }
3428 
3429  // Price by quantity
3430  if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1 && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES'))) {
3431  $found = 1;
3432  $outqty = $objp->quantity;
3433  $outdiscount = $objp->remise_percent;
3434  if ($objp->quantity == 1) {
3435  $opt .= ' - ' . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/";
3436  $outval .= ' - ' . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/";
3437  $opt .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3438  $outval .= $langs->transnoentities("Unit");
3439  } else {
3440  $opt .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
3441  $outval .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
3442  $opt .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3443  $outval .= $langs->transnoentities("Units");
3444  }
3445 
3446  $outprice_ht = price($objp->unitprice);
3447  $outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
3448  $outpricebasetype = $objp->price_base_type;
3449  $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
3450  $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
3451  }
3452  if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
3453  $opt .= " (" . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
3454  $outval .= " (" . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->transnoentities("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
3455  }
3456  if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
3457  $opt .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
3458  $outval .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
3459  }
3460 
3461  // Price by customer
3462  if (empty($hidepriceinlabel) && getDolGlobalString('PRODUIT_CUSTOMER_PRICES')) {
3463  if (!empty($objp->idprodcustprice)) {
3464  $found = 1;
3465 
3466  if ($objp->custprice_base_type == 'HT') {
3467  $opt .= ' - ' . price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
3468  $outval .= ' - ' . price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
3469  } else {
3470  $opt .= ' - ' . price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
3471  $outval .= ' - ' . price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
3472  }
3473 
3474  $outprice_ht = price($objp->custprice);
3475  $outprice_ttc = price($objp->custprice_ttc);
3476  $outpricebasetype = $objp->custprice_base_type;
3477  $outtva_tx = $objp->custtva_tx;
3478  $outdefault_vat_code = $objp->custdefault_vat_code;
3479  }
3480  }
3481 
3482  // If level no defined or multiprice not found, we used the default price
3483  if (empty($hidepriceinlabel) && !$found) {
3484  if ($objp->price_base_type == 'HT') {
3485  $opt .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
3486  $outval .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
3487  } else {
3488  $opt .= ' - ' . price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
3489  $outval .= ' - ' . price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
3490  }
3491  $outprice_ht = price($objp->price);
3492  $outprice_ttc = price($objp->price_ttc);
3493  $outpricebasetype = $objp->price_base_type;
3494  $outtva_tx = $objp->tva_tx;
3495  $outdefault_vat_code = $objp->default_vat_code;
3496  }
3497 
3498  if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
3499  if ($user->hasRight('stock', 'lire')) {
3500  $opt .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'));
3501 
3502  if ($objp->stock > 0) {
3503  $outval .= ' - <span class="product_line_stock_ok">';
3504  } elseif ($objp->stock <= 0) {
3505  $outval .= ' - <span class="product_line_stock_too_low">';
3506  }
3507  $outval .= $langs->transnoentities("Stock") . ': ' . price(price2num($objp->stock, 'MS'));
3508  $outval .= '</span>';
3509  if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) { // Warning, this option may slow down combo list generation
3510  $langs->load("stocks");
3511 
3512  $tmpproduct = new Product($this->db);
3513  $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3514  $tmpproduct->load_virtual_stock();
3515  $virtualstock = $tmpproduct->stock_theorique;
3516 
3517  $opt .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
3518 
3519  $outval .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
3520  if ($virtualstock > 0) {
3521  $outval .= '<span class="product_line_stock_ok">';
3522  } elseif ($virtualstock <= 0) {
3523  $outval .= '<span class="product_line_stock_too_low">';
3524  }
3525  $outval .= $virtualstock;
3526  $outval .= '</span>';
3527 
3528  unset($tmpproduct);
3529  }
3530  }
3531  }
3532 
3533  $parameters = array('objp' => $objp);
3534  $reshook = $hookmanager->executeHooks('constructProductListOption', $parameters); // Note that $action and $object may have been modified by hook
3535  if (empty($reshook)) {
3536  $opt .= $hookmanager->resPrint;
3537  } else {
3538  $opt = $hookmanager->resPrint;
3539  }
3540 
3541  $opt .= "</option>\n";
3542  $optJson = array(
3543  'key' => $outkey,
3544  'value' => $outref,
3545  'label' => $outval,
3546  'label2' => $outlabel,
3547  'desc' => $outdesc,
3548  'type' => $outtype,
3549  'price_ht' => price2num($outprice_ht),
3550  'price_ttc' => price2num($outprice_ttc),
3551  'price_ht_locale' => price(price2num($outprice_ht)),
3552  'price_ttc_locale' => price(price2num($outprice_ttc)),
3553  'pricebasetype' => $outpricebasetype,
3554  'tva_tx' => $outtva_tx,
3555  'default_vat_code' => $outdefault_vat_code,
3556  'qty' => $outqty,
3557  'discount' => $outdiscount,
3558  'duration_value' => $outdurationvalue,
3559  'duration_unit' => $outdurationunit,
3560  'pbq' => $outpbq,
3561  'labeltrans' => $outlabel_translated,
3562  'desctrans' => $outdesc_translated,
3563  'ref_customer' => $outrefcust
3564  );
3565  }
3566 
3567  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3568 
3584  public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '')
3585  {
3586  // phpcs:enable
3587  global $langs, $conf;
3588  global $price_level, $status, $finished;
3589 
3590  if (!isset($status)) {
3591  $status = 1;
3592  }
3593 
3594  $selected_input_value = '';
3595  if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3596  if ($selected > 0) {
3597  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3598  $producttmpselect = new Product($this->db);
3599  $producttmpselect->fetch($selected);
3600  $selected_input_value = $producttmpselect->ref;
3601  unset($producttmpselect);
3602  }
3603 
3604  // mode=2 means suppliers products
3605  $urloption = ($socid > 0 ? 'socid=' . $socid . '&' : '') . 'htmlname=' . $htmlname . '&outjson=1&price_level=' . $price_level . '&type=' . $filtertype . '&mode=2&status=' . $status . '&finished=' . $finished . '&alsoproductwithnosupplierprice=' . $alsoproductwithnosupplierprice;
3606  print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/product/ajax/products.php', $urloption, getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
3607 
3608  print($hidelabel ? '' : $langs->trans("RefOrLabel") . ' : ') . '<input type="text" class="'.$morecss.'" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . $placeholder . '"' : '') . '>';
3609  } else {
3610  print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, 0, $placeholder);
3611  }
3612  }
3613 
3614  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3615 
3634  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 = '')
3635  {
3636  // phpcs:enable
3637  global $langs, $conf, $user;
3638  global $hookmanager;
3639 
3640  $out = '';
3641  $outarray = array();
3642 
3643  $maxlengtharticle = (!getDolGlobalString('PRODUCT_MAX_LENGTH_COMBO') ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3644 
3645  $langs->load('stocks');
3646  // Units
3647  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3648  $langs->load('other');
3649  }
3650 
3651  $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,";
3652  $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice, pfp.barcode";
3653  if (isModEnabled('multicurrency')) {
3654  $sql .= ", pfp.multicurrency_code, pfp.multicurrency_unitprice";
3655  }
3656  $sql .= ", pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.default_vat_code, pfp.fk_soc, s.nom as name";
3657  $sql .= ", pfp.supplier_reputation";
3658  // if we use supplier description of the products
3659  if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
3660  $sql .= ", pfp.desc_fourn as description";
3661  } else {
3662  $sql .= ", p.description";
3663  }
3664  // Units
3665  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3666  $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";
3667  }
3668  $sql .= " FROM " . $this->db->prefix() . "product as p";
3669  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (" . getEntity('product') . ") )";
3670  if ($socid > 0) {
3671  $sql .= " AND pfp.fk_soc = " . ((int) $socid);
3672  }
3673  $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
3674  // Units
3675  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3676  $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units u ON u.rowid = p.fk_unit";
3677  }
3678  $sql .= " WHERE p.entity IN (" . getEntity('product') . ")";
3679  if ($statut != -1) {
3680  $sql .= " AND p.tobuy = " . ((int) $statut);
3681  }
3682  if (strval($filtertype) != '') {
3683  $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
3684  }
3685  if (!empty($filtre)) {
3686  $sql .= " " . $filtre;
3687  }
3688  // Add where from hooks
3689  $parameters = array();
3690  $reshook = $hookmanager->executeHooks('selectSuppliersProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3691  $sql .= $hookmanager->resPrint;
3692  // Add criteria on ref/label
3693  if ($filterkey != '') {
3694  $sql .= ' AND (';
3695  $prefix = !getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3696  // For natural search
3697  $search_crit = explode(' ', $filterkey);
3698  $i = 0;
3699  if (count($search_crit) > 1) {
3700  $sql .= "(";
3701  }
3702  foreach ($search_crit as $crit) {
3703  if ($i > 0) {
3704  $sql .= " AND ";
3705  }
3706  $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) . "%'";
3707  if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
3708  $sql .= " OR pfp.desc_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3709  }
3710  $sql .= ")";
3711  $i++;
3712  }
3713  if (count($search_crit) > 1) {
3714  $sql .= ")";
3715  }
3716  if (isModEnabled('barcode')) {
3717  $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3718  $sql .= " OR pfp.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3719  }
3720  $sql .= ')';
3721  }
3722  $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
3723  $sql .= $this->db->plimit($limit, 0);
3724 
3725  // Build output string
3726 
3727  dol_syslog(get_class($this) . "::select_produits_fournisseurs_list", LOG_DEBUG);
3728  $result = $this->db->query($sql);
3729  if ($result) {
3730  require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3731  require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
3732 
3733  $num = $this->db->num_rows($result);
3734 
3735  //$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">'; // remove select to have id same with combo and ajax
3736  $out .= '<select class="flat ' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '">';
3737  if (!$selected) {
3738  $out .= '<option value="-1" selected>' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
3739  } else {
3740  $out .= '<option value="-1">' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
3741  }
3742 
3743  $i = 0;
3744  while ($i < $num) {
3745  $objp = $this->db->fetch_object($result);
3746 
3747  if (is_null($objp->idprodfournprice)) {
3748  // There is no supplier price found, we will use the vat rate for sale
3749  $objp->tva_tx = $objp->tva_tx_sale;
3750  $objp->default_vat_code = $objp->default_vat_code_sale;
3751  }
3752 
3753  $outkey = $objp->idprodfournprice; // id in table of price
3754  if (!$outkey && $alsoproductwithnosupplierprice) {
3755  $outkey = 'idprod_' . $objp->rowid; // id of product
3756  }
3757 
3758  $outref = $objp->ref;
3759  $outbarcode = $objp->barcode;
3760  $outqty = 1;
3761  $outdiscount = 0;
3762  $outtype = $objp->fk_product_type;
3763  $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3764  $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
3765 
3766  // Units
3767  $outvalUnits = '';
3768  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3769  if (!empty($objp->unit_short)) {
3770  $outvalUnits .= ' - ' . $objp->unit_short;
3771  }
3772  if (!empty($objp->weight) && $objp->weight_units !== null) {
3773  $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3774  $outvalUnits .= ' - ' . $unitToShow;
3775  }
3776  if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3777  $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
3778  $outvalUnits .= ' - ' . $unitToShow;
3779  }
3780  if (!empty($objp->surface) && $objp->surface_units !== null) {
3781  $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3782  $outvalUnits .= ' - ' . $unitToShow;
3783  }
3784  if (!empty($objp->volume) && $objp->volume_units !== null) {
3785  $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3786  $outvalUnits .= ' - ' . $unitToShow;
3787  }
3788  if ($outdurationvalue && $outdurationunit) {
3789  $da = array(
3790  'h' => $langs->trans('Hour'),
3791  'd' => $langs->trans('Day'),
3792  'w' => $langs->trans('Week'),
3793  'm' => $langs->trans('Month'),
3794  'y' => $langs->trans('Year')
3795  );
3796  if (isset($da[$outdurationunit])) {
3797  $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
3798  }
3799  }
3800  }
3801 
3802  $objRef = $objp->ref;
3803  if ($filterkey && $filterkey != '') {
3804  $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
3805  }
3806  $objRefFourn = $objp->ref_fourn;
3807  if ($filterkey && $filterkey != '') {
3808  $objRefFourn = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRefFourn, 1);
3809  }
3810  $label = $objp->label;
3811  if ($filterkey && $filterkey != '') {
3812  $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
3813  }
3814 
3815  switch ($objp->fk_product_type) {
3816  case Product::TYPE_PRODUCT:
3817  $picto = 'product';
3818  break;
3819  case Product::TYPE_SERVICE:
3820  $picto = 'service';
3821  break;
3822  default:
3823  $picto = '';
3824  break;
3825  }
3826 
3827  if (empty($picto)) {
3828  $optlabel = '';
3829  } else {
3830  $optlabel = img_object('', $picto, 'class="paddingright classfortooltip"', 0, 0, 1);
3831  }
3832 
3833  $optlabel .= $objp->ref;
3834  if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3835  $optlabel .= ' <span class="opacitymedium">(' . $objp->ref_fourn . ')</span>';
3836  }
3837  if (isModEnabled('barcode') && !empty($objp->barcode)) {
3838  $optlabel .= ' (' . $outbarcode . ')';
3839  }
3840  $optlabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
3841 
3842  $outvallabel = $objRef;
3843  if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3844  $outvallabel .= ' (' . $objRefFourn . ')';
3845  }
3846  if (isModEnabled('barcode') && !empty($objp->barcode)) {
3847  $outvallabel .= ' (' . $outbarcode . ')';
3848  }
3849  $outvallabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
3850 
3851  // Units
3852  $optlabel .= $outvalUnits;
3853  $outvallabel .= $outvalUnits;
3854 
3855  if (!empty($objp->idprodfournprice)) {
3856  $outqty = $objp->quantity;
3857  $outdiscount = $objp->remise_percent;
3858  if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
3859  $prod_supplier = new ProductFournisseur($this->db);
3860  $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3861  $prod_supplier->id = $objp->fk_product;
3862  $prod_supplier->fourn_qty = $objp->quantity;
3863  $prod_supplier->fourn_tva_tx = $objp->tva_tx;
3864  $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3865 
3866  require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3867  $priceparser = new PriceParser($this->db);
3868  $price_result = $priceparser->parseProductSupplier($prod_supplier);
3869  if ($price_result >= 0) {
3870  $objp->fprice = $price_result;
3871  if ($objp->quantity >= 1) {
3872  $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
3873  }
3874  }
3875  }
3876  if ($objp->quantity == 1) {
3877  $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
3878  $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/";
3879  $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3880  $outvallabel .= $langs->transnoentities("Unit");
3881  } else {
3882  $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
3883  $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
3884  $optlabel .= ' ' . $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3885  $outvallabel .= ' ' . $langs->transnoentities("Units");
3886  }
3887 
3888  if ($objp->quantity > 1) {
3889  $optlabel .= " (" . price($objp->unitprice * (getDolGlobalString('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
3890  $outvallabel .= " (" . price($objp->unitprice * (getDolGlobalString('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
3891  }
3892  if ($objp->remise_percent >= 1) {
3893  $optlabel .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
3894  $outvallabel .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
3895  }
3896  if ($objp->duration) {
3897  $optlabel .= " - " . $objp->duration;
3898  $outvallabel .= " - " . $objp->duration;
3899  }
3900  if (!$socid) {
3901  $optlabel .= " - " . dol_trunc($objp->name, 8);
3902  $outvallabel .= " - " . dol_trunc($objp->name, 8);
3903  }
3904  if ($objp->supplier_reputation) {
3905  //TODO dictionary
3906  $reputations = array('' => $langs->trans('Standard'), 'FAVORITE' => $langs->trans('Favorite'), 'NOTTHGOOD' => $langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER' => $langs->trans('DoNotOrderThisProductToThisSupplier'));
3907 
3908  $optlabel .= " - " . $reputations[$objp->supplier_reputation];
3909  $outvallabel .= " - " . $reputations[$objp->supplier_reputation];
3910  }
3911  } else {
3912  $optlabel .= " - <span class='opacitymedium'>" . $langs->trans("NoPriceDefinedForThisSupplier") . '</span>';
3913  $outvallabel .= ' - ' . $langs->transnoentities("NoPriceDefinedForThisSupplier");
3914  }
3915 
3916  if (isModEnabled('stock') && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
3917  $novirtualstock = ($showstockinlist == 2);
3918 
3919  if ($user->hasRight('stock', 'lire')) {
3920  $outvallabel .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'));
3921 
3922  if ($objp->stock > 0) {
3923  $optlabel .= ' - <span class="product_line_stock_ok">';
3924  } elseif ($objp->stock <= 0) {
3925  $optlabel .= ' - <span class="product_line_stock_too_low">';
3926  }
3927  $optlabel .= $langs->transnoentities("Stock") . ':' . price(price2num($objp->stock, 'MS'));
3928  $optlabel .= '</span>';
3929  if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) { // Warning, this option may slow down combo list generation
3930  $langs->load("stocks");
3931 
3932  $tmpproduct = new Product($this->db);
3933  $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3934  $tmpproduct->load_virtual_stock();
3935  $virtualstock = $tmpproduct->stock_theorique;
3936 
3937  $outvallabel .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
3938 
3939  $optlabel .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
3940  if ($virtualstock > 0) {
3941  $optlabel .= '<span class="product_line_stock_ok">';
3942  } elseif ($virtualstock <= 0) {
3943  $optlabel .= '<span class="product_line_stock_too_low">';
3944  }
3945  $optlabel .= $virtualstock;
3946  $optlabel .= '</span>';
3947 
3948  unset($tmpproduct);
3949  }
3950  }
3951  }
3952 
3953  $optstart = '<option value="' . $outkey . '"';
3954  if ($selected && $selected == $objp->idprodfournprice) {
3955  $optstart .= ' selected';
3956  }
3957  if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
3958  $optstart .= ' disabled';
3959  }
3960 
3961  if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
3962  $optstart .= ' data-product-id="' . dol_escape_htmltag($objp->rowid) . '"';
3963  $optstart .= ' data-price-id="' . dol_escape_htmltag($objp->idprodfournprice) . '"';
3964  $optstart .= ' data-qty="' . dol_escape_htmltag($objp->quantity) . '"';
3965  $optstart .= ' data-up="' . dol_escape_htmltag(price2num($objp->unitprice)) . '"';
3966  $optstart .= ' data-up-locale="' . dol_escape_htmltag(price($objp->unitprice)) . '"';
3967  $optstart .= ' data-discount="' . dol_escape_htmltag($outdiscount) . '"';
3968  $optstart .= ' data-tvatx="' . dol_escape_htmltag(price2num($objp->tva_tx)) . '"';
3969  $optstart .= ' data-tvatx-formated="' . dol_escape_htmltag(price($objp->tva_tx, 0, $langs, 1, -1, 2)) . '"';
3970  $optstart .= ' data-default-vat-code="' . dol_escape_htmltag($objp->default_vat_code) . '"';
3971  $optstart .= ' data-supplier-ref="' . dol_escape_htmltag($objp->ref_fourn) . '"';
3972  if (isModEnabled('multicurrency')) {
3973  $optstart .= ' data-multicurrency-code="' . dol_escape_htmltag($objp->multicurrency_code) . '"';
3974  $optstart .= ' data-multicurrency-up="' . dol_escape_htmltag($objp->multicurrency_unitprice) . '"';
3975  }
3976  }
3977  $optstart .= ' data-description="' . dol_escape_htmltag($objp->description, 0, 1) . '"';
3978 
3979  $outarrayentry = array(
3980  'key' => $outkey,
3981  'value' => $outref,
3982  'label' => $outvallabel,
3983  'qty' => $outqty,
3984  'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
3985  'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
3986  'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
3987  'tva_tx_formated' => price($objp->tva_tx, 0, $langs, 1, -1, 2),
3988  'tva_tx' => price2num($objp->tva_tx),
3989  'default_vat_code' => $objp->default_vat_code,
3990  'supplier_ref' => $objp->ref_fourn,
3991  'discount' => $outdiscount,
3992  'type' => $outtype,
3993  'duration_value' => $outdurationvalue,
3994  'duration_unit' => $outdurationunit,
3995  'disabled' => empty($objp->idprodfournprice),
3996  'description' => $objp->description
3997  );
3998  if (isModEnabled('multicurrency')) {
3999  $outarrayentry['multicurrency_code'] = $objp->multicurrency_code;
4000  $outarrayentry['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4001  }
4002 
4003  $parameters = array(
4004  'objp' => &$objp,
4005  'optstart' => &$optstart,
4006  'optlabel' => &$optlabel,
4007  'outvallabel' => &$outvallabel,
4008  'outarrayentry' => &$outarrayentry
4009  );
4010  $reshook = $hookmanager->executeHooks('selectProduitsFournisseurListOption', $parameters, $this);
4011 
4012 
4013  // Add new entry
4014  // "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
4015  // "label" value of json key array is used by jQuery automatically as text for combo box
4016  $out .= $optstart . ' data-html="' . dol_escape_htmltag($optlabel) . '">' . $optlabel . "</option>\n";
4017  $outarraypush = array(
4018  'key' => $outkey,
4019  'value' => $outref,
4020  'label' => $outvallabel,
4021  'qty' => $outqty,
4022  'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
4023  'price_qty_ht_locale' => price($objp->fprice),
4024  'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
4025  'price_unit_ht_locale' => price($objp->unitprice),
4026  'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
4027  'tva_tx_formated' => price($objp->tva_tx),
4028  'tva_tx' => price2num($objp->tva_tx),
4029  'default_vat_code' => $objp->default_vat_code,
4030  'supplier_ref' => $objp->ref_fourn,
4031  'discount' => $outdiscount,
4032  'type' => $outtype,
4033  'duration_value' => $outdurationvalue,
4034  'duration_unit' => $outdurationunit,
4035  'disabled' => empty($objp->idprodfournprice),
4036  'description' => $objp->description
4037  );
4038  if (isModEnabled('multicurrency')) {
4039  $outarraypush['multicurrency_code'] = $objp->multicurrency_code;
4040  $outarraypush['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4041  }
4042  array_push($outarray, $outarraypush);
4043 
4044  // Example of var_dump $outarray
4045  // array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
4046  // ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)"
4047  // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
4048  //}
4049  //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4050  //$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)');
4051  //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4052 
4053  $i++;
4054  }
4055  $out .= '</select>';
4056 
4057  $this->db->free($result);
4058 
4059  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
4060  $out .= ajax_combobox($htmlname);
4061  } else {
4062  dol_print_error($this->db);
4063  }
4064 
4065  if (empty($outputmode)) {
4066  return $out;
4067  }
4068  return $outarray;
4069  }
4070 
4071  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4072 
4081  public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = 0)
4082  {
4083  // phpcs:enable
4084  global $langs, $conf;
4085 
4086  $langs->load('stocks');
4087 
4088  $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
4089  $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
4090  $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
4091  $sql .= " FROM " . $this->db->prefix() . "product as p";
4092  $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
4093  $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
4094  $sql .= " WHERE pfp.entity IN (" . getEntity('productsupplierprice') . ")";
4095  $sql .= " AND p.tobuy = 1";
4096  $sql .= " AND s.fournisseur = 1";
4097  $sql .= " AND p.rowid = " . ((int) $productid);
4098  if (!getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED')) {
4099  $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
4100  } else {
4101  $sql .= " ORDER BY pfp.unitprice ASC";
4102  }
4103 
4104  dol_syslog(get_class($this) . "::select_product_fourn_price", LOG_DEBUG);
4105  $result = $this->db->query($sql);
4106 
4107  if ($result) {
4108  $num = $this->db->num_rows($result);
4109 
4110  $form = '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4111 
4112  if (!$num) {
4113  $form .= '<option value="0">-- ' . $langs->trans("NoSupplierPriceDefinedForThisProduct") . ' --</option>';
4114  } else {
4115  require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4116  $form .= '<option value="0">&nbsp;</option>';
4117 
4118  $i = 0;
4119  while ($i < $num) {
4120  $objp = $this->db->fetch_object($result);
4121 
4122  $opt = '<option value="' . $objp->idprodfournprice . '"';
4123  //if there is only one supplier, preselect it
4124  if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier) || ($i == 0 && getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED'))) {
4125  $opt .= ' selected';
4126  }
4127  $opt .= '>' . $objp->name . ' - ' . $objp->ref_fourn . ' - ';
4128 
4129  if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
4130  $prod_supplier = new ProductFournisseur($this->db);
4131  $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
4132  $prod_supplier->id = $productid;
4133  $prod_supplier->fourn_qty = $objp->quantity;
4134  $prod_supplier->fourn_tva_tx = $objp->tva_tx;
4135  $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
4136 
4137  require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4138  $priceparser = new PriceParser($this->db);
4139  $price_result = $priceparser->parseProductSupplier($prod_supplier);
4140  if ($price_result >= 0) {
4141  $objp->fprice = $price_result;
4142  if ($objp->quantity >= 1) {
4143  $objp->unitprice = $objp->fprice / $objp->quantity;
4144  }
4145  }
4146  }
4147  if ($objp->quantity == 1) {
4148  $opt .= price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
4149  }
4150 
4151  $opt .= $objp->quantity . ' ';
4152 
4153  if ($objp->quantity == 1) {
4154  $opt .= $langs->trans("Unit");
4155  } else {
4156  $opt .= $langs->trans("Units");
4157  }
4158  if ($objp->quantity > 1) {
4159  $opt .= " - ";
4160  $opt .= price($objp->unitprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit");
4161  }
4162  if ($objp->duration) {
4163  $opt .= " - " . $objp->duration;
4164  }
4165  $opt .= "</option>\n";
4166 
4167  $form .= $opt;
4168  $i++;
4169  }
4170  }
4171 
4172  $form .= '</select>';
4173  $this->db->free($result);
4174  return $form;
4175  } else {
4176  dol_print_error($this->db);
4177  return '';
4178  }
4179  }
4180 
4181 
4182  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4189  {
4190  // phpcs:enable
4191  global $langs;
4192 
4193  $num = count($this->cache_conditions_paiements);
4194  if ($num > 0) {
4195  return 0; // Cache already loaded
4196  }
4197 
4198  dol_syslog(__METHOD__, LOG_DEBUG);
4199 
4200  $sql = "SELECT rowid, code, libelle as label, deposit_percent";
4201  $sql .= " FROM " . $this->db->prefix() . 'c_payment_term';
4202  $sql .= " WHERE entity IN (" . getEntity('c_payment_term') . ")";
4203  $sql .= " AND active > 0";
4204  $sql .= " ORDER BY sortorder";
4205 
4206  $resql = $this->db->query($sql);
4207  if ($resql) {
4208  $num = $this->db->num_rows($resql);
4209  $i = 0;
4210  while ($i < $num) {
4211  $obj = $this->db->fetch_object($resql);
4212 
4213  // Si traduction existe, on l'utilise, sinon on prend le libelle par default
4214  $label = ($langs->trans("PaymentConditionShort" . $obj->code) != "PaymentConditionShort" . $obj->code ? $langs->trans("PaymentConditionShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
4215  $this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code;
4216  $this->cache_conditions_paiements[$obj->rowid]['label'] = $label;
4217  $this->cache_conditions_paiements[$obj->rowid]['deposit_percent'] = $obj->deposit_percent;
4218  $i++;
4219  }
4220 
4221  //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table
4222 
4223  return $num;
4224  } else {
4225  dol_print_error($this->db);
4226  return -1;
4227  }
4228  }
4229 
4230  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4231 
4237  public function load_cache_availability()
4238  {
4239  // phpcs:enable
4240  global $langs;
4241 
4242  $num = count($this->cache_availability); // TODO Use $conf->cache['availability'] instead of $this->cache_availability
4243  if ($num > 0) {
4244  return 0; // Cache already loaded
4245  }
4246 
4247  dol_syslog(__METHOD__, LOG_DEBUG);
4248 
4249  $langs->load('propal');
4250 
4251  $sql = "SELECT rowid, code, label, position";
4252  $sql .= " FROM " . $this->db->prefix() . 'c_availability';
4253  $sql .= " WHERE active > 0";
4254 
4255  $resql = $this->db->query($sql);
4256  if ($resql) {
4257  $num = $this->db->num_rows($resql);
4258  $i = 0;
4259  while ($i < $num) {
4260  $obj = $this->db->fetch_object($resql);
4261 
4262  // Si traduction existe, on l'utilise, sinon on prend le libelle par default
4263  $label = ($langs->trans("AvailabilityType" . $obj->code) != "AvailabilityType" . $obj->code ? $langs->trans("AvailabilityType" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
4264  $this->cache_availability[$obj->rowid]['code'] = $obj->code;
4265  $this->cache_availability[$obj->rowid]['label'] = $label;
4266  $this->cache_availability[$obj->rowid]['position'] = $obj->position;
4267  $i++;
4268  }
4269 
4270  $this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
4271 
4272  return $num;
4273  } else {
4274  dol_print_error($this->db);
4275  return -1;
4276  }
4277  }
4278 
4289  public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '')
4290  {
4291  global $langs, $user;
4292 
4293  $this->load_cache_availability();
4294 
4295  dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
4296 
4297  print '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
4298  if ($addempty) {
4299  print '<option value="0">&nbsp;</option>';
4300  }
4301  foreach ($this->cache_availability as $id => $arrayavailability) {
4302  if ($selected == $id) {
4303  print '<option value="' . $id . '" selected>';
4304  } else {
4305  print '<option value="' . $id . '">';
4306  }
4307  print dol_escape_htmltag($arrayavailability['label']);
4308  print '</option>';
4309  }
4310  print '</select>';
4311  if ($user->admin) {
4312  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4313  }
4314  print ajax_combobox($htmlname);
4315  }
4316 
4322  public function loadCacheInputReason()
4323  {
4324  global $langs;
4325 
4326  $num = count($this->cache_demand_reason); // TODO Use $conf->cache['input_reason'] instead of $this->cache_demand_reason
4327  if ($num > 0) {
4328  return 0; // Cache already loaded
4329  }
4330 
4331  $sql = "SELECT rowid, code, label";
4332  $sql .= " FROM " . $this->db->prefix() . 'c_input_reason';
4333  $sql .= " WHERE active > 0";
4334 
4335  $resql = $this->db->query($sql);
4336  if ($resql) {
4337  $num = $this->db->num_rows($resql);
4338  $i = 0;
4339  $tmparray = array();
4340  while ($i < $num) {
4341  $obj = $this->db->fetch_object($resql);
4342 
4343  // Si traduction existe, on l'utilise, sinon on prend le libelle par default
4344  $label = ($obj->label != '-' ? $obj->label : '');
4345  if ($langs->trans("DemandReasonType" . $obj->code) != "DemandReasonType" . $obj->code) {
4346  $label = $langs->trans("DemandReasonType" . $obj->code); // So translation key DemandReasonTypeSRC_XXX will work
4347  }
4348  if ($langs->trans($obj->code) != $obj->code) {
4349  $label = $langs->trans($obj->code); // So translation key SRC_XXX will work
4350  }
4351 
4352  $tmparray[$obj->rowid]['id'] = $obj->rowid;
4353  $tmparray[$obj->rowid]['code'] = $obj->code;
4354  $tmparray[$obj->rowid]['label'] = $label;
4355  $i++;
4356  }
4357 
4358  $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
4359 
4360  unset($tmparray);
4361  return $num;
4362  } else {
4363  dol_print_error($this->db);
4364  return -1;
4365  }
4366  }
4367 
4380  public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
4381  {
4382  global $langs, $user;
4383 
4384  $this->loadCacheInputReason();
4385 
4386  print '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4387  if ($addempty) {
4388  print '<option value="0"' . (empty($selected) ? ' selected' : '') . '>&nbsp;</option>';
4389  }
4390  foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
4391  if ($arraydemandreason['code'] == $exclude) {
4392  continue;
4393  }
4394 
4395  if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
4396  print '<option value="' . $arraydemandreason['id'] . '" selected>';
4397  } else {
4398  print '<option value="' . $arraydemandreason['id'] . '">';
4399  }
4400  $label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
4401  print $langs->trans($label);
4402  print '</option>';
4403  }
4404  print '</select>';
4405  if ($user->admin && empty($notooltip)) {
4406  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4407  }
4408  print ajax_combobox('select_' . $htmlname);
4409  }
4410 
4411  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4412 
4418  public function load_cache_types_paiements()
4419  {
4420  // phpcs:enable
4421  global $langs;
4422 
4423  $num = count($this->cache_types_paiements); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_types_paiements
4424  if ($num > 0) {
4425  return $num; // Cache already loaded
4426  }
4427 
4428  dol_syslog(__METHOD__, LOG_DEBUG);
4429 
4430  $this->cache_types_paiements = array();
4431 
4432  $sql = "SELECT id, code, libelle as label, type, active";
4433  $sql .= " FROM " . $this->db->prefix() . "c_paiement";
4434  $sql .= " WHERE entity IN (" . getEntity('c_paiement') . ")";
4435 
4436  $resql = $this->db->query($sql);
4437  if ($resql) {
4438  $num = $this->db->num_rows($resql);
4439  $i = 0;
4440  while ($i < $num) {
4441  $obj = $this->db->fetch_object($resql);
4442 
4443  // Si traduction existe, on l'utilise, sinon on prend le libelle par default
4444  $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
4445  $this->cache_types_paiements[$obj->id]['id'] = $obj->id;
4446  $this->cache_types_paiements[$obj->id]['code'] = $obj->code;
4447  $this->cache_types_paiements[$obj->id]['label'] = $label;
4448  $this->cache_types_paiements[$obj->id]['type'] = $obj->type;
4449  $this->cache_types_paiements[$obj->id]['active'] = $obj->active;
4450  $i++;
4451  }
4452 
4453  $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
4454 
4455  return $num;
4456  } else {
4457  dol_print_error($this->db);
4458  return -1;
4459  }
4460  }
4461 
4462 
4463  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4464 
4483  public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1, $noprint = 0)
4484  {
4485  // phpcs:enable
4486  $out = $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, $noinfoadmin, $morecss, $deposit_percent);
4487  if (empty($noprint)) {
4488  print $out;
4489  } else {
4490  return $out;
4491  }
4492  }
4493 
4494 
4511  public function getSelectConditionsPaiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
4512  {
4513  global $langs, $user, $conf;
4514 
4515  $out = '';
4516  dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
4517 
4519 
4520  // Set default value if not already set by caller
4521  if (empty($selected) && getDolGlobalString('MAIN_DEFAULT_PAYMENT_TERM_ID')) {
4522  dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TERM_ID", LOG_NOTICE);
4523  $selected = getDolGlobalString('MAIN_DEFAULT_PAYMENT_TERM_ID');
4524  }
4525 
4526  $out .= '<select id="' . $htmlname . '" class="flat selectpaymentterms' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
4527  if ($addempty) {
4528  $out .= '<option value="0">&nbsp;</option>';
4529  }
4530 
4531  $selectedDepositPercent = null;
4532 
4533  foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
4534  if ($filtertype <= 0 && !empty($arrayconditions['deposit_percent'])) {
4535  continue;
4536  }
4537 
4538  if ($selected == $id) {
4539  $selectedDepositPercent = $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'];
4540  $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '" selected>';
4541  } else {
4542  $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '">';
4543  }
4544  $label = $arrayconditions['label'];
4545 
4546  if (!empty($arrayconditions['deposit_percent'])) {
4547  $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'], $label);
4548  }
4549 
4550  $out .= $label;
4551  $out .= '</option>';
4552  }
4553  $out .= '</select>';
4554  if ($user->admin && empty($noinfoadmin)) {
4555  $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4556  }
4557  $out .= ajax_combobox($htmlname);
4558 
4559  if ($deposit_percent >= 0) {
4560  $out .= ' <span id="' . $htmlname . '_deposit_percent_container"' . (empty($selectedDepositPercent) ? ' style="display: none"' : '') . '>';
4561  $out .= $langs->trans('DepositPercent') . ' : ';
4562  $out .= '<input id="' . $htmlname . '_deposit_percent" name="' . $htmlname . '_deposit_percent" class="maxwidth50" value="' . $deposit_percent . '" />';
4563  $out .= '</span>';
4564  $out .= '
4565  <script nonce="' . getNonce() . '">
4566  $(document).ready(function () {
4567  $("#' . $htmlname . '").change(function () {
4568  let $selected = $(this).find("option:selected");
4569  let depositPercent = $selected.attr("data-deposit_percent");
4570 
4571  if (depositPercent.length > 0) {
4572  $("#' . $htmlname . '_deposit_percent_container").show().find("#' . $htmlname . '_deposit_percent").val(depositPercent);
4573  } else {
4574  $("#' . $htmlname . '_deposit_percent_container").hide();
4575  }
4576 
4577  return true;
4578  });
4579  });
4580  </script>';
4581  }
4582 
4583  return $out;
4584  }
4585 
4586 
4587  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4588 
4605  public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
4606  {
4607  // phpcs:enable
4608  global $langs, $user, $conf;
4609 
4610  $out = '';
4611 
4612  dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $filtertype . ", " . $format, LOG_DEBUG);
4613 
4614  $filterarray = array();
4615  if ($filtertype == 'CRDT') {
4616  $filterarray = array(0, 2, 3);
4617  } elseif ($filtertype == 'DBIT') {
4618  $filterarray = array(1, 2, 3);
4619  } elseif ($filtertype != '' && $filtertype != '-1') {
4620  $filterarray = explode(',', $filtertype);
4621  }
4622 
4623  $this->load_cache_types_paiements();
4624 
4625  // Set default value if not already set by caller
4626  if (empty($selected) && getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID')) {
4627  dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TYPE_ID", LOG_NOTICE);
4628  $selected = getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID');
4629  }
4630 
4631  $out .= '<select id="select' . $htmlname . '" class="flat selectpaymenttypes' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
4632  if ($empty) {
4633  $out .= '<option value="">&nbsp;</option>';
4634  }
4635  foreach ($this->cache_types_paiements as $id => $arraytypes) {
4636  // If not good status
4637  if ($active >= 0 && $arraytypes['active'] != $active) {
4638  continue;
4639  }
4640 
4641  // We skip of the user requested to filter on specific payment methods
4642  if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
4643  continue;
4644  }
4645 
4646  // We discard empty lines if showempty is on because an empty line has already been output.
4647  if ($empty && empty($arraytypes['code'])) {
4648  continue;
4649  }
4650 
4651  if ($format == 0) {
4652  $out .= '<option value="' . $id . '"';
4653  } elseif ($format == 1) {
4654  $out .= '<option value="' . $arraytypes['code'] . '"';
4655  } elseif ($format == 2) {
4656  $out .= '<option value="' . $arraytypes['code'] . '"';
4657  } elseif ($format == 3) {
4658  $out .= '<option value="' . $id . '"';
4659  }
4660  // Print attribute selected or not
4661  if ($format == 1 || $format == 2) {
4662  if ($selected == $arraytypes['code']) {
4663  $out .= ' selected';
4664  }
4665  } else {
4666  if ($selected == $id) {
4667  $out .= ' selected';
4668  }
4669  }
4670  $out .= '>';
4671  $value = '';
4672  if ($format == 0) {
4673  $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4674  } elseif ($format == 1) {
4675  $value = $arraytypes['code'];
4676  } elseif ($format == 2) {
4677  $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4678  } elseif ($format == 3) {
4679  $value = $arraytypes['code'];
4680  }
4681  $out .= $value ? $value : '&nbsp;';
4682  $out .= '</option>';
4683  }
4684  $out .= '</select>';
4685  if ($user->admin && !$noadmininfo) {
4686  $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4687  }
4688  $out .= ajax_combobox('select' . $htmlname);
4689 
4690  if (empty($nooutput)) {
4691  print $out;
4692  } else {
4693  return $out;
4694  }
4695  }
4696 
4697 
4706  public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
4707  {
4708  global $langs;
4709 
4710  $return = '<select class="flat maxwidth100" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4711  $options = array(
4712  'HT' => $langs->trans("HT"),
4713  'TTC' => $langs->trans("TTC")
4714  );
4715  foreach ($options as $id => $value) {
4716  if ($selected == $id) {
4717  $return .= '<option value="' . $id . '" selected>' . $value;
4718  } else {
4719  $return .= '<option value="' . $id . '">' . $value;
4720  }
4721  $return .= '</option>';
4722  }
4723  $return .= '</select>';
4724  if ($addjscombo) {
4725  $return .= ajax_combobox('select_' . $htmlname);
4726  }
4727 
4728  return $return;
4729  }
4730 
4731  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4732 
4738  public function load_cache_transport_mode()
4739  {
4740  // phpcs:enable
4741  global $langs;
4742 
4743  $num = count($this->cache_transport_mode); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_transport_mode
4744  if ($num > 0) {
4745  return $num; // Cache already loaded
4746  }
4747 
4748  dol_syslog(__METHOD__, LOG_DEBUG);
4749 
4750  $this->cache_transport_mode = array();
4751 
4752  $sql = "SELECT rowid, code, label, active";
4753  $sql .= " FROM " . $this->db->prefix() . "c_transport_mode";
4754  $sql .= " WHERE entity IN (" . getEntity('c_transport_mode') . ")";
4755 
4756  $resql = $this->db->query($sql);
4757  if ($resql) {
4758  $num = $this->db->num_rows($resql);
4759  $i = 0;
4760  while ($i < $num) {
4761  $obj = $this->db->fetch_object($resql);
4762 
4763  // If traduction exist, we use it else we take the default label
4764  $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
4765  $this->cache_transport_mode[$obj->rowid]['rowid'] = $obj->rowid;
4766  $this->cache_transport_mode[$obj->rowid]['code'] = $obj->code;
4767  $this->cache_transport_mode[$obj->rowid]['label'] = $label;
4768  $this->cache_transport_mode[$obj->rowid]['active'] = $obj->active;
4769  $i++;
4770  }
4771 
4772  $this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
4773 
4774  return $num;
4775  } else {
4776  dol_print_error($this->db);
4777  return -1;
4778  }
4779  }
4780 
4794  public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
4795  {
4796  global $langs, $user;
4797 
4798  dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $format, LOG_DEBUG);
4799 
4800  $this->load_cache_transport_mode();
4801 
4802  print '<select id="select' . $htmlname . '" class="flat selectmodetransport' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
4803  if ($empty) {
4804  print '<option value="">&nbsp;</option>';
4805  }
4806  foreach ($this->cache_transport_mode as $id => $arraytypes) {
4807  // If not good status
4808  if ($active >= 0 && $arraytypes['active'] != $active) {
4809  continue;
4810  }
4811 
4812  // We discard empty line if showempty is on because an empty line has already been output.
4813  if ($empty && empty($arraytypes['code'])) {
4814  continue;
4815  }
4816 
4817  if ($format == 0) {
4818  print '<option value="' . $id . '"';
4819  } elseif ($format == 1) {
4820  print '<option value="' . $arraytypes['code'] . '"';
4821  } elseif ($format == 2) {
4822  print '<option value="' . $arraytypes['code'] . '"';
4823  } elseif ($format == 3) {
4824  print '<option value="' . $id . '"';
4825  }
4826  // If text is selected, we compare with code, else with id
4827  if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
4828  print ' selected';
4829  } elseif ($selected == $id) {
4830  print ' selected';
4831  }
4832  print '>';
4833  $value = '';
4834  if ($format == 0) {
4835  $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4836  } elseif ($format == 1) {
4837  $value = $arraytypes['code'];
4838  } elseif ($format == 2) {
4839  $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4840  } elseif ($format == 3) {
4841  $value = $arraytypes['code'];
4842  }
4843  print $value ? $value : '&nbsp;';
4844  print '</option>';
4845  }
4846  print '</select>';
4847  if ($user->admin && !$noadmininfo) {
4848  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4849  }
4850  }
4851 
4864  public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
4865  {
4866  global $langs, $user;
4867 
4868  $langs->load("admin");
4869  $langs->load("deliveries");
4870 
4871  $sql = "SELECT rowid, code, libelle as label";
4872  $sql .= " FROM " . $this->db->prefix() . "c_shipment_mode";
4873  $sql .= " WHERE active > 0";
4874  if ($filtre) {
4875  $sql .= " AND " . $filtre;
4876  }
4877  $sql .= " ORDER BY libelle ASC";
4878 
4879  dol_syslog(get_class($this) . "::selectShippingMode", LOG_DEBUG);
4880  $result = $this->db->query($sql);
4881  if ($result) {
4882  $num = $this->db->num_rows($result);
4883  $i = 0;
4884  if ($num) {
4885  print '<select id="select' . $htmlname . '" class="flat selectshippingmethod' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
4886  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4887  print '<option value="-1">&nbsp;</option>';
4888  }
4889  while ($i < $num) {
4890  $obj = $this->db->fetch_object($result);
4891  if ($selected == $obj->rowid) {
4892  print '<option value="' . $obj->rowid . '" selected>';
4893  } else {
4894  print '<option value="' . $obj->rowid . '">';
4895  }
4896  print ($langs->trans("SendingMethod" . strtoupper($obj->code)) != "SendingMethod" . strtoupper($obj->code)) ? $langs->trans("SendingMethod" . strtoupper($obj->code)) : $obj->label;
4897  print '</option>';
4898  $i++;
4899  }
4900  print "</select>";
4901  if ($user->admin && empty($noinfoadmin)) {
4902  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4903  }
4904 
4905  print ajax_combobox('select' . $htmlname);
4906  } else {
4907  print $langs->trans("NoShippingMethodDefined");
4908  }
4909  } else {
4910  dol_print_error($this->db);
4911  }
4912  }
4913 
4923  public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
4924  {
4925  global $langs;
4926 
4927  $langs->load("deliveries");
4928 
4929  if ($htmlname != "none") {
4930  print '<form method="POST" action="' . $page . '">';
4931  print '<input type="hidden" name="action" value="setshippingmethod">';
4932  print '<input type="hidden" name="token" value="' . newToken() . '">';
4933  $this->selectShippingMethod($selected, $htmlname, '', $addempty);
4934  print '<input type="submit" class="button valignmiddle" value="' . $langs->trans("Modify") . '">';
4935  print '</form>';
4936  } else {
4937  if ($selected) {
4938  $code = $langs->getLabelFromKey($this->db, $selected, 'c_shipment_mode', 'rowid', 'code');
4939  print $langs->trans("SendingMethod" . strtoupper($code));
4940  } else {
4941  print "&nbsp;";
4942  }
4943  }
4944  }
4945 
4954  public function selectSituationInvoices($selected = '', $socid = 0)
4955  {
4956  global $langs;
4957 
4958  $langs->load('bills');
4959 
4960  $opt = '<option value="" selected></option>';
4961  $sql = "SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc";
4962  $sql .= ' FROM ' . $this->db->prefix() . 'facture';
4963  $sql .= ' WHERE entity IN (' . getEntity('invoice') . ')';
4964  $sql .= ' AND situation_counter >= 1';
4965  $sql .= ' AND fk_soc = ' . (int) $socid;
4966  $sql .= ' AND type <> 2';
4967  $sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
4968  $resql = $this->db->query($sql);
4969 
4970  if ($resql && $this->db->num_rows($resql) > 0) {
4971  // Last seen cycle
4972  $ref = 0;
4973  while ($obj = $this->db->fetch_object($resql)) {
4974  //Same cycle ?
4975  if ($obj->situation_cycle_ref != $ref) {
4976  // Just seen this cycle
4977  $ref = $obj->situation_cycle_ref;
4978  //not final ?
4979  if ($obj->situation_final != 1) {
4980  //Not prov?
4981  if (substr($obj->ref, 1, 4) != 'PROV') {
4982  if ($selected == $obj->rowid) {
4983  $opt .= '<option value="' . $obj->rowid . '" selected>' . $obj->ref . '</option>';
4984  } else {
4985  $opt .= '<option value="' . $obj->rowid . '">' . $obj->ref . '</option>';
4986  }
4987  }
4988  }
4989  }
4990  }
4991  } else {
4992  dol_syslog("Error sql=" . $sql . ", error=" . $this->error, LOG_ERR);
4993  }
4994  if ($opt == '<option value ="" selected></option>') {
4995  $opt = '<option value ="0" selected>' . $langs->trans('NoSituations') . '</option>';
4996  }
4997  return $opt;
4998  }
4999 
5009  public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
5010  {
5011  global $langs;
5012 
5013  $langs->load('products');
5014 
5015  $return = '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '">';
5016 
5017  $sql = "SELECT rowid, label, code FROM " . $this->db->prefix() . "c_units";
5018  $sql .= ' WHERE active > 0';
5019  if (!empty($unit_type)) {
5020  $sql .= " AND unit_type = '" . $this->db->escape($unit_type) . "'";
5021  }
5022  $sql .= " ORDER BY sortorder";
5023 
5024  $resql = $this->db->query($sql);
5025  if ($resql && $this->db->num_rows($resql) > 0) {
5026  if ($showempty) {
5027  $return .= '<option value="none"></option>';
5028  }
5029 
5030  while ($res = $this->db->fetch_object($resql)) {
5031  $unitLabel = $res->label;
5032  if (!empty($langs->tab_translate['unit' . $res->code])) { // check if Translation is available before
5033  $unitLabel = $langs->trans('unit' . $res->code) != $res->label ? $langs->trans('unit' . $res->code) : $res->label;
5034  }
5035 
5036  if ($selected == $res->rowid) {
5037  $return .= '<option value="' . $res->rowid . '" selected>' . $unitLabel . '</option>';
5038  } else {
5039  $return .= '<option value="' . $res->rowid . '">' . $unitLabel . '</option>';
5040  }
5041  }
5042  $return .= '</select>';
5043  }
5044  return $return;
5045  }
5046 
5047  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5048 
5063  public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0)
5064  {
5065  // phpcs:enable
5066  global $langs;
5067 
5068  $out = '';
5069 
5070  $langs->load("admin");
5071  $num = 0;
5072 
5073  $sql = "SELECT rowid, label, bank, clos as status, currency_code";
5074  $sql .= " FROM " . $this->db->prefix() . "bank_account";
5075  $sql .= " WHERE entity IN (" . getEntity('bank_account') . ")";
5076  if ($status != 2) {
5077  $sql .= " AND clos = " . (int) $status;
5078  }
5079  if ($filtre) { // TODO Support USF
5080  $sql .= " AND " . $filtre;
5081  }
5082  $sql .= " ORDER BY label";
5083 
5084  dol_syslog(get_class($this) . "::select_comptes", LOG_DEBUG);
5085  $result = $this->db->query($sql);
5086  if ($result) {
5087  $num = $this->db->num_rows($result);
5088  $i = 0;
5089  if ($num) {
5090  $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
5091 
5092  if (!empty($useempty) && !is_numeric($useempty)) {
5093  $out .= '<option value="-1">'.$langs->trans($useempty).'</option>';
5094  } elseif ($useempty == 1 || ($useempty == 2 && $num > 1)) {
5095  $out .= '<option value="-1">&nbsp;</option>';
5096  }
5097 
5098  while ($i < $num) {
5099  $obj = $this->db->fetch_object($result);
5100  if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
5101  $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" selected>';
5102  } else {
5103  $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '">';
5104  }
5105  $out .= trim($obj->label);
5106  if ($showcurrency) {
5107  $out .= ' (' . $obj->currency_code . ')';
5108  }
5109  if ($status == 2 && $obj->status == 1) {
5110  $out .= ' (' . $langs->trans("Closed") . ')';
5111  }
5112  $out .= '</option>';
5113  $i++;
5114  }
5115  $out .= "</select>";
5116  $out .= ajax_combobox('select' . $htmlname);
5117  } else {
5118  if ($status == 0) {
5119  $out .= '<span class="opacitymedium">' . $langs->trans("NoActiveBankAccountDefined") . '</span>';
5120  } else {
5121  $out .= '<span class="opacitymedium">' . $langs->trans("NoBankAccountFound") . '</span>';
5122  }
5123  }
5124  } else {
5125  dol_print_error($this->db);
5126  }
5127 
5128  // Output or return
5129  if (empty($nooutput)) {
5130  print $out;
5131  } else {
5132  return $out;
5133  }
5134 
5135  return $num;
5136  }
5137 
5149  public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
5150  {
5151  global $langs;
5152 
5153  $langs->load("admin");
5154  $num = 0;
5155 
5156  $sql = "SELECT rowid, name, fk_country, status, entity";
5157  $sql .= " FROM " . $this->db->prefix() . "establishment";
5158  $sql .= " WHERE 1=1";
5159  if ($status != 2) {
5160  $sql .= " AND status = " . (int) $status;
5161  }
5162  if ($filtre) { // TODO Support USF
5163  $sql .= " AND " . $filtre;
5164  }
5165  $sql .= " ORDER BY name";
5166 
5167  dol_syslog(get_class($this) . "::select_establishment", LOG_DEBUG);
5168  $result = $this->db->query($sql);
5169  if ($result) {
5170  $num = $this->db->num_rows($result);
5171  $i = 0;
5172  if ($num) {
5173  print '<select id="select' . $htmlname . '" class="flat selectestablishment" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
5174  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
5175  print '<option value="-1">&nbsp;</option>';
5176  }
5177 
5178  while ($i < $num) {
5179  $obj = $this->db->fetch_object($result);
5180  if ($selected == $obj->rowid) {
5181  print '<option value="' . $obj->rowid . '" selected>';
5182  } else {
5183  print '<option value="' . $obj->rowid . '">';
5184  }
5185  print trim($obj->name);
5186  if ($status == 2 && $obj->status == 1) {
5187  print ' (' . $langs->trans("Closed") . ')';
5188  }
5189  print '</option>';
5190  $i++;
5191  }
5192  print "</select>";
5193  } else {
5194  if ($status == 0) {
5195  print '<span class="opacitymedium">' . $langs->trans("NoActiveEstablishmentDefined") . '</span>';
5196  } else {
5197  print '<span class="opacitymedium">' . $langs->trans("NoEstablishmentFound") . '</span>';
5198  }
5199  }
5200 
5201  return $num;
5202  } else {
5203  dol_print_error($this->db);
5204  return -1;
5205  }
5206  }
5207 
5217  public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
5218  {
5219  global $langs;
5220  if ($htmlname != "none") {
5221  print '<form method="POST" action="' . $page . '">';
5222  print '<input type="hidden" name="action" value="setbankaccount">';
5223  print '<input type="hidden" name="token" value="' . newToken() . '">';
5224  print img_picto('', 'bank_account', 'class="pictofixedwidth"');
5225  $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
5226  if ($nbaccountfound > 0) {
5227  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
5228  }
5229  print '</form>';
5230  } else {
5231  $langs->load('banks');
5232 
5233  if ($selected) {
5234  require_once DOL_DOCUMENT_ROOT . '/compta/bank/class/account.class.php';
5235  $bankstatic = new Account($this->db);
5236  $result = $bankstatic->fetch($selected);
5237  if ($result) {
5238  print $bankstatic->getNomUrl(1);
5239  }
5240  } else {
5241  print "&nbsp;";
5242  }
5243  }
5244  }
5245 
5246  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5247 
5267  public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $fromid = 0, $outputmode = 0, $include = 0, $morecss = '', $useempty = 1)
5268  {
5269  // phpcs:enable
5270  global $conf, $langs;
5271  $langs->load("categories");
5272 
5273  include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
5274 
5275  // For backward compatibility
5276  if (is_numeric($type)) {
5277  dol_syslog(__METHOD__ . ': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING);
5278  }
5279 
5280  if ($type === Categorie::TYPE_BANK_LINE) {
5281  // TODO Move this into common category feature
5282  $cate_arbo = array();
5283  $sql = "SELECT c.label, c.rowid";
5284  $sql .= " FROM " . $this->db->prefix() . "bank_categ as c";
5285  $sql .= " WHERE entity = " . $conf->entity;
5286  $sql .= " ORDER BY c.label";
5287  $result = $this->db->query($sql);
5288  if ($result) {
5289  $num = $this->db->num_rows($result);
5290  $i = 0;
5291  while ($i < $num) {
5292  $objp = $this->db->fetch_object($result);
5293  if ($objp) {
5294  $cate_arbo[$objp->rowid] = array('id' => $objp->rowid, 'fulllabel' => $objp->label, 'color' => '', 'picto' => 'category');
5295  }
5296  $i++;
5297  }
5298  $this->db->free($result);
5299  } else {
5300  dol_print_error($this->db);
5301  }
5302  } else {
5303  $cat = new Categorie($this->db);
5304  $cate_arbo = $cat->get_full_arbo($type, $fromid, $include);
5305  }
5306 
5307  $outarray = array();
5308  $outarrayrichhtml = array();
5309 
5310 
5311  $output = '<select class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
5312  if (is_array($cate_arbo)) {
5313  $num = count($cate_arbo);
5314 
5315  if (!$num) {
5316  $output .= '<option value="-1" disabled>' . $langs->trans("NoCategoriesDefined") . '</option>';
5317  } else {
5318  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
5319  $output .= '<option value="-1">&nbsp;</option>';
5320  }
5321  foreach ($cate_arbo as $key => $value) {
5322  if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
5323  $add = 'selected ';
5324  } else {
5325  $add = '';
5326  }
5327 
5328  $labeltoshow = img_picto('', 'category', 'class="pictofixedwidth" style="color: #' . $cate_arbo[$key]['color'] . '"');
5329  $labeltoshow .= dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle');
5330 
5331  $outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
5332 
5333  $outarrayrichhtml[$cate_arbo[$key]['id']] = $labeltoshow;
5334 
5335  $output .= '<option ' . $add . 'value="' . $cate_arbo[$key]['id'] . '"';
5336  $output .= ' data-html="' . dol_escape_htmltag($labeltoshow) . '"';
5337  $output .= '>';
5338  $output .= dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle');
5339  $output .= '</option>';
5340 
5341  $cate_arbo[$key]['data-html'] = $labeltoshow;
5342  }
5343  }
5344  }
5345  $output .= '</select>';
5346  $output .= "\n";
5347 
5348  if ($outputmode == 2) {
5349  // TODO: handle error when $cate_arbo is not an array
5350  return $cate_arbo;
5351  } elseif ($outputmode == 1) {
5352  return $outarray;
5353  } elseif ($outputmode == 3) {
5354  return $outarrayrichhtml;
5355  }
5356  return $output;
5357  }
5358 
5359  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5360 
5377  public function form_confirm($page, $title, $question, $action, $formquestion = array(), $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
5378  {
5379  // phpcs:enable
5380  dol_syslog(__METHOD__ . ': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
5381  print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
5382  }
5383 
5411  public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0, $labelbuttonyes = 'Yes', $labelbuttonno = 'No')
5412  {
5413  global $langs, $conf;
5414 
5415  $more = '<!-- formconfirm - before call, page=' . dol_escape_htmltag($page) . ' -->';
5416  $formconfirm = '';
5417  $inputok = array();
5418  $inputko = array();
5419 
5420  // Clean parameters
5421  $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
5422  if ($conf->browser->layout == 'phone') {
5423  $width = '95%';
5424  }
5425 
5426  // Set height automatically if not defined
5427  if (empty($height)) {
5428  $height = 220;
5429  if (is_array($formquestion) && count($formquestion) > 2) {
5430  $height += ((count($formquestion) - 2) * 24);
5431  }
5432  }
5433 
5434  if (is_array($formquestion) && !empty($formquestion)) {
5435  // First add hidden fields and value
5436  foreach ($formquestion as $key => $input) {
5437  if (is_array($input) && !empty($input)) {
5438  if ($input['type'] == 'hidden') {
5439  $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
5440  $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
5441 
5442  $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";
5443  }
5444  }
5445  }
5446 
5447  // Now add questions
5448  $moreonecolumn = '';
5449  $more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">' . "\n";
5450  foreach ($formquestion as $key => $input) {
5451  if (is_array($input) && !empty($input)) {
5452  $size = (!empty($input['size']) ? ' size="' . $input['size'] . '"' : ''); // deprecated. Use morecss instead.
5453  $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
5454  $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
5455 
5456  if ($input['type'] == 'text') {
5457  $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";
5458  } elseif ($input['type'] == 'password') {
5459  $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";
5460  } elseif ($input['type'] == 'textarea') {
5461  /*$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd">';
5462  $more .= '<textarea name="'.$input['name'].'" class="'.$morecss.'"'.$moreattr.'>';
5463  $more .= $input['value'];
5464  $more .= '</textarea>';
5465  $more .= '</div></div>'."\n";*/
5466  $moreonecolumn .= '<div class="margintoponly">';
5467  $moreonecolumn .= $input['label'] . '<br>';
5468  $moreonecolumn .= '<textarea name="' . dol_escape_htmltag($input['name']) . '" id="' . dol_escape_htmltag($input['name']) . '" class="' . $morecss . '"' . $moreattr . '>';
5469  $moreonecolumn .= $input['value'];
5470  $moreonecolumn .= '</textarea>';
5471  $moreonecolumn .= '</div>';
5472  } elseif (in_array($input['type'], ['select', 'multiselect'])) {
5473  if (empty($morecss)) {
5474  $morecss = 'minwidth100';
5475  }
5476 
5477  $show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
5478  $key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
5479  $value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
5480  $translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
5481  $maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
5482  $disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
5483  $sort = isset($input['select_sort']) ? $input['select_sort'] : '';
5484 
5485  $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">';
5486  if (!empty($input['label'])) {
5487  $more .= $input['label'] . '</div><div class="tagtd left">';
5488  }
5489  if ($input['type'] == 'select') {
5490  $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);
5491  } else {
5492  $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);
5493  }
5494  $more .= '</div></div>' . "\n";
5495  } elseif ($input['type'] == 'checkbox') {
5496  $more .= '<div class="tagtr">';
5497  $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '"><label for="' . dol_escape_htmltag($input['name']) . '">' . $input['label'] . '</label></div><div class="tagtd">';
5498  $more .= '<input type="checkbox" class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '"' . $moreattr;
5499  if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0' && $input['value'] != '') {
5500  $more .= ' checked';
5501  }
5502  if (is_bool($input['value']) && $input['value']) {
5503  $more .= ' checked';
5504  }
5505  if (isset($input['disabled'])) {
5506  $more .= ' disabled';
5507  }
5508  $more .= ' /></div>';
5509  $more .= '</div>' . "\n";
5510  } elseif ($input['type'] == 'radio') {
5511  $i = 0;
5512  foreach ($input['values'] as $selkey => $selval) {
5513  $more .= '<div class="tagtr">';
5514  if (isset($input['label'])) {
5515  if ($i == 0) {
5516  $more .= '<div class="tagtd' . (empty($input['tdclass']) ? ' tdtop' : (' tdtop ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
5517  } else {
5518  $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' "' . $input['tdclass'])) . '">&nbsp;</div>';
5519  }
5520  }
5521  $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;
5522  if (!empty($input['disabled'])) {
5523  $more .= ' disabled';
5524  }
5525  if (isset($input['default']) && $input['default'] === $selkey) {
5526  $more .= ' checked="checked"';
5527  }
5528  $more .= ' /> ';
5529  $more .= '<label for="' . dol_escape_htmltag($input['name'] . $selkey) . '" class="valignmiddle">' . $selval . '</label>';
5530  $more .= '</div></div>' . "\n";
5531  $i++;
5532  }
5533  } elseif ($input['type'] == 'date' || $input['type'] == 'datetime') {
5534  $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
5535  $more .= '<div class="tagtd">';
5536  $addnowlink = (empty($input['datenow']) ? 0 : 1);
5537  $h = $m = 0;
5538  if ($input['type'] == 'datetime') {
5539  $h = isset($input['hours']) ? $input['hours'] : 1;
5540  $m = isset($input['minutes']) ? $input['minutes'] : 1;
5541  }
5542  $more .= $this->selectDate(isset($input['value']) ? $input['value'] : -1, $input['name'], $h, $m, 0, '', 1, $addnowlink);
5543  $more .= '</div></div>'."\n";
5544  $formquestion[] = array('name' => $input['name'].'day');
5545  $formquestion[] = array('name' => $input['name'].'month');
5546  $formquestion[] = array('name' => $input['name'].'year');
5547  $formquestion[] = array('name' => $input['name'].'hour');
5548  $formquestion[] = array('name' => $input['name'].'min');
5549  } elseif ($input['type'] == 'other') { // can be 1 column or 2 depending if label is set or not
5550  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
5551  if (!empty($input['label'])) {
5552  $more .= $input['label'] . '</div><div class="tagtd">';
5553  }
5554  $more .= $input['value'];
5555  $more .= '</div></div>' . "\n";
5556  } elseif ($input['type'] == 'onecolumn') {
5557  $moreonecolumn .= '<div class="margintoponly">';
5558  $moreonecolumn .= $input['value'];
5559  $moreonecolumn .= '</div>' . "\n";
5560  } elseif ($input['type'] == 'hidden') {
5561  // Do nothing more, already added by a previous loop
5562  } elseif ($input['type'] == 'separator') {
5563  $more .= '<br>';
5564  } else {
5565  $more .= 'Error type ' . $input['type'] . ' for the confirm box is not a supported type';
5566  }
5567  }
5568  }
5569  $more .= '</div>' . "\n";
5570  $more .= $moreonecolumn;
5571  }
5572 
5573  // JQUERY method dialog is broken with smartphone, we use standard HTML.
5574  // 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
5575  // See page product/card.php for example
5576  if (!empty($conf->dol_use_jmobile)) {
5577  $useajax = 0;
5578  }
5579  if (empty($conf->use_javascript_ajax)) {
5580  $useajax = 0;
5581  }
5582 
5583  if ($useajax) {
5584  $autoOpen = true;
5585  $dialogconfirm = 'dialog-confirm';
5586  $button = '';
5587  if (!is_numeric($useajax)) {
5588  $button = $useajax;
5589  $useajax = 1;
5590  $autoOpen = false;
5591  $dialogconfirm .= '-' . $button;
5592  }
5593  $pageyes = $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=yes';
5594  $pageno = ($useajax == 2 ? $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=no' : '');
5595 
5596  // Add input fields into list of fields to read during submit (inputok and inputko)
5597  if (is_array($formquestion)) {
5598  foreach ($formquestion as $key => $input) {
5599  //print "xx ".$key." rr ".is_array($input)."<br>\n";
5600  // Add name of fields to propagate with the GET when submitting the form with button OK.
5601  if (is_array($input) && isset($input['name'])) {
5602  if (strpos($input['name'], ',') > 0) {
5603  $inputok = array_merge($inputok, explode(',', $input['name']));
5604  } else {
5605  array_push($inputok, $input['name']);
5606  }
5607  }
5608  // Add name of fields to propagate with the GET when submitting the form with button KO.
5609  // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
5610  if (is_array($input) && isset($input['inputko']) && $input['inputko'] == 1 && isset($input['name'])) {
5611  array_push($inputko, $input['name']);
5612  }
5613  }
5614  }
5615 
5616  // Show JQuery confirm box.
5617  $formconfirm .= '<div id="' . $dialogconfirm . '" title="' . dol_escape_htmltag($title) . '" style="display: none;">';
5618  if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
5619  $formconfirm .= '<div class="confirmtext">' . $formquestion['text'] . '</div>' . "\n";
5620  }
5621  if (!empty($more)) {
5622  $formconfirm .= '<div class="confirmquestions">' . $more . '</div>' . "\n";
5623  }
5624  $formconfirm .= ($question ? '<div class="confirmmessage">' . img_help(0, '') . ' ' . $question . '</div>' : '');
5625  $formconfirm .= '</div>' . "\n";
5626 
5627  $formconfirm .= "\n<!-- begin code of popup for formconfirm page=" . $page . " -->\n";
5628  $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
5629  $formconfirm .= "/* Code for the jQuery('#dialogforpopup').dialog() */\n";
5630  $formconfirm .= 'jQuery(document).ready(function() {
5631  $(function() {
5632  $( "#' . $dialogconfirm . '" ).dialog(
5633  {
5634  autoOpen: ' . ($autoOpen ? "true" : "false") . ',';
5635  if ($newselectedchoice == 'no') {
5636  $formconfirm .= '
5637  open: function() {
5638  $(this).parent().find("button.ui-button:eq(2)").focus();
5639  },';
5640  }
5641 
5642  $jsforcursor = '';
5643  if ($useajax == 1) {
5644  $jsforcursor = '// The call to urljump can be slow, so we set the wait cursor' . "\n";
5645  $jsforcursor .= 'jQuery("html,body,#id-container").addClass("cursorwait");' . "\n";
5646  }
5647 
5648  $postconfirmas = 'GET';
5649 
5650  $formconfirm .= '
5651  resizable: false,
5652  height: "' . $height . '",
5653  width: "' . $width . '",
5654  modal: true,
5655  closeOnEscape: false,
5656  buttons: {
5657  "' . dol_escape_js($langs->transnoentities($labelbuttonyes)) . '": function() {
5658  var options = "token=' . urlencode(newToken()) . '";
5659  var inputok = ' . json_encode($inputok) . '; /* List of fields into form */
5660  var page = "' . dol_escape_js(!empty($page) ? $page : '') . '";
5661  var pageyes = "' . dol_escape_js(!empty($pageyes) ? $pageyes : '') . '";
5662 
5663  if (inputok.length > 0) {
5664  $.each(inputok, function(i, inputname) {
5665  var more = "";
5666  var inputvalue;
5667  if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
5668  inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
5669  } else {
5670  if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5671  inputvalue = $("#" + inputname + more).val();
5672  }
5673  if (typeof inputvalue == "undefined") { inputvalue=""; }
5674  console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
5675  options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5676  });
5677  }
5678  var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "&") + options;
5679  if (pageyes.length > 0) {';
5680  if ($postconfirmas == 'GET') {
5681  $formconfirm .= 'location.href = urljump;';
5682  } else {
5683  $formconfirm .= $jsforcursor;
5684  $formconfirm .= 'var post = $.post(
5685  pageyes,
5686  options,
5687  function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
5688  );';
5689  }
5690  $formconfirm .= '
5691  console.log("after post ok");
5692  }
5693  $(this).dialog("close");
5694  },
5695  "' . dol_escape_js($langs->transnoentities($labelbuttonno)) . '": function() {
5696  var options = "token=' . urlencode(newToken()) . '";
5697  var inputko = ' . json_encode($inputko) . '; /* List of fields into form */
5698  var page = "' . dol_escape_js(!empty($page) ? $page : '') . '";
5699  var pageno="' . dol_escape_js(!empty($pageno) ? $pageno : '') . '";
5700  if (inputko.length > 0) {
5701  $.each(inputko, function(i, inputname) {
5702  var more = "";
5703  if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5704  var inputvalue = $("#" + inputname + more).val();
5705  if (typeof inputvalue == "undefined") { inputvalue=""; }
5706  options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5707  });
5708  }
5709  var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "&") + options;
5710  //alert(urljump);
5711  if (pageno.length > 0) {';
5712  if ($postconfirmas == 'GET') {
5713  $formconfirm .= 'location.href = urljump;';
5714  } else {
5715  $formconfirm .= $jsforcursor;
5716  $formconfirm .= 'var post = $.post(
5717  pageno,
5718  options,
5719  function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
5720  );';
5721  }
5722  $formconfirm .= '
5723  console.log("after post ko");
5724  }
5725  $(this).dialog("close");
5726  }
5727  }
5728  }
5729  );
5730 
5731  var button = "' . $button . '";
5732  if (button.length > 0) {
5733  $( "#" + button ).click(function() {
5734  $("#' . $dialogconfirm . '").dialog("open");
5735  });
5736  }
5737  });
5738  });
5739  </script>';
5740  $formconfirm .= "<!-- end ajax formconfirm -->\n";
5741  } else {
5742  $formconfirm .= "\n<!-- begin formconfirm page=" . dol_escape_htmltag($page) . " -->\n";
5743 
5744  if (empty($disableformtag)) {
5745  $formconfirm .= '<form method="POST" action="' . $page . '" class="notoptoleftroright">' . "\n";
5746  }
5747 
5748  $formconfirm .= '<input type="hidden" name="action" value="' . $action . '">' . "\n";
5749  $formconfirm .= '<input type="hidden" name="token" value="' . newToken() . '">' . "\n";
5750 
5751  $formconfirm .= '<table class="valid centpercent">' . "\n";
5752 
5753  // Line title
5754  $formconfirm .= '<tr class="validtitre"><td class="validtitre" colspan="2">';
5755  $formconfirm .= img_picto('', 'pictoconfirm') . ' ' . $title;
5756  $formconfirm .= '</td></tr>' . "\n";
5757 
5758  // Line text
5759  if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
5760  $formconfirm .= '<tr class="valid"><td class="valid" colspan="2">' . $formquestion['text'] . '</td></tr>' . "\n";
5761  }
5762 
5763  // Line form fields
5764  if ($more) {
5765  $formconfirm .= '<tr class="valid"><td class="valid" colspan="2">' . "\n";
5766  $formconfirm .= $more;
5767  $formconfirm .= '</td></tr>' . "\n";
5768  }
5769 
5770  // Line with question
5771  $formconfirm .= '<tr class="valid">';
5772  $formconfirm .= '<td class="valid">' . $question . '</td>';
5773  $formconfirm .= '<td class="valid center">';
5774  $formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly', $labelbuttonyes, $labelbuttonno);
5775  $formconfirm .= '<input class="button valignmiddle confirmvalidatebutton small" type="submit" value="' . $langs->trans("Validate") . '">';
5776  $formconfirm .= '</td>';
5777  $formconfirm .= '</tr>' . "\n";
5778 
5779  $formconfirm .= '</table>' . "\n";
5780 
5781  if (empty($disableformtag)) {
5782  $formconfirm .= "</form>\n";
5783  }
5784  $formconfirm .= '<br>';
5785 
5786  if (!empty($conf->use_javascript_ajax)) {
5787  $formconfirm .= '<!-- code to disable button to avoid double clic -->';
5788  $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
5789  $formconfirm .= '
5790  $(document).ready(function () {
5791  $(".confirmvalidatebutton").on("click", function() {
5792  console.log("We click on button confirmvalidatebutton");
5793  $(this).attr("disabled", "disabled");
5794  setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
5795  //console.log($(this).closest("form"));
5796  $(this).closest("form").submit();
5797  });
5798  });
5799  ';
5800  $formconfirm .= '</script>' . "\n";
5801  }
5802 
5803  $formconfirm .= "<!-- end formconfirm -->\n";
5804  }
5805 
5806  return $formconfirm;
5807  }
5808 
5809 
5810  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5811 
5827  public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0, $textifnoproject = '', $morecss = '')
5828  {
5829  // phpcs:enable
5830  global $langs;
5831 
5832  require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
5833  require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
5834 
5835  $out = '';
5836 
5837  $formproject = new FormProjets($this->db);
5838 
5839  $langs->load("project");
5840  if ($htmlname != "none") {
5841  $out .= '<form method="post" action="' . $page . '">';
5842  $out .= '<input type="hidden" name="action" value="classin">';
5843  $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
5844  $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1, 0, $morecss);
5845  $out .= '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
5846  $out .= '</form>';
5847  } else {
5848  $out .= '<span class="project_head_block">';
5849  if ($selected) {
5850  $projet = new Project($this->db);
5851  $projet->fetch($selected);
5852  $out .= $projet->getNomUrl(0, '', 1);
5853  } else {
5854  $out .= '<span class="opacitymedium">' . $textifnoproject . '</span>';
5855  }
5856  $out .= '</span>';
5857  }
5858 
5859  if (empty($nooutput)) {
5860  print $out;
5861  return '';
5862  }
5863  return $out;
5864  }
5865 
5866  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5867 
5883  public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0, $type = '', $filtertype = -1, $deposit_percent = -1, $nooutput = 0)
5884  {
5885  // phpcs:enable
5886  global $langs;
5887 
5888  $out = '';
5889 
5890  if ($htmlname != "none") {
5891  $out .= '<form method="POST" action="' . $page . '">';
5892  $out .= '<input type="hidden" name="action" value="setconditions">';
5893  $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
5894  if ($type) {
5895  $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
5896  }
5897  $out .= $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, 0, '', $deposit_percent);
5898  $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
5899  $out .= '</form>';
5900  } else {
5901  if ($selected) {
5903  if (isset($this->cache_conditions_paiements[$selected])) {
5904  $label = $this->cache_conditions_paiements[$selected]['label'];
5905 
5906  if (!empty($this->cache_conditions_paiements[$selected]['deposit_percent'])) {
5907  $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $this->cache_conditions_paiements[$selected]['deposit_percent'], $label);
5908  }
5909 
5910  $out .= $label;
5911  } else {
5912  $langs->load('errors');
5913  $out .= $langs->trans('ErrorNotInDictionaryPaymentConditions');
5914  }
5915  } else {
5916  $out .= '&nbsp;';
5917  }
5918  }
5919 
5920  if (empty($nooutput)) {
5921  print $out;
5922  return '';
5923  }
5924  return $out;
5925  }
5926 
5927  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5928 
5938  public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
5939  {
5940  // phpcs:enable
5941  global $langs;
5942  if ($htmlname != "none") {
5943  print '<form method="post" action="' . $page . '">';
5944  print '<input type="hidden" name="action" value="setavailability">';
5945  print '<input type="hidden" name="token" value="' . newToken() . '">';
5946  $this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty);
5947  print '<input type="submit" name="modify" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
5948  print '<input type="submit" name="cancel" class="button smallpaddingimp" value="' . $langs->trans("Cancel") . '">';
5949  print '</form>';
5950  } else {
5951  if ($selected) {
5952  $this->load_cache_availability();
5953  print $this->cache_availability[$selected]['label'];
5954  } else {
5955  print "&nbsp;";
5956  }
5957  }
5958  }
5959 
5970  public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0)
5971  {
5972  global $langs;
5973  if ($htmlname != "none") {
5974  print '<form method="post" action="' . $page . '">';
5975  print '<input type="hidden" name="action" value="setdemandreason">';
5976  print '<input type="hidden" name="token" value="' . newToken() . '">';
5977  $this->selectInputReason($selected, $htmlname, -1, $addempty);
5978  print '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
5979  print '</form>';
5980  } else {
5981  if ($selected) {
5982  $this->loadCacheInputReason();
5983  foreach ($this->cache_demand_reason as $key => $val) {
5984  if ($val['id'] == $selected) {
5985  print $val['label'];
5986  break;
5987  }
5988  }
5989  } else {
5990  print "&nbsp;";
5991  }
5992  }
5993  }
5994 
5995  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5996 
6010  public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0, $type = '')
6011  {
6012  // phpcs:enable
6013  global $langs;
6014 
6015  $ret = '';
6016 
6017  if ($htmlname != "none") {
6018  $ret .= '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
6019  $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
6020  $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
6021  if ($type) {
6022  $ret .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
6023  }
6024  $ret .= '<table class="nobordernopadding">';
6025  $ret .= '<tr><td>';
6026  $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form' . $htmlname, 1, 0);
6027  $ret .= '</td>';
6028  $ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
6029  $ret .= '</tr></table></form>';
6030  } else {
6031  if ($displayhour) {
6032  $ret .= dol_print_date($selected, 'dayhour');
6033  } else {
6034  $ret .= dol_print_date($selected, 'day');
6035  }
6036  }
6037 
6038  if (empty($nooutput)) {
6039  print $ret;
6040  }
6041  return $ret;
6042  }
6043 
6044 
6045  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6046 
6057  public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = array(), $include = array())
6058  {
6059  // phpcs:enable
6060  global $langs;
6061 
6062  if ($htmlname != "none") {
6063  print '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
6064  print '<input type="hidden" name="action" value="set' . $htmlname . '">';
6065  print '<input type="hidden" name="token" value="' . newToken() . '">';
6066  print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
6067  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6068  print '</form>';
6069  } else {
6070  if ($selected) {
6071  require_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
6072  $theuser = new User($this->db);
6073  $theuser->fetch($selected);
6074  print $theuser->getNomUrl(1);
6075  } else {
6076  print "&nbsp;";
6077  }
6078  }
6079  }
6080 
6081 
6082  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6083 
6097  public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0, $type = '', $nooutput = 0)
6098  {
6099  // phpcs:enable
6100  global $langs;
6101 
6102  $out = '';
6103  if ($htmlname != "none") {
6104  $out .= '<form method="POST" action="' . $page . '">';
6105  $out .= '<input type="hidden" name="action" value="setmode">';
6106  $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
6107  if ($type) {
6108  $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
6109  }
6110  $out .= $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active, '', 1);
6111  $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6112  $out .= '</form>';
6113  } else {
6114  if ($selected) {
6115  $this->load_cache_types_paiements();
6116  $out .= $this->cache_types_paiements[$selected]['label'];
6117  } else {
6118  $out .= "&nbsp;";
6119  }
6120  }
6121 
6122  if ($nooutput) {
6123  return $out;
6124  } else {
6125  print $out;
6126  }
6127  return '';
6128  }
6129 
6140  public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
6141  {
6142  global $langs;
6143  if ($htmlname != "none") {
6144  print '<form method="POST" action="' . $page . '">';
6145  print '<input type="hidden" name="action" value="settransportmode">';
6146  print '<input type="hidden" name="token" value="' . newToken() . '">';
6147  $this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
6148  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6149  print '</form>';
6150  } else {
6151  if ($selected) {
6152  $this->load_cache_transport_mode();
6153  print $this->cache_transport_mode[$selected]['label'];
6154  } else {
6155  print "&nbsp;";
6156  }
6157  }
6158  }
6159 
6160  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6161 
6170  public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
6171  {
6172  // phpcs:enable
6173  global $langs;
6174  if ($htmlname != "none") {
6175  print '<form method="POST" action="' . $page . '">';
6176  print '<input type="hidden" name="action" value="setmulticurrencycode">';
6177  print '<input type="hidden" name="token" value="' . newToken() . '">';
6178  print $this->selectMultiCurrency($selected, $htmlname, 0);
6179  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6180  print '</form>';
6181  } else {
6182  require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
6183  print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
6184  }
6185  }
6186 
6187  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6188 
6198  public function form_multicurrency_rate($page, $rate = 0.0, $htmlname = 'multicurrency_tx', $currency = '')
6199  {
6200  // phpcs:enable
6201  global $langs, $mysoc, $conf;
6202 
6203  if ($htmlname != "none") {
6204  print '<form method="POST" action="' . $page . '">';
6205  print '<input type="hidden" name="action" value="setmulticurrencyrate">';
6206  print '<input type="hidden" name="token" value="' . newToken() . '">';
6207  print '<input type="text" class="maxwidth100" name="' . $htmlname . '" value="' . (!empty($rate) ? price(price2num($rate, 'CU')) : 1) . '" /> ';
6208  print '<select name="calculation_mode">';
6209  print '<option value="1">Change ' . $langs->trans("PriceUHT") . ' of lines</option>';
6210  print '<option value="2">Change ' . $langs->trans("PriceUHTCurrency") . ' of lines</option>';
6211  print '</select> ';
6212  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6213  print '</form>';
6214  } else {
6215  if (!empty($rate)) {
6216  print price($rate, 1, $langs, 0, 0);
6217  if ($currency && $rate != 1) {
6218  print ' &nbsp; (' . price($rate, 1, $langs, 0, 0) . ' ' . $currency . ' = 1 ' . $conf->currency . ')';
6219  }
6220  } else {
6221  print 1;
6222  }
6223  }
6224  }
6225 
6226 
6227  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6228 
6244  public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0)
6245  {
6246  // phpcs:enable
6247  global $conf, $langs;
6248  if ($htmlname != "none") {
6249  print '<form method="post" action="' . $page . '">';
6250  print '<input type="hidden" name="action" value="setabsolutediscount">';
6251  print '<input type="hidden" name="token" value="' . newToken() . '">';
6252  print '<div class="inline-block">';
6253  if (!empty($discount_type)) {
6254  if (getDolGlobalString('FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS')) {
6255  if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
6256  $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be subtracted to payments only and not to total of final invoice
6257  } else {
6258  $translationKey = 'HasCreditNoteFromSupplier';
6259  }
6260  } else {
6261  if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
6262  $translationKey = 'HasAbsoluteDiscountFromSupplier';
6263  } else {
6264  $translationKey = 'HasCreditNoteFromSupplier';
6265  }
6266  }
6267  } else {
6268  if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) {
6269  if (!$filter || $filter == "fk_facture_source IS NULL") {
6270  $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be subtracted to payments only and not to total of final invoice
6271  } else {
6272  $translationKey = 'CompanyHasCreditNote';
6273  }
6274  } else {
6275  if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
6276  $translationKey = 'CompanyHasAbsoluteDiscount';
6277  } else {
6278  $translationKey = 'CompanyHasCreditNote';
6279  }
6280  }
6281  }
6282  print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
6283  if (empty($hidelist)) {
6284  print ' ';
6285  }
6286  print '</div>';
6287  if (empty($hidelist)) {
6288  print '<div class="inline-block" style="padding-right: 10px">';
6289  $newfilter = 'discount_type=' . intval($discount_type);
6290  if (!empty($discount_type)) {
6291  $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
6292  } else {
6293  $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
6294  }
6295  if ($filter) {
6296  $newfilter .= ' AND (' . $filter . ')';
6297  }
6298  // output the combo of discounts
6299  $nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue);
6300  if ($nbqualifiedlines > 0) {
6301  print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="' . dol_escape_htmltag($langs->trans("UseLine")) . '"';
6302  if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
6303  print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
6304  }
6305  if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
6306  print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
6307  }
6308 
6309  print '>';
6310  }
6311  print '</div>';
6312  }
6313  if ($more) {
6314  print '<div class="inline-block">';
6315  print $more;
6316  print '</div>';
6317  }
6318  print '</form>';
6319  } else {
6320  if ($selected) {
6321  print $selected;
6322  } else {
6323  print "0";
6324  }
6325  }
6326  }
6327 
6328 
6329  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6330 
6340  public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
6341  {
6342  // phpcs:enable
6343  global $langs;
6344 
6345  if ($htmlname != "none") {
6346  print '<form method="post" action="' . $page . '">';
6347  print '<input type="hidden" name="action" value="set_contact">';
6348