dolibarr  17.0.4
html.form.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 2002-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
5  * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6  * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
7  * Copyright (C) 2005-2017 Regis Houssin <regis.houssin@inodbox.com>
8  * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
9  * Copyright (C) 2006 Marc Barilley/Ocebo <marc@ocebo.com>
10  * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerker@telenet.be>
11  * Copyright (C) 2007 Patrick Raguin <patrick.raguin@gmail.com>
12  * Copyright (C) 2010 Juanjo Menent <jmenent@2byte.es>
13  * Copyright (C) 2010-2021 Philippe Grand <philippe.grand@atoo-net.com>
14  * Copyright (C) 2011 Herve Prot <herve.prot@symeos.com>
15  * Copyright (C) 2012-2016 Marcos García <marcosgdf@gmail.com>
16  * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
17  * Copyright (C) 2012-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
18  * Copyright (C) 2014-2020 Alexandre Spangaro <aspangaro@open-dsi.fr>
19  * Copyright (C) 2018-2022 Ferran Marcet <fmarcet@2byte.es>
20  * Copyright (C) 2018-2021 Frédéric France <frederic.france@netlogic.fr>
21  * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
22  * Copyright (C) 2018 Christophe Battarel <christophe@altairis.fr>
23  * Copyright (C) 2018 Josep Lluis Amador <joseplluis@lliuretic.cat>
24  * Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
25  *
26  * This program is free software; you can redistribute it and/or modify
27  * it under the terms of the GNU General Public License as published by
28  * the Free Software Foundation; either version 3 of the License, or
29  * (at your option) any later version.
30  *
31  * This program is distributed in the hope that it will be useful,
32  * but WITHOUT ANY WARRANTY; without even the implied warranty of
33  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34  * GNU General Public License for more details.
35  *
36  * You should have received a copy of the GNU General Public License
37  * along with this program. If not, see <https://www.gnu.org/licenses/>.
38  */
39 
53 class Form
54 {
58  public $db;
59 
63  public $error = '';
64 
68  public $errors = array();
69 
70  public $num;
71 
72  // Cache arrays
73  public $cache_types_paiements = array();
74  public $cache_conditions_paiements = array();
75  public $cache_transport_mode = array();
76  public $cache_availability = array();
77  public $cache_demand_reason = array();
78  public $cache_types_fees = array();
79  public $cache_vatrates = array();
80 
81 
87  public function __construct($db)
88  {
89  $this->db = $db;
90  }
91 
108  public function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata = 'string', $moreparam = '', $fieldrequired = 0, $notabletag = 0, $paramid = 'id', $help = '')
109  {
110  global $conf, $langs;
111 
112  $ret = '';
113 
114  // TODO change for compatibility
115  if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;/', $typeofdata)) {
116  if (!empty($perm)) {
117  $tmp = explode(':', $typeofdata);
118  $ret .= '<div class="editkey_'.$tmp[0].(!empty($tmp[1]) ? ' '.$tmp[1] : '').'" id="'.$htmlname.'">';
119  if ($fieldrequired) {
120  $ret .= '<span class="fieldrequired">';
121  }
122  if ($help) {
123  $ret .= $this->textwithpicto($langs->trans($text), $help);
124  } else {
125  $ret .= $langs->trans($text);
126  }
127  if ($fieldrequired) {
128  $ret .= '</span>';
129  }
130  $ret .= '</div>'."\n";
131  } else {
132  if ($fieldrequired) {
133  $ret .= '<span class="fieldrequired">';
134  }
135  if ($help) {
136  $ret .= $this->textwithpicto($langs->trans($text), $help);
137  } else {
138  $ret .= $langs->trans($text);
139  }
140  if ($fieldrequired) {
141  $ret .= '</span>';
142  }
143  }
144  } else {
145  if (empty($notabletag) && $perm) {
146  $ret .= '<table class="nobordernopadding centpercent"><tr><td class="nowrap">';
147  }
148  if ($fieldrequired) {
149  $ret .= '<span class="fieldrequired">';
150  }
151  if ($help) {
152  $ret .= $this->textwithpicto($langs->trans($text), $help);
153  } else {
154  $ret .= $langs->trans($text);
155  }
156  if ($fieldrequired) {
157  $ret .= '</span>';
158  }
159  if (!empty($notabletag)) {
160  $ret .= ' ';
161  }
162  if (empty($notabletag) && $perm) {
163  $ret .= '</td>';
164  }
165  if (empty($notabletag) && $perm) {
166  $ret .= '<td class="right">';
167  }
168  if ($htmlname && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) {
169  $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>';
170  }
171  if (!empty($notabletag) && $notabletag == 1) {
172  $ret .= ' : ';
173  }
174  if (!empty($notabletag) && $notabletag == 3) {
175  $ret .= ' ';
176  }
177  if (empty($notabletag) && $perm) {
178  $ret .= '</td>';
179  }
180  if (empty($notabletag) && $perm) {
181  $ret .= '</tr></table>';
182  }
183  }
184 
185  return $ret;
186  }
187 
207  public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 0, $formatfunc = '', $paramid = 'id', $gm = 'auto')
208  {
209  global $conf, $langs;
210 
211  $ret = '';
212 
213  // Check parameters
214  if (empty($typeofdata)) {
215  return 'ErrorBadParameter typeofdata is empty';
216  }
217  // Clean paramater $typeofdata
218  if ($typeofdata == 'datetime') {
219  $typeofdata = 'dayhour';
220  }
221  $reg = array();
222  if (preg_match('/^(\w+)\‍((\d+)\‍)$/', $typeofdata, $reg)) {
223  if ($reg[1] == 'varchar') {
224  $typeofdata = 'string';
225  } elseif ($reg[1] == 'int') {
226  $typeofdata = 'numeric';
227  } else {
228  return 'ErrorBadParameter '.$typeofdata;
229  }
230  }
231 
232  // When option to edit inline is activated
233  if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;|day|datepicker|dayhour|datehourpicker/', $typeofdata)) { // TODO add jquery timepicker and support select
234  $ret .= $this->editInPlace($object, $value, $htmlname, $perm, $typeofdata, $editvalue, $extObject, $custommsg);
235  } else {
236  $editmode = (GETPOST('action', 'aZ09') == 'edit'.$htmlname);
237  if ($editmode) {
238  $ret .= "\n";
239  $ret .= '<form method="post" action="'.$_SERVER["PHP_SELF"].($moreparam ? '?'.$moreparam : '').'">';
240  $ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
241  $ret .= '<input type="hidden" name="token" value="'.newToken().'">';
242  $ret .= '<input type="hidden" name="'.$paramid.'" value="'.$object->id.'">';
243  if (empty($notabletag)) {
244  $ret .= '<table class="nobordernopadding centpercent">';
245  }
246  if (empty($notabletag)) {
247  $ret .= '<tr><td>';
248  }
249  if (preg_match('/^(string|safehtmlstring|email|url)/', $typeofdata)) {
250  $tmp = explode(':', $typeofdata);
251  $ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.($editvalue ? $editvalue : $value).'"'.(empty($tmp[1]) ? '' : ' size="'.$tmp[1].'"').' autofocus>';
252  } elseif (preg_match('/^(integer)/', $typeofdata)) {
253  $tmp = explode(':', $typeofdata);
254  $valuetoshow = price2num($editvalue ? $editvalue : $value, 0);
255  $ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.$valuetoshow.'"'.(empty($tmp[1]) ? '' : ' size="'.$tmp[1].'"').' autofocus>';
256  } elseif (preg_match('/^(numeric|amount)/', $typeofdata)) {
257  $tmp = explode(':', $typeofdata);
258  $valuetoshow = price2num($editvalue ? $editvalue : $value);
259  $ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.($valuetoshow != '' ? price($valuetoshow) : '').'"'.(empty($tmp[1]) ? '' : ' size="'.$tmp[1].'"').' autofocus>';
260  } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
261  $tmp = explode(':', $typeofdata);
262  $ret .= '<input type="checkbox" id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . (empty($tmp[1]) ? '' : $tmp[1]) . '/>';
263  } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) { // if wysiwyg is enabled $typeofdata = 'ckeditor'
264  $tmp = explode(':', $typeofdata);
265  $cols = (empty($tmp[2]) ? '' : $tmp[2]);
266  $morealt = '';
267  if (preg_match('/%/', $cols)) {
268  $morealt = ' style="width: '.$cols.'"';
269  $cols = '';
270  }
271 
272  $valuetoshow = ($editvalue ? $editvalue : $value);
273  $ret .= '<textarea id="'.$htmlname.'" name="'.$htmlname.'" wrap="soft" rows="'.(empty($tmp[1]) ? '20' : $tmp[1]).'"'.($cols ? ' cols="'.$cols.'"' : 'class="quatrevingtpercent"').$morealt.'" autofocus>';
274  // textarea convert automatically entities chars into simple chars.
275  // So we convert & into &amp; so a string like 'a &lt; <b>b</b><br>é<br>&lt;script&gt;alert('X');&lt;script&gt;' stay a correct html and is not converted by textarea component when wysiwig is off.
276  $valuetoshow = str_replace('&', '&amp;', $valuetoshow);
277  $ret .= dol_string_neverthesehtmltags($valuetoshow, array('textarea'));
278  $ret .= '</textarea>';
279  } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
280  $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form'.$htmlname, 1, 0, 0, '', '', '', '', 1, '', '', $gm);
281  } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
282  $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form'.$htmlname, 1, 0, 0, '', '', '', '', 1, '', '', $gm);
283  } elseif (preg_match('/^select;/', $typeofdata)) {
284  $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
285  $arraylist = array();
286  foreach ($arraydata as $val) {
287  $tmp = explode(':', $val);
288  $tmpkey = str_replace('|', ':', $tmp[0]);
289  $arraylist[$tmpkey] = $tmp[1];
290  }
291  $ret .= $this->selectarray($htmlname, $arraylist, $value);
292  } elseif (preg_match('/^ckeditor/', $typeofdata)) {
293  $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser
294  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
295  $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]));
296  $ret .= $doleditor->Create(1);
297  }
298  if (empty($notabletag)) {
299  $ret .= '</td>';
300  }
301 
302  // Button save-cancel
303  if (empty($notabletag)) {
304  $ret .= '<td class="left">';
305  }
306  //else $ret.='<div class="clearboth"></div>';
307  $ret .= '<input type="submit" class="smallpaddingimp button'.(empty($notabletag) ? '' : ' ').'" name="modify" value="'.$langs->trans("Modify").'">';
308  if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) {
309  $ret .= '<br>'."\n";
310  }
311  $ret .= '<input type="submit" class="smallpaddingimp button button-cancel'.(empty($notabletag) ? '' : ' ').'" name="cancel" value="'.$langs->trans("Cancel").'">';
312  if (empty($notabletag)) {
313  $ret .= '</td>';
314  }
315 
316  if (empty($notabletag)) {
317  $ret .= '</tr></table>'."\n";
318  }
319  $ret .= '</form>'."\n";
320  } else {
321  if (preg_match('/^(email)/', $typeofdata)) {
322  $ret .= dol_print_email($value, 0, 0, 0, 0, 1);
323  } elseif (preg_match('/^url/', $typeofdata)) {
324  $ret .= dol_print_url($value, '_blank', 32, 1);
325  } elseif (preg_match('/^(amount|numeric)/', $typeofdata)) {
326  $ret .= ($value != '' ? price($value, '', $langs, 0, -1, -1, $conf->currency) : '');
327  } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
328  $tmp = explode(':', $typeofdata);
329  $ret .= '<input type="checkbox" disabled id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($tmp[1] ? $tmp[1] : '') . '/>';
330  } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {
331  $ret .= dol_htmlentitiesbr($value);
332  } elseif (preg_match('/^safehtmlstring/', $typeofdata)) {
333  $ret .= dol_string_onlythesehtmltags($value);
334  } elseif (preg_match('/^restricthtml/', $typeofdata)) {
335  $ret .= dol_string_onlythesehtmltags($value);
336  } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
337  $ret .= '<span class="valuedate">'.dol_print_date($value, 'day', $gm).'</span>';
338  } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
339  $ret .= '<span class="valuedate">'.dol_print_date($value, 'dayhour', $gm).'</span>';
340  } elseif (preg_match('/^select;/', $typeofdata)) {
341  $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
342  $arraylist = array();
343  foreach ($arraydata as $val) {
344  $tmp = explode(':', $val);
345  $arraylist[$tmp[0]] = $tmp[1];
346  }
347  $ret .= $arraylist[$value];
348  if ($htmlname == 'fk_product_type') {
349  if ($value == 0) {
350  $ret = img_picto($langs->trans("Product"), 'product', 'class="paddingleftonly paddingrightonly colorgrey"').$ret;
351  } else {
352  $ret = img_picto($langs->trans("Service"), 'service', 'class="paddingleftonly paddingrightonly colorgrey"').$ret;
353  }
354  }
355  } elseif (preg_match('/^ckeditor/', $typeofdata)) {
356  $tmpcontent = dol_htmlentitiesbr($value);
357  if (!empty($conf->global->MAIN_DISABLE_NOTES_TAB)) {
358  $firstline = preg_replace('/<br>.*/', '', $tmpcontent);
359  $firstline = preg_replace('/[\n\r].*/', '', $firstline);
360  $tmpcontent = $firstline.((strlen($firstline) != strlen($tmpcontent)) ? '...' : '');
361  }
362  // We dont use dol_escape_htmltag to get the html formating active, but this need we must also
363  // clean data from some dangerous html
364  $ret .= dol_string_onlythesehtmltags(dol_htmlentitiesbr($tmpcontent));
365  } else {
366  $ret .= dol_escape_htmltag($value);
367  }
368 
369  if ($formatfunc && method_exists($object, $formatfunc)) {
370  $ret = $object->$formatfunc($ret);
371  }
372  }
373  }
374  return $ret;
375  }
376 
388  public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '')
389  {
390  global $conf, $langs, $extralanguages;
391 
392  $result = '';
393 
394  // List of extra languages
395  $arrayoflangcode = array();
396  if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) {
397  $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
398  }
399 
400  if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
401  if (!is_object($extralanguages)) {
402  include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php';
403  $extralanguages = new ExtraLanguages($this->db);
404  }
405  $extralanguages->fetch_name_extralanguages('societe');
406 
407  if (!is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) {
408  return ''; // No extralang field to show
409  }
410 
411  $result .= '<!-- Widget for translation -->'."\n";
412  $result .= '<div class="inline-block paddingleft image-'.$object->element.'-'.$fieldname.'">';
413  $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', false, 0, 0, '', 'fa-15 editfieldlang');
414  $result .= $s;
415  $result .= '</div>';
416 
417  $result .= '<div class="inline-block hidden field-'.$object->element.'-'.$fieldname.'">';
418 
419  $resultforextrlang = '';
420  foreach ($arrayoflangcode as $langcode) {
421  $valuetoshow = GETPOSTISSET('field-'.$object->element."-".$fieldname."-".$langcode) ? GETPOST('field-'.$object->element.'-'.$fieldname."-".$langcode, $check) : '';
422  if (empty($valuetoshow)) {
423  $object->fetchValuesForExtraLanguages();
424  //var_dump($object->array_languages);
425  $valuetoshow = $object->array_languages[$fieldname][$langcode];
426  }
427 
428  $s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"');
429  $resultforextrlang .= $s;
430 
431  // TODO Use the showInputField() method of ExtraLanguages object
432  if ($typeofdata == 'textarea') {
433  $resultforextrlang .= '<textarea name="field-'.$object->element."-".$fieldname."-".$langcode.'" id="'.$fieldname."-".$langcode.'" class="'.$morecss.'" rows="'.ROWS_2.'" wrap="soft">';
434  $resultforextrlang .= $valuetoshow;
435  $resultforextrlang .= '</textarea>';
436  } else {
437  $resultforextrlang .= '<input type="text" class="inputfieldforlang '.($morecss ? ' '.$morecss : '').'" name="field-'.$object->element.'-'.$fieldname.'-'.$langcode.'" value="'.$valuetoshow.'">';
438  }
439  }
440  $result .= $resultforextrlang;
441 
442  $result .= '</div>';
443  $result .= '<script>$(".image-'.$object->element.'-'.$fieldname.'").click(function() { console.log("Toggle lang widget"); jQuery(".field-'.$object->element.'-'.$fieldname.'").toggle(); });</script>';
444  }
445 
446  return $result;
447  }
448 
462  protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null)
463  {
464  global $conf;
465 
466  $out = '';
467 
468  // Check parameters
469  if (preg_match('/^text/', $inputType)) {
470  $value = dol_nl2br($value);
471  } elseif (preg_match('/^numeric/', $inputType)) {
472  $value = price($value);
473  } elseif ($inputType == 'day' || $inputType == 'datepicker') {
474  $value = dol_print_date($value, 'day');
475  }
476 
477  if ($condition) {
478  $element = false;
479  $table_element = false;
480  $fk_element = false;
481  $loadmethod = false;
482  $savemethod = false;
483  $ext_element = false;
484  $button_only = false;
485  $inputOption = '';
486  $rows = '';
487  $cols = '';
488 
489  if (is_object($object)) {
490  $element = $object->element;
491  $table_element = $object->table_element;
492  $fk_element = $object->id;
493  }
494 
495  if (is_object($extObject)) {
496  $ext_element = $extObject->element;
497  }
498 
499  if (preg_match('/^(string|email|numeric)/', $inputType)) {
500  $tmp = explode(':', $inputType);
501  $inputType = $tmp[0];
502  if (!empty($tmp[1])) {
503  $inputOption = $tmp[1];
504  }
505  if (!empty($tmp[2])) {
506  $savemethod = $tmp[2];
507  }
508  $out .= '<input id="width_'.$htmlname.'" value="'.$inputOption.'" type="hidden"/>'."\n";
509  } elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) {
510  $tmp = explode(':', $inputType);
511  $inputType = $tmp[0];
512  if (!empty($tmp[1])) {
513  $inputOption = $tmp[1];
514  }
515  if (!empty($tmp[2])) {
516  $savemethod = $tmp[2];
517  }
518 
519  $out .= '<input id="timestamp" type="hidden"/>'."\n"; // Use for timestamp format
520  } elseif (preg_match('/^(select|autocomplete)/', $inputType)) {
521  $tmp = explode(':', $inputType);
522  $inputType = $tmp[0];
523  $loadmethod = $tmp[1];
524  if (!empty($tmp[2])) {
525  $savemethod = $tmp[2];
526  }
527  if (!empty($tmp[3])) {
528  $button_only = true;
529  }
530  } elseif (preg_match('/^textarea/', $inputType)) {
531  $tmp = explode(':', $inputType);
532  $inputType = $tmp[0];
533  $rows = (empty($tmp[1]) ? '8' : $tmp[1]);
534  $cols = (empty($tmp[2]) ? '80' : $tmp[2]);
535  } elseif (preg_match('/^ckeditor/', $inputType)) {
536  $tmp = explode(':', $inputType);
537  $inputType = $tmp[0];
538  $toolbar = $tmp[1];
539  if (!empty($tmp[2])) {
540  $width = $tmp[2];
541  }
542  if (!empty($tmp[3])) {
543  $heigth = $tmp[3];
544  }
545  if (!empty($tmp[4])) {
546  $savemethod = $tmp[4];
547  }
548 
549  if (isModEnabled('fckeditor')) {
550  $out .= '<input id="ckeditor_toolbar" value="'.$toolbar.'" type="hidden"/>'."\n";
551  } else {
552  $inputType = 'textarea';
553  }
554  }
555 
556  $out .= '<input id="element_'.$htmlname.'" value="'.$element.'" type="hidden"/>'."\n";
557  $out .= '<input id="table_element_'.$htmlname.'" value="'.$table_element.'" type="hidden"/>'."\n";
558  $out .= '<input id="fk_element_'.$htmlname.'" value="'.$fk_element.'" type="hidden"/>'."\n";
559  $out .= '<input id="loadmethod_'.$htmlname.'" value="'.$loadmethod.'" type="hidden"/>'."\n";
560  if (!empty($savemethod)) {
561  $out .= '<input id="savemethod_'.$htmlname.'" value="'.$savemethod.'" type="hidden"/>'."\n";
562  }
563  if (!empty($ext_element)) {
564  $out .= '<input id="ext_element_'.$htmlname.'" value="'.$ext_element.'" type="hidden"/>'."\n";
565  }
566  if (!empty($custommsg)) {
567  if (is_array($custommsg)) {
568  if (!empty($custommsg['success'])) {
569  $out .= '<input id="successmsg_'.$htmlname.'" value="'.$custommsg['success'].'" type="hidden"/>'."\n";
570  }
571  if (!empty($custommsg['error'])) {
572  $out .= '<input id="errormsg_'.$htmlname.'" value="'.$custommsg['error'].'" type="hidden"/>'."\n";
573  }
574  } else {
575  $out .= '<input id="successmsg_'.$htmlname.'" value="'.$custommsg.'" type="hidden"/>'."\n";
576  }
577  }
578  if ($inputType == 'textarea') {
579  $out .= '<input id="textarea_'.$htmlname.'_rows" value="'.$rows.'" type="hidden"/>'."\n";
580  $out .= '<input id="textarea_'.$htmlname.'_cols" value="'.$cols.'" type="hidden"/>'."\n";
581  }
582  $out .= '<span id="viewval_'.$htmlname.'" class="viewval_'.$inputType.($button_only ? ' inactive' : ' active').'">'.$value.'</span>'."\n";
583  $out .= '<span id="editval_'.$htmlname.'" class="editval_'.$inputType.($button_only ? ' inactive' : ' active').' hideobject">'.(!empty($editvalue) ? $editvalue : $value).'</span>'."\n";
584  } else {
585  $out = $value;
586  }
587 
588  return $out;
589  }
590 
609  public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
610  {
611  if ($incbefore) {
612  $text = $incbefore.$text;
613  }
614  if (!$htmltext) {
615  return $text;
616  }
617  $direction = (int) $direction; // For backward compatibility when $direction was set to '' instead of 0
618 
619  $tag = 'td';
620  if ($notabs == 2) {
621  $tag = 'div';
622  }
623  if ($notabs == 3) {
624  $tag = 'span';
625  }
626  // Sanitize tooltip
627  $htmltext = str_replace(array("\r", "\n"), '', $htmltext);
628 
629  $extrastyle = '';
630  if ($direction < 0) {
631  $extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : '');
632  $extrastyle = 'padding: 0px; padding-left: 3px;';
633  }
634  if ($direction > 0) {
635  $extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : '');
636  $extrastyle = 'padding: 0px; padding-right: 3px;';
637  }
638 
639  $classfortooltip = 'classfortooltip';
640 
641  $s = '';
642  $textfordialog = '';
643 
644  if ($tooltiptrigger == '') {
645  $htmltext = str_replace('"', '&quot;', $htmltext);
646  } else {
647  $classfortooltip = 'classfortooltiponclick';
648  $textfordialog .= '<div style="display: none;" id="idfortooltiponclick_'.$tooltiptrigger.'" class="classfortooltiponclicktext">'.$htmltext.'</div>';
649  }
650  if ($tooltipon == 2 || $tooltipon == 3) {
651  $paramfortooltipimg = ' class="'.$classfortooltip.($notabs != 3 ? ' inline-block' : '').($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'"';
652  if ($tooltiptrigger == '') {
653  $paramfortooltipimg .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on img tag to store tooltip
654  } else {
655  $paramfortooltipimg .= ' dolid="'.$tooltiptrigger.'"';
656  }
657  } else {
658  $paramfortooltipimg = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag
659  }
660  if ($tooltipon == 1 || $tooltipon == 3) {
661  $paramfortooltiptd = ' class="'.($tooltipon == 3 ? 'cursorpointer ' : '').$classfortooltip.' inline-block'.($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'" ';
662  if ($tooltiptrigger == '') {
663  $paramfortooltiptd .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on td tag to store tooltip
664  } else {
665  $paramfortooltiptd .= ' dolid="'.$tooltiptrigger.'"';
666  }
667  } else {
668  $paramfortooltiptd = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag
669  }
670  if (empty($notabs)) {
671  $s .= '<table class="nobordernopadding"><tr style="height: auto;">';
672  } elseif ($notabs == 2) {
673  $s .= '<div class="inline-block'.($forcenowrap ? ' nowrap' : '').'">';
674  }
675  // Define value if value is before
676  if ($direction < 0) {
677  $s .= '<'.$tag.$paramfortooltipimg;
678  if ($tag == 'td') {
679  $s .= ' class="valigntop" width="14"';
680  }
681  $s .= '>'.$textfordialog.$img.'</'.$tag.'>';
682  }
683  // Use another method to help avoid having a space in value in order to use this value with jquery
684  // Define label
685  if ((string) $text != '') {
686  $s .= '<'.$tag.$paramfortooltiptd.'>'.$text.'</'.$tag.'>';
687  }
688  // Define value if value is after
689  if ($direction > 0) {
690  $s .= '<'.$tag.$paramfortooltipimg;
691  if ($tag == 'td') {
692  $s .= ' class="valignmiddle" width="14"';
693  }
694  $s .= '>'.$textfordialog.$img.'</'.$tag.'>';
695  }
696  if (empty($notabs)) {
697  $s .= '</tr></table>';
698  } elseif ($notabs == 2) {
699  $s .= '</div>';
700  }
701 
702  return $s;
703  }
704 
719  public function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
720  {
721  global $conf, $langs;
722 
723  //For backwards compatibility
724  if ($type == '0') {
725  $type = 'info';
726  } elseif ($type == '1') {
727  $type = 'help';
728  }
729 
730  if (preg_match('/onsmartphone$/', $tooltiptrigger) && empty($conf->dol_no_mouse_hover)) {
731  $tooltiptrigger = preg_replace('/^.*onsmartphone$/', '', $tooltiptrigger);
732  }
733 
734  $alt = '';
735  if ($tooltiptrigger) {
736  $alt = $langs->transnoentitiesnoconv("ClickToShowHelp");
737  }
738 
739  // If info or help with no javascript, show only text
740  if (empty($conf->use_javascript_ajax)) {
741  if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
742  return $text;
743  } else {
744  $alt = $htmltext;
745  $htmltext = '';
746  }
747  }
748 
749  // If info or help with smartphone, show only text (tooltip hover can't works)
750  if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) {
751  if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
752  return $text;
753  }
754  }
755  // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone)
756  //if (!empty($conf->dol_no_mouse_hover) && !empty($tooltiptrigger))
757  //{
758  //if ($type == 'info' || $type == 'help') return '<a href="'..'">'.$text.''</a>';
759  //}
760 
761  $img = '';
762  if ($type == 'info') {
763  $img = img_help(0, $alt);
764  } elseif ($type == 'help') {
765  $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
766  } elseif ($type == 'helpclickable') {
767  $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
768  } elseif ($type == 'superadmin') {
769  $img = img_picto($alt, 'redstar');
770  } elseif ($type == 'admin') {
771  $img = img_picto($alt, 'star');
772  } elseif ($type == 'warning') {
773  $img = img_warning($alt);
774  } elseif ($type != 'none') {
775  $img = img_picto($alt, $type); // $type can be an image path
776  }
777 
778  return $this->textwithtooltip($text, $htmltext, ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2), $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
779  }
780 
791  public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect')
792  {
793  global $conf, $langs, $hookmanager;
794 
795 
796  $disabled = 0;
797  $ret = '<div class="centpercent center">';
798  $ret .= '<select class="flat'.(empty($conf->use_javascript_ajax) ? '' : ' hideobject').' '.$name.' '.$name.'select valignmiddle alignstart" id="'.$name.'" name="'.$name.'"'.($disabled ? ' disabled="disabled"' : '').'>';
799 
800  // 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.
801  $parameters = array();
802  $reshook = $hookmanager->executeHooks('addMoreMassActions', $parameters); // Note that $action and $object may have been modified by hook
803  // check if there is a mass action
804  if (count($arrayofaction) == 0 && empty($hookmanager->resPrint)) {
805  return;
806  }
807  if (empty($reshook)) {
808  $ret .= '<option value="0"'.($disabled ? ' disabled="disabled"' : '').'>-- '.$langs->trans("SelectAction").' --</option>';
809  foreach ($arrayofaction as $code => $label) {
810  $ret .= '<option value="'.$code.'"'.($disabled ? ' disabled="disabled"' : '').' data-html="'.dol_escape_htmltag($label).'">'.$label.'</option>';
811  }
812  }
813  $ret .= $hookmanager->resPrint;
814 
815  $ret .= '</select>';
816 
817  if (empty($conf->dol_optimize_smallscreen)) {
818  $ret .= ajax_combobox('.'.$name.'select');
819  }
820 
821  // 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
822  $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.
823  $ret .= '<input type="submit" disabled name="confirmmassaction"'.(empty($conf->use_javascript_ajax) ? '' : ' style="display: none"').' class="button smallpaddingimp'.(empty($conf->use_javascript_ajax) ? '' : ' hideobject').' '.$name.' '.$name.'confirmed" value="'.dol_escape_htmltag($langs->trans("Confirm")).'">';
824  $ret .= '</div>';
825 
826  if (!empty($conf->use_javascript_ajax)) {
827  $ret .= '<!-- JS CODE TO ENABLE mass action select -->
828  <script>
829  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 */
830  {
831  atleastoneselected=0;
832  jQuery("."+cssclass).each(function( index ) {
833  /* console.log( index + ": " + $( this ).text() ); */
834  if ($(this).is(\':checked\')) atleastoneselected++;
835  });
836 
837  console.log("initCheckForSelect mode="+mode+" name="+name+" cssclass="+cssclass+" atleastoneselected="+atleastoneselected);
838 
839  if (atleastoneselected || '.$alwaysvisible.')
840  {
841  jQuery("."+name).show();
842  '.($selected ? 'if (atleastoneselected) { jQuery("."+name+"select").val("'.$selected.'").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', false); }' : '').'
843  '.($selected ? 'if (! atleastoneselected) { jQuery("."+name+"select").val("0").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', true); } ' : '').'
844  }
845  else
846  {
847  jQuery("."+name).hide();
848  jQuery("."+name+"other").hide();
849  }
850  }
851 
852  jQuery(document).ready(function () {
853  initCheckForSelect(0, "' . $name.'", "'.$cssclass.'");
854  jQuery(".' . $cssclass.'").click(function() {
855  initCheckForSelect(1, "'.$name.'", "'.$cssclass.'");
856  });
857  jQuery(".' . $name.'select").change(function() {
858  var massaction = $( this ).val();
859  var urlform = $( this ).closest("form").attr("action").replace("#show_files","");
860  if (massaction == "builddoc")
861  {
862  urlform = urlform + "#show_files";
863  }
864  $( this ).closest("form").attr("action", urlform);
865  console.log("we select a mass action name='.$name.' massaction="+massaction+" - "+urlform);
866  /* Warning: if you set submit button to disabled, post using Enter will no more work if there is no other button */
867  if ($(this).val() != \'0\')
868  {
869  jQuery(".' . $name.'confirmed").prop(\'disabled\', false);
870  jQuery(".' . $name.'other").hide(); /* To disable if another div was open */
871  jQuery(".' . $name.'"+massaction).show();
872  }
873  else
874  {
875  jQuery(".' . $name.'confirmed").prop(\'disabled\', true);
876  jQuery(".' . $name.'other").hide(); /* To disable any div open */
877  }
878  });
879  });
880  </script>
881  ';
882  }
883 
884  return $ret;
885  }
886 
887  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
904  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)
905  {
906  // phpcs:enable
907  global $conf, $langs, $mysoc;
908 
909  $langs->load("dict");
910 
911  $out = '';
912  $countryArray = array();
913  $favorite = array();
914  $label = array();
915  $atleastonefavorite = 0;
916 
917  $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite, eec";
918  $sql .= " FROM ".$this->db->prefix()."c_country";
919  $sql .= " WHERE active > 0";
920  //$sql.= " ORDER BY code ASC";
921 
922  dol_syslog(get_class($this)."::select_country", LOG_DEBUG);
923  $resql = $this->db->query($sql);
924  if ($resql) {
925  $out .= '<select id="select'.$htmlname.'" class="flat maxwidth200onsmartphone selectcountry'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" '.$htmloption.'>';
926  $num = $this->db->num_rows($resql);
927  $i = 0;
928  if ($num) {
929  while ($i < $num) {
930  $obj = $this->db->fetch_object($resql);
931 
932  $countryArray[$i]['rowid'] = $obj->rowid;
933  $countryArray[$i]['code_iso'] = $obj->code_iso;
934  $countryArray[$i]['code_iso3'] = $obj->code_iso3;
935  $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 : ''));
936  $countryArray[$i]['favorite'] = $obj->favorite;
937  $countryArray[$i]['eec'] = $obj->eec;
938  $favorite[$i] = $obj->favorite;
939  $label[$i] = dol_string_unaccent($countryArray[$i]['label']);
940  $i++;
941  }
942 
943  if (empty($disablefavorites)) {
944  $array1_sort_order = SORT_DESC;
945  $array2_sort_order = SORT_ASC;
946  array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $countryArray);
947  } else {
948  $countryArray = dol_sort_array($countryArray, 'label');
949  }
950 
951  if ($showempty) {
952  if (is_numeric($showempty)) {
953  $out .= '<option value="">&nbsp;</option>'."\n";
954  } else {
955  $out .= '<option value="">'.$langs->trans($showempty).'</option>'."\n";
956  }
957  }
958 
959  if ($addspecialentries) { // Add dedicated entries for groups of countries
960  //if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
961  $out .= '<option value="special_allnotme"'.($selected == 'special_allnotme' ? ' selected' : '').'>'.$langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
962  $out .= '<option value="special_eec"'.($selected == 'special_eec' ? ' selected' : '').'>'.$langs->trans("CountriesInEEC").'</option>';
963  if ($mysoc->isInEEC()) {
964  $out .= '<option value="special_eecnotme"'.($selected == 'special_eecnotme' ? ' selected' : '').'>'.$langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
965  }
966  $out .= '<option value="special_noteec"'.($selected == 'special_noteec' ? ' selected' : '').'>'.$langs->trans("CountriesNotInEEC").'</option>';
967  $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
968  }
969 
970  foreach ($countryArray as $row) {
971  //if (empty($showempty) && empty($row['rowid'])) continue;
972  if (empty($row['rowid'])) {
973  continue;
974  }
975  if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
976  continue; // exclude some countries
977  }
978 
979  if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
980  $atleastonefavorite++;
981  }
982  if (empty($row['favorite']) && $atleastonefavorite) {
983  $atleastonefavorite = 0;
984  $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
985  }
986 
987  $labeltoshow = '';
988  if ($row['label']) {
989  $labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
990  } else {
991  $labeltoshow .= '&nbsp;';
992  }
993  if ($row['code_iso']) {
994  $labeltoshow .= ' <span class="opacitymedium">('.$row['code_iso'].')</span>';
995  if (empty($hideflags)) {
996  $tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
997  $labeltoshow = $tmpflag.' '.$labeltoshow;
998  }
999  }
1000 
1001  if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
1002  $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']).'">';
1003  } else {
1004  $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']).'">';
1005  }
1006  $out .= $labeltoshow;
1007  $out .= '</option>'."\n";
1008  }
1009  }
1010  $out .= '</select>';
1011  } else {
1012  dol_print_error($this->db);
1013  }
1014 
1015  // Make select dynamic
1016  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1017  $out .= ajax_combobox('select'.$htmlname, array(), 0, 0, 'resolve');
1018 
1019  return $out;
1020  }
1021 
1022  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1036  public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array(), $disableautocomplete = 0)
1037  {
1038  // phpcs:enable
1039  global $conf, $langs;
1040 
1041  $langs->load("dict");
1042 
1043  $out = '';
1044  $moreattrib = '';
1045  $incotermArray = array();
1046 
1047  $sql = "SELECT rowid, code";
1048  $sql .= " FROM ".$this->db->prefix()."c_incoterms";
1049  $sql .= " WHERE active > 0";
1050  $sql .= " ORDER BY code ASC";
1051 
1052  dol_syslog(get_class($this)."::select_incoterm", LOG_DEBUG);
1053  $resql = $this->db->query($sql);
1054  if ($resql) {
1055  if ($conf->use_javascript_ajax && !$forcecombo) {
1056  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1057  $out .= ajax_combobox($htmlname, $events);
1058  }
1059 
1060  if (!empty($page)) {
1061  $out .= '<form method="post" action="'.$page.'">';
1062  $out .= '<input type="hidden" name="action" value="set_incoterms">';
1063  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
1064  }
1065 
1066  $out .= '<select id="'.$htmlname.'" class="flat selectincoterm width75" name="'.$htmlname.'" '.$htmloption.'>';
1067  $out .= '<option value="0">&nbsp;</option>';
1068  $num = $this->db->num_rows($resql);
1069  $i = 0;
1070  if ($num) {
1071  while ($i < $num) {
1072  $obj = $this->db->fetch_object($resql);
1073  $incotermArray[$i]['rowid'] = $obj->rowid;
1074  $incotermArray[$i]['code'] = $obj->code;
1075  $i++;
1076  }
1077 
1078  foreach ($incotermArray as $row) {
1079  if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1080  $out .= '<option value="'.$row['rowid'].'" selected>';
1081  } else {
1082  $out .= '<option value="'.$row['rowid'].'">';
1083  }
1084 
1085  if ($row['code']) {
1086  $out .= $row['code'];
1087  }
1088 
1089  $out .= '</option>';
1090  }
1091  }
1092  $out .= '</select>';
1093 
1094  if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
1095  $out .= ajax_multiautocompleter('location_incoterms', '', DOL_URL_ROOT.'/core/ajax/locationincoterms.php')."\n";
1096  $moreattrib .= ' autocomplete="off"';
1097  }
1098  $out .= '<input id="location_incoterms" class="maxwidthonsmartphone type="text" name="location_incoterms" value="'.$location_incoterms.'">'."\n";
1099 
1100  if (!empty($page)) {
1101  $out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="'.$langs->trans("Modify").'"></form>';
1102  }
1103  } else {
1104  dol_print_error($this->db);
1105  }
1106 
1107  return $out;
1108  }
1109 
1110  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1122  public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0)
1123  {
1124  // phpcs:enable
1125  global $langs, $conf;
1126 
1127  // If product & services are enabled or both disabled.
1128  if ($forceall == 1 || (empty($forceall) && isModEnabled("product") && isModEnabled("service"))
1129  || (empty($forceall) && !isModEnabled('product') && !isModEnabled('service'))) {
1130  if (empty($hidetext)) {
1131  print $langs->trans("Type").': ';
1132  }
1133  print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
1134  if ($showempty) {
1135  print '<option value="-1"';
1136  if ($selected == -1) {
1137  print ' selected';
1138  }
1139  print '>&nbsp;</option>';
1140  }
1141 
1142  print '<option value="0"';
1143  if (0 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'product')) {
1144  print ' selected';
1145  }
1146  print '>'.$langs->trans("Product");
1147 
1148  print '<option value="1"';
1149  if (1 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'service')) {
1150  print ' selected';
1151  }
1152  print '>'.$langs->trans("Service");
1153 
1154  print '</select>';
1155  print ajax_combobox('select_'.$htmlname);
1156  //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1157  }
1158  if ((empty($forceall) && !isModEnabled('product') && isModEnabled("service")) || $forceall == 3) {
1159  print $langs->trans("Service");
1160  print '<input type="hidden" name="'.$htmlname.'" value="1">';
1161  }
1162  if ((empty($forceall) && isModEnabled("product") && !isModEnabled('service')) || $forceall == 2) {
1163  print $langs->trans("Product");
1164  print '<input type="hidden" name="'.$htmlname.'" value="0">';
1165  }
1166  if ($forceall < 0) { // This should happened only for contracts when both predefined product and service are disabled.
1167  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
1168  }
1169  }
1170 
1171  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1177  public function load_cache_types_fees()
1178  {
1179  // phpcs:enable
1180  global $langs;
1181 
1182  $num = count($this->cache_types_fees);
1183  if ($num > 0) {
1184  return 0; // Cache already loaded
1185  }
1186 
1187  dol_syslog(__METHOD__, LOG_DEBUG);
1188 
1189  $langs->load("trips");
1190 
1191  $sql = "SELECT c.code, c.label";
1192  $sql .= " FROM ".$this->db->prefix()."c_type_fees as c";
1193  $sql .= " WHERE active > 0";
1194 
1195  $resql = $this->db->query($sql);
1196  if ($resql) {
1197  $num = $this->db->num_rows($resql);
1198  $i = 0;
1199 
1200  while ($i < $num) {
1201  $obj = $this->db->fetch_object($resql);
1202 
1203  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1204  $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1205  $this->cache_types_fees[$obj->code] = $label;
1206  $i++;
1207  }
1208 
1209  asort($this->cache_types_fees);
1210 
1211  return $num;
1212  } else {
1213  dol_print_error($this->db);
1214  return -1;
1215  }
1216  }
1217 
1218  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1227  public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1228  {
1229  // phpcs:enable
1230  global $user, $langs;
1231 
1232  dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
1233 
1234  $this->load_cache_types_fees();
1235 
1236  print '<select id="select_'.$htmlname.'" class="flat" name="'.$htmlname.'">';
1237  if ($showempty) {
1238  print '<option value="-1"';
1239  if ($selected == -1) {
1240  print ' selected';
1241  }
1242  print '>&nbsp;</option>';
1243  }
1244 
1245  foreach ($this->cache_types_fees as $key => $value) {
1246  print '<option value="'.$key.'"';
1247  if ($key == $selected) {
1248  print ' selected';
1249  }
1250  print '>';
1251  print $value;
1252  print '</option>';
1253  }
1254 
1255  print '</select>';
1256  if ($user->admin) {
1257  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1258  }
1259  }
1260 
1261 
1262  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1284  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)
1285  {
1286  // phpcs:enable
1287  global $conf, $user, $langs;
1288 
1289  $out = '';
1290 
1291  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && !$forcecombo) {
1292  if (is_null($ajaxoptions)) {
1293  $ajaxoptions = array();
1294  }
1295 
1296  require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1297 
1298  // No immediate load of all database
1299  $placeholder = '';
1300  if ($selected && empty($selected_input_value)) {
1301  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1302  $societetmp = new Societe($this->db);
1303  $societetmp->fetch($selected);
1304  $selected_input_value = $societetmp->name;
1305  unset($societetmp);
1306  }
1307 
1308  // mode 1
1309  $urloption = 'htmlname='.urlencode(str_replace('.', '_', $htmlname)).'&outjson=1&filter='.urlencode($filter).(empty($excludeids) ? '' : '&excludeids='.join(',', $excludeids)).($showtype ? '&showtype='.urlencode($showtype) : '').($showcode ? '&showcode='.urlencode($showcode) : '');
1310 
1311  $out .= '<style type="text/css">.ui-autocomplete { z-index: 1003; }</style>';
1312  if (empty($hidelabel)) {
1313  print $langs->trans("RefOrLabel").' : ';
1314  } elseif ($hidelabel > 1) {
1315  $placeholder = $langs->trans("RefOrLabel");
1316  if ($hidelabel == 2) {
1317  $out .= img_picto($langs->trans("Search"), 'search');
1318  }
1319  }
1320  $out .= '<input type="text" class="'.$morecss.'" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.($placeholder ? ' placeholder="'.dol_escape_htmltag($placeholder).'"' : '').' '.(!empty($conf->global->THIRDPARTY_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
1321  if ($hidelabel == 3) {
1322  $out .= img_picto($langs->trans("Search"), 'search');
1323  }
1324 
1325  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, $conf->global->COMPANY_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
1326  } else {
1327  // Immediate load of all database
1328  $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids, $showcode);
1329  }
1330 
1331  return $out;
1332  }
1333 
1334  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1356  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)
1357  {
1358  // phpcs:enable
1359  global $conf, $user, $langs;
1360  global $hookmanager;
1361 
1362  $out = '';
1363  $num = 0;
1364  $outarray = array();
1365 
1366  if ($selected === '') {
1367  $selected = array();
1368  } elseif (!is_array($selected)) {
1369  $selected = array($selected);
1370  }
1371 
1372  // Clean $filter that may contains sql conditions so sql code
1373  if (function_exists('testSqlAndScriptInject')) {
1374  if (testSqlAndScriptInject($filter, 3) > 0) {
1375  $filter = '';
1376  }
1377  }
1378 
1379  // We search companies
1380  $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
1381  if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1382  $sql .= ", s.address, s.zip, s.town";
1383  $sql .= ", dictp.code as country_code";
1384  }
1385  $sql .= " FROM ".$this->db->prefix()."societe as s";
1386  if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1387  $sql .= " LEFT JOIN ".$this->db->prefix()."c_country as dictp ON dictp.rowid = s.fk_pays";
1388  }
1389  if (empty($user->rights->societe->client->voir) && !$user->socid) {
1390  $sql .= ", ".$this->db->prefix()."societe_commerciaux as sc";
1391  }
1392  $sql .= " WHERE s.entity IN (".getEntity('societe').")";
1393  if (!empty($user->socid)) {
1394  $sql .= " AND s.rowid = ".((int) $user->socid);
1395  }
1396  if ($filter) {
1397  $sql .= " AND (".$filter.")";
1398  }
1399  if (empty($user->rights->societe->client->voir) && !$user->socid) {
1400  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
1401  }
1402  if (!empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) {
1403  $sql .= " AND s.status <> 0";
1404  }
1405  if (!empty($excludeids)) {
1406  $sql .= " AND s.rowid NOT IN (".$this->db->sanitize(join(',', $excludeids)).")";
1407  }
1408  // Add where from hooks
1409  $parameters = array();
1410  $reshook = $hookmanager->executeHooks('selectThirdpartyListWhere', $parameters); // Note that $action and $object may have been modified by hook
1411  $sql .= $hookmanager->resPrint;
1412  // Add criteria
1413  if ($filterkey && $filterkey != '') {
1414  $sql .= " AND (";
1415  $prefix = empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
1416  // For natural search
1417  $scrit = explode(' ', $filterkey);
1418  $i = 0;
1419  if (count($scrit) > 1) {
1420  $sql .= "(";
1421  }
1422  foreach ($scrit as $crit) {
1423  if ($i > 0) {
1424  $sql .= " AND ";
1425  }
1426  $sql .= "(s.nom LIKE '".$this->db->escape($prefix.$crit)."%')";
1427  $i++;
1428  }
1429  if (count($scrit) > 1) {
1430  $sql .= ")";
1431  }
1432  if (isModEnabled('barcode')) {
1433  $sql .= " OR s.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1434  }
1435  $sql .= " OR s.code_client LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.code_fournisseur LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1436  $sql .= " OR s.name_alias LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.tva_intra LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1437  $sql .= ")";
1438  }
1439  $sql .= $this->db->order("nom", "ASC");
1440  $sql .= $this->db->plimit($limit, 0);
1441 
1442  // Build output string
1443  dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
1444  $resql = $this->db->query($sql);
1445  if ($resql) {
1446  if (!$forcecombo) {
1447  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1448  $out .= ajax_combobox($htmlname, $events, getDolGlobalString("COMPANY_USE_SEARCH_TO_SELECT"));
1449  }
1450 
1451  // Construct $out and $outarray
1452  $out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').'>'."\n";
1453 
1454  $textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
1455  if (!empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT)) {
1456  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
1457  //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
1458  if ($showempty && !is_numeric($showempty)) {
1459  $textifempty = $langs->trans($showempty);
1460  } else {
1461  $textifempty .= $langs->trans("All");
1462  }
1463  }
1464  if ($showempty) {
1465  $out .= '<option value="-1" data-html="'.dol_escape_htmltag('<span class="opacitymedium">'.($textifempty ? $textifempty : '&nbsp;').'</span>').'">'.$textifempty.'</option>'."\n";
1466  }
1467 
1468  $companytemp = new Societe($this->db);
1469 
1470  $num = $this->db->num_rows($resql);
1471  $i = 0;
1472  if ($num) {
1473  while ($i < $num) {
1474  $obj = $this->db->fetch_object($resql);
1475  $label = '';
1476  if ($showcode || !empty($conf->global->SOCIETE_ADD_REF_IN_LIST)) {
1477  if (($obj->client) && (!empty($obj->code_client))) {
1478  $label = $obj->code_client.' - ';
1479  }
1480  if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
1481  $label .= $obj->code_fournisseur.' - ';
1482  }
1483  $label .= ' '.$obj->name;
1484  } else {
1485  $label = $obj->name;
1486  }
1487 
1488  if (!empty($obj->name_alias)) {
1489  $label .= ' ('.$obj->name_alias.')';
1490  }
1491 
1492  if (!empty($conf->global->SOCIETE_SHOW_VAT_IN_LIST) && !empty($obj->tva_intra)) {
1493  $label .= ' - '.$obj->tva_intra;
1494  }
1495 
1496  $labelhtml = $label;
1497 
1498  if ($showtype) {
1499  $companytemp->id = $obj->rowid;
1500  $companytemp->client = $obj->client;
1501  $companytemp->fournisseur = $obj->fournisseur;
1502  $tmptype = $companytemp->getTypeUrl(1, '', 0, 'span');
1503  if ($tmptype) {
1504  $labelhtml .= ' '.$tmptype;
1505  }
1506 
1507  if ($obj->client || $obj->fournisseur) {
1508  $label .= ' (';
1509  }
1510  if ($obj->client == 1 || $obj->client == 3) {
1511  $label .= $langs->trans("Customer");
1512  }
1513  if ($obj->client == 2 || $obj->client == 3) {
1514  $label .= ($obj->client == 3 ? ', ' : '').$langs->trans("Prospect");
1515  }
1516  if ($obj->fournisseur) {
1517  $label .= ($obj->client ? ', ' : '').$langs->trans("Supplier");
1518  }
1519  if ($obj->client || $obj->fournisseur) {
1520  $label .= ')';
1521  }
1522  }
1523 
1524  if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1525  $s = ($obj->address ? ' - '.$obj->address : '').($obj->zip ? ' - '.$obj->zip : '').($obj->town ? ' '.$obj->town : '');
1526  if (!empty($obj->country_code)) {
1527  $s .= ', '.$langs->trans('Country'.$obj->country_code);
1528  }
1529  $label .= $s;
1530  $labelhtml .= $s;
1531  }
1532 
1533  if (empty($outputmode)) {
1534  if (in_array($obj->rowid, $selected)) {
1535  $out .= '<option value="'.$obj->rowid.'" selected data-html="'.dol_escape_htmltag($labelhtml).'">'.$label.'</option>';
1536  } else {
1537  $out .= '<option value="'.$obj->rowid.'" data-html="'.dol_escape_htmltag($labelhtml).'">'.$label.'</option>';
1538  }
1539  } else {
1540  array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label, 'labelhtml'=>$labelhtml));
1541  }
1542 
1543  $i++;
1544  if (($i % 10) == 0) {
1545  $out .= "\n";
1546  }
1547  }
1548  }
1549  $out .= '</select>'."\n";
1550  } else {
1551  dol_print_error($this->db);
1552  }
1553 
1554  $this->result = array('nbofthirdparties'=>$num);
1555 
1556  if ($outputmode) {
1557  return $outarray;
1558  }
1559  return $out;
1560  }
1561 
1562 
1563  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1574  public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
1575  {
1576  // phpcs:enable
1577  global $langs, $conf;
1578 
1579  // On recherche les remises
1580  $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1581  $sql .= " re.description, re.fk_facture_source";
1582  $sql .= " FROM ".$this->db->prefix()."societe_remise_except as re";
1583  $sql .= " WHERE re.fk_soc = ".(int) $socid;
1584  $sql .= " AND re.entity = ".$conf->entity;
1585  if ($filter) {
1586  $sql .= " AND ".$filter;
1587  }
1588  $sql .= " ORDER BY re.description ASC";
1589 
1590  dol_syslog(get_class($this)."::select_remises", LOG_DEBUG);
1591  $resql = $this->db->query($sql);
1592  if ($resql) {
1593  print '<select id="select_'.$htmlname.'" class="flat maxwidthonsmartphone" name="'.$htmlname.'">';
1594  $num = $this->db->num_rows($resql);
1595 
1596  $qualifiedlines = $num;
1597 
1598  $i = 0;
1599  if ($num) {
1600  print '<option value="0">&nbsp;</option>';
1601  while ($i < $num) {
1602  $obj = $this->db->fetch_object($resql);
1603  $desc = dol_trunc($obj->description, 40);
1604  if (preg_match('/\‍(CREDIT_NOTE\‍)/', $desc)) {
1605  $desc = preg_replace('/\‍(CREDIT_NOTE\‍)/', $langs->trans("CreditNote"), $desc);
1606  }
1607  if (preg_match('/\‍(DEPOSIT\‍)/', $desc)) {
1608  $desc = preg_replace('/\‍(DEPOSIT\‍)/', $langs->trans("Deposit"), $desc);
1609  }
1610  if (preg_match('/\‍(EXCESS RECEIVED\‍)/', $desc)) {
1611  $desc = preg_replace('/\‍(EXCESS RECEIVED\‍)/', $langs->trans("ExcessReceived"), $desc);
1612  }
1613  if (preg_match('/\‍(EXCESS PAID\‍)/', $desc)) {
1614  $desc = preg_replace('/\‍(EXCESS PAID\‍)/', $langs->trans("ExcessPaid"), $desc);
1615  }
1616 
1617  $selectstring = '';
1618  if ($selected > 0 && $selected == $obj->rowid) {
1619  $selectstring = ' selected';
1620  }
1621 
1622  $disabled = '';
1623  if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
1624  $qualifiedlines--;
1625  $disabled = ' disabled';
1626  }
1627 
1628  if (!empty($conf->global->MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST) && !empty($obj->fk_facture_source)) {
1629  $tmpfac = new Facture($this->db);
1630  if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
1631  $desc = $desc.' - '.$tmpfac->ref;
1632  }
1633  }
1634 
1635  print '<option value="'.$obj->rowid.'"'.$selectstring.$disabled.'>'.$desc.' ('.price($obj->amount_ht).' '.$langs->trans("HT").' - '.price($obj->amount_ttc).' '.$langs->trans("TTC").')</option>';
1636  $i++;
1637  }
1638  }
1639  print '</select>';
1640  print ajax_combobox('select_'.$htmlname);
1641 
1642  return $qualifiedlines;
1643  } else {
1644  dol_print_error($this->db);
1645  return -1;
1646  }
1647  }
1648 
1649  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1670  public function select_contacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $showsoc = 0, $forcecombo = 0, $events = array(), $options_only = false, $moreparam = '', $htmlid = '')
1671  {
1672  // phpcs:enable
1673  print $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $morecss, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid);
1674  return $this->num;
1675  }
1676 
1701  public function selectcontacts($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $options_only = false, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false, $disableifempty = 0)
1702  {
1703  global $conf, $langs, $hookmanager, $action;
1704 
1705  $langs->load('companies');
1706 
1707  if (empty($htmlid)) {
1708  $htmlid = $htmlname;
1709  }
1710  $num = 0;
1711 
1712  if ($selected === '') {
1713  $selected = array();
1714  } elseif (!is_array($selected)) {
1715  $selected = array($selected);
1716  }
1717  $out = '';
1718 
1719  if (!is_object($hookmanager)) {
1720  include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1721  $hookmanager = new HookManager($this->db);
1722  }
1723 
1724  // We search third parties
1725  $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";
1726  if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1727  $sql .= ", s.nom as company, s.town AS company_town";
1728  }
1729  $sql .= " FROM ".$this->db->prefix()."socpeople as sp";
1730  if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1731  $sql .= " LEFT OUTER JOIN ".$this->db->prefix()."societe as s ON s.rowid=sp.fk_soc";
1732  }
1733  $sql .= " WHERE sp.entity IN (".getEntity('contact').")";
1734  if ($socid > 0 || $socid == -1) {
1735  $sql .= " AND sp.fk_soc = ".((int) $socid);
1736  }
1737  if (!empty($conf->global->CONTACT_HIDE_INACTIVE_IN_COMBOBOX)) {
1738  $sql .= " AND sp.statut <> 0";
1739  }
1740  // Add where from hooks
1741  $parameters = array();
1742  $reshook = $hookmanager->executeHooks('selectContactListWhere', $parameters); // Note that $action and $object may have been modified by hook
1743  $sql .= $hookmanager->resPrint;
1744  $sql .= " ORDER BY sp.lastname ASC";
1745 
1746  dol_syslog(get_class($this)."::selectcontacts", LOG_DEBUG);
1747  $resql = $this->db->query($sql);
1748  if ($resql) {
1749  $num = $this->db->num_rows($resql);
1750 
1751  if ($htmlname != 'none' && !$options_only) {
1752  $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="'.$htmlid.'" name="'.$htmlname.(($num || empty($disableifempty)) ? '' : ' disabled').($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.(!empty($moreparam) ? $moreparam : '').'>';
1753  }
1754 
1755  if ($showempty && ! is_numeric($showempty)) {
1756  $textforempty = $showempty;
1757  $out .= '<option class="optiongrey" value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'>'.$textforempty.'</option>';
1758  } else {
1759  if (($showempty == 1 || ($showempty == 3 && $num > 1)) && ! $multiple) {
1760  $out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>&nbsp;</option>';
1761  }
1762  if ($showempty == 2) {
1763  $out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>-- '.$langs->trans("Internal").' --</option>';
1764  }
1765  }
1766 
1767  $i = 0;
1768  if ($num) {
1769  include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1770  $contactstatic = new Contact($this->db);
1771 
1772  while ($i < $num) {
1773  $obj = $this->db->fetch_object($resql);
1774 
1775  // Set email (or phones) and town extended infos
1776  $extendedInfos = '';
1777  if (!empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1778  $extendedInfos = array();
1779  $email = trim($obj->email);
1780  if (!empty($email)) {
1781  $extendedInfos[] = $email;
1782  } else {
1783  $phone = trim($obj->phone);
1784  $phone_perso = trim($obj->phone_perso);
1785  $phone_mobile = trim($obj->phone_mobile);
1786  if (!empty($phone)) {
1787  $extendedInfos[] = $phone;
1788  }
1789  if (!empty($phone_perso)) {
1790  $extendedInfos[] = $phone_perso;
1791  }
1792  if (!empty($phone_mobile)) {
1793  $extendedInfos[] = $phone_mobile;
1794  }
1795  }
1796  $contact_town = trim($obj->contact_town);
1797  $company_town = trim($obj->company_town);
1798  if (!empty($contact_town)) {
1799  $extendedInfos[] = $contact_town;
1800  } elseif (!empty($company_town)) {
1801  $extendedInfos[] = $company_town;
1802  }
1803  $extendedInfos = implode(' - ', $extendedInfos);
1804  if (!empty($extendedInfos)) {
1805  $extendedInfos = ' - '.$extendedInfos;
1806  }
1807  }
1808 
1809  $contactstatic->id = $obj->rowid;
1810  $contactstatic->lastname = $obj->lastname;
1811  $contactstatic->firstname = $obj->firstname;
1812  if ($obj->statut == 1) {
1813  if ($htmlname != 'none') {
1814  $disabled = 0;
1815  if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
1816  $disabled = 1;
1817  }
1818  if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
1819  $disabled = 1;
1820  }
1821  if (!empty($selected) && in_array($obj->rowid, $selected)) {
1822  $out .= '<option value="'.$obj->rowid.'"';
1823  if ($disabled) {
1824  $out .= ' disabled';
1825  }
1826  $out .= ' selected>';
1827  $out .= $contactstatic->getFullName($langs).$extendedInfos;
1828  if ($showfunction && $obj->poste) {
1829  $out .= ' ('.$obj->poste.')';
1830  }
1831  if (($showsoc > 0) && $obj->company) {
1832  $out .= ' - ('.$obj->company.')';
1833  }
1834  $out .= '</option>';
1835  } else {
1836  $out .= '<option value="'.$obj->rowid.'"';
1837  if ($disabled) {
1838  $out .= ' disabled';
1839  }
1840  $out .= '>';
1841  $out .= $contactstatic->getFullName($langs).$extendedInfos;
1842  if ($showfunction && $obj->poste) {
1843  $out .= ' ('.$obj->poste.')';
1844  }
1845  if (($showsoc > 0) && $obj->company) {
1846  $out .= ' - ('.$obj->company.')';
1847  }
1848  $out .= '</option>';
1849  }
1850  } else {
1851  if (in_array($obj->rowid, $selected)) {
1852  $out .= $contactstatic->getFullName($langs).$extendedInfos;
1853  if ($showfunction && $obj->poste) {
1854  $out .= ' ('.$obj->poste.')';
1855  }
1856  if (($showsoc > 0) && $obj->company) {
1857  $out .= ' - ('.$obj->company.')';
1858  }
1859  }
1860  }
1861  }
1862  $i++;
1863  }
1864  } else {
1865  $labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
1866  $out .= '<option class="disabled" value="-1"'.(($showempty == 2 || $multiple) ? '' : ' selected').' disabled="disabled">';
1867  $out .= $labeltoshow;
1868  $out .= '</option>';
1869  }
1870 
1871  $parameters = array(
1872  'socid'=>$socid,
1873  'htmlname'=>$htmlname,
1874  'resql'=>$resql,
1875  'out'=>&$out,
1876  'showfunction'=>$showfunction,
1877  'showsoc'=>$showsoc,
1878  );
1879 
1880  $reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1881 
1882  if ($htmlname != 'none' && !$options_only) {
1883  $out .= '</select>';
1884  }
1885 
1886  if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
1887  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1888  $out .= ajax_combobox($htmlid, $events, getDolGlobalString("CONTACT_USE_SEARCH_TO_SELECT"));
1889  }
1890 
1891  $this->num = $num;
1892  return $out;
1893  } else {
1894  dol_print_error($this->db);
1895  return -1;
1896  }
1897  }
1898 
1899  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1915  public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0')
1916  {
1917  // phpcs:enable
1918  print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
1919  }
1920 
1921  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1946  public function select_dolusers($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $show_every = 0, $enableonlytext = '', $morecss = '', $notdisabled = 0, $outputmode = 0, $multiple = false, $forcecombo = 0)
1947  {
1948  // phpcs:enable
1949  global $conf, $user, $langs, $hookmanager;
1950  global $action;
1951 
1952  // If no preselected user defined, we take current user
1953  if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && empty($conf->global->SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE)) {
1954  $selected = $user->id;
1955  }
1956 
1957  if ($selected === '') {
1958  $selected = array();
1959  } elseif (!is_array($selected)) {
1960  $selected = array($selected);
1961  }
1962 
1963  $excludeUsers = null;
1964  $includeUsers = null;
1965 
1966  // Permettre l'exclusion d'utilisateurs
1967  if (is_array($exclude)) {
1968  $excludeUsers = implode(",", $exclude);
1969  }
1970  // Permettre l'inclusion d'utilisateurs
1971  if (is_array($include)) {
1972  $includeUsers = implode(",", $include);
1973  } elseif ($include == 'hierarchy') {
1974  // Build list includeUsers to have only hierarchy
1975  $includeUsers = implode(",", $user->getAllChildIds(0));
1976  } elseif ($include == 'hierarchyme') {
1977  // Build list includeUsers to have only hierarchy and current user
1978  $includeUsers = implode(",", $user->getAllChildIds(1));
1979  }
1980 
1981  $out = '';
1982  $outarray = array();
1983 
1984  // Forge request to select users
1985  $sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.photo";
1986  if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
1987  $sql .= ", e.label";
1988  }
1989  $sql .= " FROM ".$this->db->prefix()."user as u";
1990  if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
1991  $sql .= " LEFT JOIN ".$this->db->prefix()."entity as e ON e.rowid = u.entity";
1992  if ($force_entity) {
1993  $sql .= " WHERE u.entity IN (0, ".$this->db->sanitize($force_entity).")";
1994  } else {
1995  $sql .= " WHERE u.entity IS NOT NULL";
1996  }
1997  } else {
1998  if (isModEnabled('multicompany') && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1999  $sql .= " LEFT JOIN ".$this->db->prefix()."usergroup_user as ug";
2000  $sql .= " ON ug.fk_user = u.rowid";
2001  $sql .= " WHERE ug.entity = ".$conf->entity;
2002  } else {
2003  $sql .= " WHERE u.entity IN (0, ".$conf->entity.")";
2004  }
2005  }
2006  if (!empty($user->socid)) {
2007  $sql .= " AND u.fk_soc = ".((int) $user->socid);
2008  }
2009  if (is_array($exclude) && $excludeUsers) {
2010  $sql .= " AND u.rowid NOT IN (".$this->db->sanitize($excludeUsers).")";
2011  }
2012  if ($includeUsers) {
2013  $sql .= " AND u.rowid IN (".$this->db->sanitize($includeUsers).")";
2014  }
2015  if (!empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX) || $notdisabled) {
2016  $sql .= " AND u.statut <> 0";
2017  }
2018  if (!empty($morefilter)) {
2019  $sql .= " ".$morefilter;
2020  }
2021 
2022  //Add hook to filter on user (for exemple on usergroup define in custom modules)
2023  $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
2024  if (!empty($reshook)) {
2025  $sql .= $hookmanager->resPrint;
2026  }
2027 
2028  if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) { // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
2029  $sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
2030  } else {
2031  $sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
2032  }
2033 
2034  dol_syslog(get_class($this)."::select_dolusers", LOG_DEBUG);
2035 
2036  $resql = $this->db->query($sql);
2037  if ($resql) {
2038  $num = $this->db->num_rows($resql);
2039  $i = 0;
2040  if ($num) {
2041  // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
2042  $out .= '<select class="flat'.($morecss ? ' '.$morecss : ' minwidth200').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
2043  if ($show_empty && !$multiple) {
2044  $textforempty = ' ';
2045  if (!empty($conf->use_javascript_ajax)) {
2046  $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
2047  }
2048  if (!is_numeric($show_empty)) {
2049  $textforempty = $show_empty;
2050  }
2051  $out .= '<option class="optiongrey" value="'.($show_empty < 0 ? $show_empty : -1).'"'.((empty($selected) || in_array(-1, $selected)) ? ' selected' : '').'>'.$textforempty.'</option>'."\n";
2052  }
2053  if ($show_every) {
2054  $out .= '<option value="-2"'.((in_array(-2, $selected)) ? ' selected' : '').'>-- '.$langs->trans("Everybody").' --</option>'."\n";
2055  }
2056 
2057  $userstatic = new User($this->db);
2058 
2059  while ($i < $num) {
2060  $obj = $this->db->fetch_object($resql);
2061 
2062  $userstatic->id = $obj->rowid;
2063  $userstatic->lastname = $obj->lastname;
2064  $userstatic->firstname = $obj->firstname;
2065  $userstatic->photo = $obj->photo;
2066  $userstatic->statut = $obj->status;
2067  $userstatic->entity = $obj->entity;
2068  $userstatic->admin = $obj->admin;
2069 
2070  $disableline = '';
2071  if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
2072  $disableline = ($enableonlytext ? $enableonlytext : '1');
2073  }
2074 
2075  $labeltoshow = ''; $labeltoshowhtml = '';
2076 
2077  // $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2078  $fullNameMode = 0;
2079  if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) {
2080  $fullNameMode = 1; //Firstname+lastname
2081  }
2082  $labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2083  $labeltoshowhtml .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2084  if (empty($obj->firstname) && empty($obj->lastname)) {
2085  $labeltoshow .= $obj->login;
2086  $labeltoshowhtml .= $obj->login;
2087  }
2088 
2089  // Complete name with a more info string like: ' (info1 - info2 - ...)'
2090  $moreinfo = ''; $moreinfohtml = '';
2091  if (!empty($conf->global->MAIN_SHOW_LOGIN)) {
2092  $moreinfo .= ($moreinfo ? ' - ' : ' (');
2093  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(');
2094  $moreinfo .= $obj->login;
2095  $moreinfohtml .= $obj->login;
2096  }
2097  if ($showstatus >= 0) {
2098  if ($obj->status == 1 && $showstatus == 1) {
2099  $moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Enabled');
2100  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').$langs->trans('Enabled');
2101  }
2102  if ($obj->status == 0 && $showstatus == 1) {
2103  $moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Disabled');
2104  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').$langs->trans('Disabled');
2105  }
2106  }
2107  if (isModEnabled('multicompany') && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1 && $user->admin && !$user->entity) {
2108  if (!$obj->entity) {
2109  $moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans("AllEntities");
2110  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').$langs->trans("AllEntities");
2111  } else {
2112  if ($obj->entity != $conf->entity) {
2113  $moreinfo .= ($moreinfo ? ' - ' : ' (').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2114  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2115  }
2116  }
2117  }
2118  $moreinfo .= ($moreinfo ? ')' : '');
2119  $moreinfohtml .= ($moreinfohtml ? ')' : '');
2120  if ($disableline && $disableline != '1') {
2121  // Add text from $enableonlytext parameter
2122  $moreinfo .= ' - '.$disableline;
2123  $moreinfohtml .= ' - '.$disableline;
2124  }
2125  $labeltoshow .= $moreinfo;
2126  $labeltoshowhtml .= $moreinfohtml;
2127 
2128  $out .= '<option value="'.$obj->rowid.'"';
2129  if ($disableline) {
2130  $out .= ' disabled';
2131  }
2132  if ((is_object($selected) && $selected->id == $obj->rowid) || (!is_object($selected) && in_array($obj->rowid, $selected))) {
2133  $out .= ' selected';
2134  }
2135  $out .= ' data-html="';
2136  $outhtml = '';
2137  // if (!empty($obj->photo)) {
2138  $outhtml .= $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1).' ';
2139  // }
2140  if ($showstatus >= 0 && $obj->status == 0) {
2141  $outhtml .= '<strike class="opacitymediumxxx">';
2142  }
2143  $outhtml .= $labeltoshowhtml;
2144  if ($showstatus >= 0 && $obj->status == 0) {
2145  $outhtml .= '</strike>';
2146  }
2147  $out .= dol_escape_htmltag($outhtml);
2148  $out .= '">';
2149  $out .= $labeltoshow;
2150  $out .= '</option>';
2151 
2152  $outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength).$moreinfo;
2153 
2154  $i++;
2155  }
2156  } else {
2157  $out .= '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'" disabled>';
2158  $out .= '<option value="">'.$langs->trans("None").'</option>';
2159  }
2160  $out .= '</select>';
2161 
2162  if ($num && !$forcecombo) {
2163  // Enhance with select2
2164  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2165  $out .= ajax_combobox($htmlname);
2166  }
2167  } else {
2168  dol_print_error($this->db);
2169  }
2170 
2171  if ($outputmode) {
2172  return $outarray;
2173  }
2174 
2175  return $out;
2176  }
2177 
2178 
2179  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2202  public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array())
2203  {
2204  // phpcs:enable
2205  global $conf, $user, $langs;
2206 
2207  $userstatic = new User($this->db);
2208  $out = '';
2209 
2210 
2211  $assignedtouser = array();
2212  if (!empty($_SESSION['assignedtouser'])) {
2213  $assignedtouser = json_decode($_SESSION['assignedtouser'], true);
2214  }
2215  $nbassignetouser = count($assignedtouser);
2216 
2217  //if ($nbassignetouser && $action != 'view') $out .= '<br>';
2218  if ($nbassignetouser) {
2219  $out .= '<ul class="attendees">';
2220  }
2221  $i = 0;
2222  $ownerid = 0;
2223  foreach ($assignedtouser as $key => $value) {
2224  if ($value['id'] == $ownerid) {
2225  continue;
2226  }
2227 
2228  $out .= '<li>';
2229  $userstatic->fetch($value['id']);
2230  $out .= $userstatic->getNomUrl(-1);
2231  if ($i == 0) {
2232  $ownerid = $value['id'];
2233  $out .= ' ('.$langs->trans("Owner").')';
2234  }
2235  if ($nbassignetouser > 1 && $action != 'view') {
2236  $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.'">';
2237  }
2238  // Show my availability
2239  if ($showproperties) {
2240  if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
2241  $out .= '<div class="myavailability inline-block">';
2242  $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>';
2243  $out .= '</div>';
2244  }
2245  }
2246  //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
2247  //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
2248 
2249  $out .= '</li>';
2250  $i++;
2251  }
2252  if ($nbassignetouser) {
2253  $out .= '</ul>';
2254  }
2255 
2256  // Method with no ajax
2257  if ($action != 'view') {
2258  $out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
2259  $out .= '<script type="text/javascript">jQuery(document).ready(function () {';
2260  $out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
2261  $out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
2262  $out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#'.$action.'assignedtouser").attr("disabled", false); }';
2263  $out .= ' else { jQuery("#'.$action.'assignedtouser").attr("disabled", true); }';
2264  $out .= '});';
2265  $out .= '})</script>';
2266  $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
2267  $out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp reposition" id="'.$action.'assignedtouser" name="'.$action.'assignedtouser" value="'.dol_escape_htmltag($langs->trans("Add")).'">';
2268  $out .= '<br>';
2269  }
2270 
2271  return $out;
2272  }
2273 
2274 
2275  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2303  public function select_produits($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 0, $price_level = 0, $status = 1, $finished = 2, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '', $selected_combinations = null, $nooutput = 0, $status_purchase = -1)
2304  {
2305  // phpcs:enable
2306  global $langs, $conf;
2307 
2308  $out = '';
2309 
2310  // check parameters
2311  $price_level = (!empty($price_level) ? $price_level : 0);
2312  if (is_null($ajaxoptions)) {
2313  $ajaxoptions = array();
2314  }
2315 
2316  if (strval($filtertype) === '' && (isModEnabled("product") || isModEnabled("service"))) {
2317  if (isModEnabled("product") && !isModEnabled('service')) {
2318  $filtertype = '0';
2319  } elseif (!isModEnabled('product') && isModEnabled("service")) {
2320  $filtertype = '1';
2321  }
2322  }
2323 
2324  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2325  $placeholder = '';
2326 
2327  if ($selected && empty($selected_input_value)) {
2328  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2329  $producttmpselect = new Product($this->db);
2330  $producttmpselect->fetch($selected);
2331  $selected_input_value = $producttmpselect->ref;
2332  unset($producttmpselect);
2333  }
2334  // handle case where product or service module is disabled + no filter specified
2335  if ($filtertype == '') {
2336  if (!isModEnabled('product')) { // when product module is disabled, show services only
2337  $filtertype = 1;
2338  } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
2339  $filtertype = 0;
2340  }
2341  }
2342  // mode=1 means customers products
2343  $urloption = 'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=1&status='.$status.'&status_purchase='.$status_purchase.'&finished='.$finished.'&hidepriceinlabel='.$hidepriceinlabel.'&warehousestatus='.$warehouseStatus;
2344  //Price by customer
2345  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2346  $urloption .= '&socid='.$socid;
2347  }
2348  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
2349 
2350  if (isModEnabled('variants') && is_array($selected_combinations)) {
2351  // Code to automatically insert with javascript the select of attributes under the select of product
2352  // when a parent of variant has been selected.
2353  $out .= '
2354  <!-- script to auto show attributes select tags if a variant was selected -->
2355  <script>
2356  // auto show attributes fields
2357  selected = '.json_encode($selected_combinations).';
2358  combvalues = {};
2359 
2360  jQuery(document).ready(function () {
2361 
2362  jQuery("input[name=\'prod_entry_mode\']").change(function () {
2363  if (jQuery(this).val() == \'free\') {
2364  jQuery(\'div#attributes_box\').empty();
2365  }
2366  });
2367 
2368  jQuery("input#'.$htmlname.'").change(function () {
2369 
2370  if (!jQuery(this).val()) {
2371  jQuery(\'div#attributes_box\').empty();
2372  return;
2373  }
2374 
2375  console.log("A change has started. We get variants fields to inject html select");
2376 
2377  jQuery.getJSON("'.DOL_URL_ROOT.'/variants/ajax/getCombinations.php", {
2378  id: jQuery(this).val()
2379  }, function (data) {
2380  jQuery(\'div#attributes_box\').empty();
2381 
2382  jQuery.each(data, function (key, val) {
2383 
2384  combvalues[val.id] = val.values;
2385 
2386  var span = jQuery(document.createElement(\'div\')).css({
2387  \'display\': \'table-row\'
2388  });
2389 
2390  span.append(
2391  jQuery(document.createElement(\'div\')).text(val.label).css({
2392  \'font-weight\': \'bold\',
2393  \'display\': \'table-cell\'
2394  })
2395  );
2396 
2397  var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
2398  \'margin-left\': \'15px\',
2399  \'white-space\': \'pre\'
2400  }).append(
2401  jQuery(document.createElement(\'option\')).val(\'\')
2402  );
2403 
2404  jQuery.each(combvalues[val.id], function (key, val) {
2405  var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
2406 
2407  if (selected[val.fk_product_attribute] == val.id) {
2408  tag.attr(\'selected\', \'selected\');
2409  }
2410 
2411  html.append(tag);
2412  });
2413 
2414  span.append(html);
2415  jQuery(\'div#attributes_box\').append(span);
2416  });
2417  })
2418  });
2419 
2420  '.($selected ? 'jQuery("input#'.$htmlname.'").change();' : '').'
2421  });
2422  </script>
2423  ';
2424  }
2425 
2426  if (empty($hidelabel)) {
2427  $out .= $langs->trans("RefOrLabel").' : ';
2428  } elseif ($hidelabel > 1) {
2429  $placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
2430  if ($hidelabel == 2) {
2431  $out .= img_picto($langs->trans("Search"), 'search');
2432  }
2433  }
2434  $out .= '<input type="text" class="minwidth100'.($morecss ? ' '.$morecss : '').'" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->PRODUCT_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
2435  if ($hidelabel == 3) {
2436  $out .= img_picto($langs->trans("Search"), 'search');
2437  }
2438  } else {
2439  $out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus, $status_purchase);
2440  }
2441 
2442  if (empty($nooutput)) {
2443  print $out;
2444  } else {
2445  return $out;
2446  }
2447  }
2448 
2449  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2450 
2466  public function select_bom($selected = '', $htmlname = 'bom_id', $limit = 0, $status = 1, $type = 0, $showempty = '1', $morecss = '', $nooutput = '', $forcecombo = 0, $TProducts = [])
2467  {
2468  // phpcs:enable
2469  global $conf, $user, $langs, $db;
2470 
2471  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2472 
2473  $error = 0;
2474  $out = '';
2475 
2476  if (!$forcecombo) {
2477  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2478  $events = array();
2479  $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
2480  }
2481 
2482  $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
2483 
2484  $sql = 'SELECT b.rowid, b.ref, b.label, b.fk_product';
2485  $sql.= ' FROM '.MAIN_DB_PREFIX.'bom_bom as b';
2486  $sql.= ' WHERE b.entity IN ('.getEntity('bom').')';
2487  if (!empty($status)) $sql.= ' AND status = '. (int) $status;
2488  if (!empty($type)) $sql.= ' AND bomtype = '. (int) $type;
2489  if (!empty($TProducts)) $sql .= ' AND fk_product IN ('.$this->db->sanitize(implode(',', $TProducts)).')';
2490  if (!empty($limit)) $sql.= ' LIMIT '. (int) $limit;
2491  $resql = $db->query($sql);
2492  if ($resql) {
2493  if ($showempty) {
2494  $out .= '<option value="-1"';
2495  if (empty($selected)) $out .= ' selected';
2496  $out .= '>&nbsp;</option>';
2497  }
2498  while ($obj = $db->fetch_object($resql)) {
2499  $product = new Product($db);
2500  $res = $product->fetch($obj->fk_product);
2501  $out .= '<option value="'.$obj->rowid.'"';
2502  if ($obj->rowid == $selected) $out .= 'selected';
2503  $out .= '>'.$obj->ref.' - '.$product->label .' - '. $obj->label.'</option>';
2504  }
2505  } else {
2506  $error++;
2507  dol_print_error($db);
2508  }
2509  $out .= '</select>';
2510  if (empty($nooutput)) {
2511  print $out;
2512  } else {
2513  return $out;
2514  }
2515  }
2516 
2517  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2543  public function select_produits_list($selected = '', $htmlname = 'productid', $filtertype = '', $limit = 20, $price_level = 0, $filterkey = '', $status = 1, $finished = 2, $outputmode = 0, $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '', $status_purchase = -1)
2544  {
2545  // phpcs:enable
2546  global $langs, $conf;
2547  global $hookmanager;
2548 
2549  $out = '';
2550  $outarray = array();
2551 
2552  // Units
2553  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2554  $langs->load('other');
2555  }
2556 
2557  $warehouseStatusArray = array();
2558  if (!empty($warehouseStatus)) {
2559  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
2560  if (preg_match('/warehouseclosed/', $warehouseStatus)) {
2561  $warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
2562  }
2563  if (preg_match('/warehouseopen/', $warehouseStatus)) {
2564  $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
2565  }
2566  if (preg_match('/warehouseinternal/', $warehouseStatus)) {
2567  $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
2568  }
2569  }
2570 
2571  $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";
2572  if (count($warehouseStatusArray)) {
2573  $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
2574  } else {
2575  $selectFieldsGrouped = ", ".$this->db->ifsql("p.stock IS NULL", 0, "p.stock")." AS stock";
2576  }
2577 
2578  $sql = "SELECT ";
2579  $sql .= $selectFields.$selectFieldsGrouped;
2580 
2581  if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2582  //Product category
2583  $sql .= ", (SELECT ".$this->db->prefix()."categorie_product.fk_categorie
2584  FROM ".$this->db->prefix()."categorie_product
2585  WHERE ".$this->db->prefix()."categorie_product.fk_product=p.rowid
2586  LIMIT 1
2587  ) AS categorie_product_id ";
2588  }
2589 
2590  //Price by customer
2591  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2592  $sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
2593  $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';
2594  $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custdefault_vat_code, custref";
2595  }
2596  // Units
2597  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2598  $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";
2599  $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';
2600  }
2601 
2602  // Multilang : we add translation
2603  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2604  $sql .= ", pl.label as label_translated";
2605  $sql .= ", pl.description as description_translated";
2606  $selectFields .= ", label_translated";
2607  $selectFields .= ", description_translated";
2608  }
2609  // Price by quantity
2610  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2611  $sql .= ", (SELECT pp.rowid FROM ".$this->db->prefix()."product_price as pp WHERE pp.fk_product = p.rowid";
2612  if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2613  $sql .= " AND price_level = ".((int) $price_level);
2614  }
2615  $sql .= " ORDER BY date_price";
2616  $sql .= " DESC LIMIT 1) as price_rowid";
2617  $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
2618  if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2619  $sql .= " AND price_level = ".((int) $price_level);
2620  }
2621  $sql .= " ORDER BY date_price";
2622  $sql .= " DESC LIMIT 1) as price_by_qty";
2623  $selectFields .= ", price_rowid, price_by_qty";
2624  }
2625  $sql .= " FROM ".$this->db->prefix()."product as p";
2626  if (count($warehouseStatusArray)) {
2627  $sql .= " LEFT JOIN ".$this->db->prefix()."product_stock as ps on ps.fk_product = p.rowid";
2628  $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (".getEntity('stock').")";
2629  $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.
2630  }
2631 
2632  // include search in supplier ref
2633  if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2634  $sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
2635  }
2636 
2637  //Price by customer
2638  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2639  $sql .= " LEFT JOIN ".$this->db->prefix()."product_customer_price as pcp ON pcp.fk_soc=".((int) $socid)." AND pcp.fk_product=p.rowid";
2640  }
2641  // Units
2642  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2643  $sql .= " LEFT JOIN ".$this->db->prefix()."c_units u ON u.rowid = p.fk_unit";
2644  }
2645  // Multilang : we add translation
2646  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2647  $sql .= " LEFT JOIN ".$this->db->prefix()."product_lang as pl ON pl.fk_product = p.rowid ";
2648  if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && !empty($socid)) {
2649  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
2650  $soc = new Societe($this->db);
2651  $result = $soc->fetch($socid);
2652  if ($result > 0 && !empty($soc->default_lang)) {
2653  $sql .= " AND pl.lang = '".$this->db->escape($soc->default_lang)."'";
2654  } else {
2655  $sql .= " AND pl.lang = '".$this->db->escape($langs->getDefaultLang())."'";
2656  }
2657  } else {
2658  $sql .= " AND pl.lang = '".$this->db->escape($langs->getDefaultLang())."'";
2659  }
2660  }
2661 
2662  if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2663  $sql .= " LEFT JOIN ".$this->db->prefix()."product_attribute_combination pac ON pac.fk_product_child = p.rowid";
2664  }
2665 
2666  $sql .= ' WHERE p.entity IN ('.getEntity('product').')';
2667 
2668  if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2669  $sql .= " AND pac.rowid IS NULL";
2670  }
2671 
2672  if ($finished == 0) {
2673  $sql .= " AND p.finished = ".((int) $finished);
2674  } elseif ($finished == 1) {
2675  $sql .= " AND p.finished = ".((int) $finished);
2676  }
2677  if ($status >= 0) {
2678  $sql .= " AND p.tosell = ".((int) $status);
2679  }
2680  if ($status_purchase >= 0) {
2681  $sql .= " AND p.tobuy = ".((int) $status_purchase);
2682  }
2683  // Filter by product type
2684  if (strval($filtertype) != '') {
2685  $sql .= " AND p.fk_product_type = ".((int) $filtertype);
2686  } elseif (!isModEnabled('product')) { // when product module is disabled, show services only
2687  $sql .= " AND p.fk_product_type = 1";
2688  } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
2689  $sql .= " AND p.fk_product_type = 0";
2690  }
2691  // Add where from hooks
2692  $parameters = array();
2693  $reshook = $hookmanager->executeHooks('selectProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
2694  $sql .= $hookmanager->resPrint;
2695  // Add criteria on ref/label
2696  if ($filterkey != '') {
2697  $sql .= ' AND (';
2698  $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
2699  // For natural search
2700  $scrit = explode(' ', $filterkey);
2701  $i = 0;
2702  if (count($scrit) > 1) {
2703  $sql .= "(";
2704  }
2705  foreach ($scrit as $crit) {
2706  if ($i > 0) {
2707  $sql .= " AND ";
2708  }
2709  $sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2710  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2711  $sql .= " OR pl.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2712  }
2713  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2714  $sql .= " OR pcp.ref_customer LIKE '".$this->db->escape($prefix.$crit)."%'";
2715  }
2716  if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION)) {
2717  $sql .= " OR p.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2718  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2719  $sql .= " OR pl.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2720  }
2721  }
2722  if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2723  $sql .= " OR pfp.ref_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
2724  }
2725  $sql .= ")";
2726  $i++;
2727  }
2728  if (count($scrit) > 1) {
2729  $sql .= ")";
2730  }
2731  if (isModEnabled('barcode')) {
2732  $sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
2733  }
2734  $sql .= ')';
2735  }
2736  if (count($warehouseStatusArray)) {
2737  $sql .= " GROUP BY ".$selectFields;
2738  }
2739 
2740  //Sort by category
2741  if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2742  $sql .= " ORDER BY categorie_product_id ";
2743  //ASC OR DESC order
2744  ($conf->global->PRODUCT_SORT_BY_CATEGORY == 1) ? $sql .= "ASC" : $sql .= "DESC";
2745  } else {
2746  $sql .= $this->db->order("p.ref");
2747  }
2748 
2749  $sql .= $this->db->plimit($limit, 0);
2750 
2751  // Build output string
2752  dol_syslog(get_class($this)."::select_produits_list search products", LOG_DEBUG);
2753  $result = $this->db->query($sql);
2754  if ($result) {
2755  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2756  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
2757  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
2758 
2759  $num = $this->db->num_rows($result);
2760 
2761  $events = null;
2762 
2763  if (!$forcecombo) {
2764  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2765  $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
2766  }
2767 
2768  $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
2769 
2770  $textifempty = '';
2771  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2772  //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2773  if (!empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2774  if ($showempty && !is_numeric($showempty)) {
2775  $textifempty = $langs->trans($showempty);
2776  } else {
2777  $textifempty .= $langs->trans("All");
2778  }
2779  } else {
2780  if ($showempty && !is_numeric($showempty)) {
2781  $textifempty = $langs->trans($showempty);
2782  }
2783  }
2784  if ($showempty) {
2785  $out .= '<option value="-1" selected>'.($textifempty ? $textifempty : '&nbsp;').'</option>';
2786  }
2787 
2788  $i = 0;
2789  while ($num && $i < $num) {
2790  $opt = '';
2791  $optJson = array();
2792  $objp = $this->db->fetch_object($result);
2793 
2794  if ((!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) && !empty($objp->price_by_qty) && $objp->price_by_qty == 1) { // Price by quantity will return many prices for the same product
2795  $sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
2796  $sql .= " FROM ".$this->db->prefix()."product_price_by_qty";
2797  $sql .= " WHERE fk_product_price = ".((int) $objp->price_rowid);
2798  $sql .= " ORDER BY quantity ASC";
2799 
2800  dol_syslog(get_class($this)."::select_produits_list search prices by qty", LOG_DEBUG);
2801  $result2 = $this->db->query($sql);
2802  if ($result2) {
2803  $nb_prices = $this->db->num_rows($result2);
2804  $j = 0;
2805  while ($nb_prices && $j < $nb_prices) {
2806  $objp2 = $this->db->fetch_object($result2);
2807 
2808  $objp->price_by_qty_rowid = $objp2->rowid;
2809  $objp->price_by_qty_price_base_type = $objp2->price_base_type;
2810  $objp->price_by_qty_quantity = $objp2->quantity;
2811  $objp->price_by_qty_unitprice = $objp2->unitprice;
2812  $objp->price_by_qty_remise_percent = $objp2->remise_percent;
2813  // For backward compatibility
2814  $objp->quantity = $objp2->quantity;
2815  $objp->price = $objp2->price;
2816  $objp->unitprice = $objp2->unitprice;
2817  $objp->remise_percent = $objp2->remise_percent;
2818 
2819  //$objp->tva_tx is not overwritten by $objp2 value
2820  //$objp->default_vat_code is not overwritten by $objp2 value
2821 
2822  $this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
2823 
2824  $j++;
2825 
2826  // Add new entry
2827  // "key" value of json key array is used by jQuery automatically as selected value
2828  // "label" value of json key array is used by jQuery automatically as text for combo box
2829  $out .= $opt;
2830  array_push($outarray, $optJson);
2831  }
2832  }
2833  } else {
2834  if (isModEnabled('dynamicprices') && !empty($objp->fk_price_expression)) {
2835  $price_product = new Product($this->db);
2836  $price_product->fetch($objp->rowid, '', '', 1);
2837 
2838  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
2839  $priceparser = new PriceParser($this->db);
2840  $price_result = $priceparser->parseProduct($price_product);
2841  if ($price_result >= 0) {
2842  $objp->price = $price_result;
2843  $objp->unitprice = $price_result;
2844  //Calculate the VAT
2845  $objp->price_ttc = price2num($objp->price) * (1 + ($objp->tva_tx / 100));
2846  $objp->price_ttc = price2num($objp->price_ttc, 'MU');
2847  }
2848  }
2849 
2850  $this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
2851  // Add new entry
2852  // "key" value of json key array is used by jQuery automatically as selected value
2853  // "label" value of json key array is used by jQuery automatically as text for combo box
2854  $out .= $opt;
2855  array_push($outarray, $optJson);
2856  }
2857 
2858  $i++;
2859  }
2860 
2861  $out .= '</select>';
2862 
2863  $this->db->free($result);
2864 
2865  if (empty($outputmode)) {
2866  return $out;
2867  }
2868  return $outarray;
2869  } else {
2870  dol_print_error($this->db);
2871  }
2872  }
2873 
2889  protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
2890  {
2891  global $langs, $conf, $user;
2892 
2893  $outkey = '';
2894  $outval = '';
2895  $outref = '';
2896  $outlabel = '';
2897  $outlabel_translated = '';
2898  $outdesc = '';
2899  $outdesc_translated = '';
2900  $outbarcode = '';
2901  $outorigin = '';
2902  $outtype = '';
2903  $outprice_ht = '';
2904  $outprice_ttc = '';
2905  $outpricebasetype = '';
2906  $outtva_tx = '';
2907  $outdefault_vat_code = '';
2908  $outqty = 1;
2909  $outdiscount = 0;
2910 
2911  $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
2912 
2913  $label = $objp->label;
2914  if (!empty($objp->label_translated)) {
2915  $label = $objp->label_translated;
2916  }
2917  if (!empty($filterkey) && $filterkey != '') {
2918  $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
2919  }
2920 
2921  $outkey = $objp->rowid;
2922  $outref = $objp->ref;
2923  $outrefcust = empty($objp->custref) ? '' : $objp->custref;
2924  $outlabel = $objp->label;
2925  $outdesc = $objp->description;
2926  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2927  $outlabel_translated = $objp->label_translated;
2928  $outdesc_translated = $objp->description_translated;
2929  }
2930  $outbarcode = $objp->barcode;
2931  $outorigin = $objp->fk_country;
2932  $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
2933 
2934  $outtype = $objp->fk_product_type;
2935  $outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
2936  $outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
2937 
2938  if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2939  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
2940  }
2941 
2942  // Units
2943  $outvalUnits = '';
2944  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2945  if (!empty($objp->unit_short)) {
2946  $outvalUnits .= ' - '.$objp->unit_short;
2947  }
2948  }
2949  if (!empty($conf->global->PRODUCT_SHOW_DIMENSIONS_IN_COMBO)) {
2950  if (!empty($objp->weight) && $objp->weight_units !== null) {
2951  $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
2952  $outvalUnits .= ' - '.$unitToShow;
2953  }
2954  if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
2955  $unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
2956  $outvalUnits .= ' - '.$unitToShow;
2957  }
2958  if (!empty($objp->surface) && $objp->surface_units !== null) {
2959  $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
2960  $outvalUnits .= ' - '.$unitToShow;
2961  }
2962  if (!empty($objp->volume) && $objp->volume_units !== null) {
2963  $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
2964  $outvalUnits .= ' - '.$unitToShow;
2965  }
2966  }
2967  if ($outdurationvalue && $outdurationunit) {
2968  $da = array(
2969  'h' => $langs->trans('Hour'),
2970  'd' => $langs->trans('Day'),
2971  'w' => $langs->trans('Week'),
2972  'm' => $langs->trans('Month'),
2973  'y' => $langs->trans('Year')
2974  );
2975  if (isset($da[$outdurationunit])) {
2976  $outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
2977  }
2978  }
2979 
2980  $opt = '<option value="'.$objp->rowid.'"';
2981  $opt .= ($objp->rowid == $selected) ? ' selected' : '';
2982  if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
2983  $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.'"';
2984  }
2985  if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
2986  if (!empty($user->rights->stock->lire)) {
2987  if ($objp->stock > 0) {
2988  $opt .= ' class="product_line_stock_ok"';
2989  } elseif ($objp->stock <= 0) {
2990  $opt .= ' class="product_line_stock_too_low"';
2991  }
2992  }
2993  }
2994  if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) {
2995  $opt .= ' data-labeltrans="'.$outlabel_translated.'"';
2996  $opt .= ' data-desctrans="'.dol_escape_htmltag($outdesc_translated).'"';
2997  }
2998  $opt .= '>';
2999  $opt .= $objp->ref;
3000  if (!empty($objp->custref)) {
3001  $opt.= ' (' . $objp->custref . ')';
3002  }
3003  if ($outbarcode) {
3004  $opt .= ' ('.$outbarcode.')';
3005  }
3006  $opt .= ' - '.dol_trunc($label, $maxlengtharticle);
3007  if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
3008  $opt .= ' ('.getCountry($outorigin, 1).')';
3009  }
3010 
3011  $objRef = $objp->ref;
3012  if (!empty($objp->custref)) {
3013  $objRef .= ' (' . $objp->custref . ')';
3014  }
3015  if (!empty($filterkey) && $filterkey != '') {
3016  $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
3017  }
3018  $outval .= $objRef;
3019  if ($outbarcode) {
3020  $outval .= ' ('.$outbarcode.')';
3021  }
3022  $outval .= ' - '.dol_trunc($label, $maxlengtharticle);
3023  if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
3024  $outval .= ' ('.getCountry($outorigin, 1).')';
3025  }
3026 
3027  // Units
3028  $opt .= $outvalUnits;
3029  $outval .= $outvalUnits;
3030 
3031  $found = 0;
3032 
3033  // Multiprice
3034  // If we need a particular price level (from 1 to n)
3035  if (empty($hidepriceinlabel) && $price_level >= 1 && (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) {
3036  $sql = "SELECT price, price_ttc, price_base_type, tva_tx, default_vat_code";
3037  $sql .= " FROM ".$this->db->prefix()."product_price";
3038  $sql .= " WHERE fk_product = ".((int) $objp->rowid);
3039  $sql .= " AND entity IN (".getEntity('productprice').")";
3040  $sql .= " AND price_level = ".((int) $price_level);
3041  $sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
3042  $sql .= " LIMIT 1";
3043 
3044  dol_syslog(get_class($this).'::constructProductListOption search price for product '.$objp->rowid.' AND level '.$price_level.'', LOG_DEBUG);
3045  $result2 = $this->db->query($sql);
3046  if ($result2) {
3047  $objp2 = $this->db->fetch_object($result2);
3048  if ($objp2) {
3049  $found = 1;
3050  if ($objp2->price_base_type == 'HT') {
3051  $opt .= ' - '.price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
3052  $outval .= ' - '.price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
3053  } else {
3054  $opt .= ' - '.price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
3055  $outval .= ' - '.price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
3056  }
3057  $outprice_ht = price($objp2->price);
3058  $outprice_ttc = price($objp2->price_ttc);
3059  $outpricebasetype = $objp2->price_base_type;
3060  if (!empty($conf->global->PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL)) { // using this option is a bug. kept for backward compatibility
3061  $outtva_tx = $objp2->tva_tx; // We use the vat rate on line of multiprice
3062  $outdefault_vat_code = $objp2->default_vat_code; // We use the vat code on line of multiprice
3063  } else {
3064  $outtva_tx = $objp->tva_tx; // We use the vat rate of product, not the one on line of multiprice
3065  $outdefault_vat_code = $objp->default_vat_code; // We use the vat code or product, not the one on line of multiprice
3066  }
3067  }
3068  } else {
3069  dol_print_error($this->db);
3070  }
3071  }
3072 
3073  // Price by quantity
3074  if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1 && (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) {
3075  $found = 1;
3076  $outqty = $objp->quantity;
3077  $outdiscount = $objp->remise_percent;
3078  if ($objp->quantity == 1) {
3079  $opt .= ' - '.price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/";
3080  $outval .= ' - '.price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/";
3081  $opt .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3082  $outval .= $langs->transnoentities("Unit");
3083  } else {
3084  $opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3085  $outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3086  $opt .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3087  $outval .= $langs->transnoentities("Units");
3088  }
3089 
3090  $outprice_ht = price($objp->unitprice);
3091  $outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
3092  $outpricebasetype = $objp->price_base_type;
3093  $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
3094  $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
3095  }
3096  if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
3097  $opt .= " (".price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
3098  $outval .= " (".price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
3099  }
3100  if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
3101  $opt .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
3102  $outval .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
3103  }
3104 
3105  // Price by customer
3106  if (empty($hidepriceinlabel) && !empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
3107  if (!empty($objp->idprodcustprice)) {
3108  $found = 1;
3109 
3110  if ($objp->custprice_base_type == 'HT') {
3111  $opt .= ' - '.price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
3112  $outval .= ' - '.price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
3113  } else {
3114  $opt .= ' - '.price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
3115  $outval .= ' - '.price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
3116  }
3117 
3118  $outprice_ht = price($objp->custprice);
3119  $outprice_ttc = price($objp->custprice_ttc);
3120  $outpricebasetype = $objp->custprice_base_type;
3121  $outtva_tx = $objp->custtva_tx;
3122  $outdefault_vat_code = $objp->custdefault_vat_code;
3123  }
3124  }
3125 
3126  // If level no defined or multiprice not found, we used the default price
3127  if (empty($hidepriceinlabel) && !$found) {
3128  if ($objp->price_base_type == 'HT') {
3129  $opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
3130  $outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
3131  } else {
3132  $opt .= ' - '.price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
3133  $outval .= ' - '.price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
3134  }
3135  $outprice_ht = price($objp->price);
3136  $outprice_ttc = price($objp->price_ttc);
3137  $outpricebasetype = $objp->price_base_type;
3138  $outtva_tx = $objp->tva_tx;
3139  $outdefault_vat_code = $objp->default_vat_code;
3140  }
3141 
3142  if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3143  if (!empty($user->rights->stock->lire)) {
3144  $opt .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
3145 
3146  if ($objp->stock > 0) {
3147  $outval .= ' - <span class="product_line_stock_ok">';
3148  } elseif ($objp->stock <= 0) {
3149  $outval .= ' - <span class="product_line_stock_too_low">';
3150  }
3151  $outval .= $langs->transnoentities("Stock").': '.price(price2num($objp->stock, 'MS'));
3152  $outval .= '</span>';
3153  if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) { // Warning, this option may slow down combo list generation
3154  $langs->load("stocks");
3155 
3156  $tmpproduct = new Product($this->db);
3157  $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3158  $tmpproduct->load_virtual_stock();
3159  $virtualstock = $tmpproduct->stock_theorique;
3160 
3161  $opt .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
3162 
3163  $outval .= ' - '.$langs->transnoentities("VirtualStock").':';
3164  if ($virtualstock > 0) {
3165  $outval .= '<span class="product_line_stock_ok">';
3166  } elseif ($virtualstock <= 0) {
3167  $outval .= '<span class="product_line_stock_too_low">';
3168  }
3169  $outval .= $virtualstock;
3170  $outval .= '</span>';
3171 
3172  unset($tmpproduct);
3173  }
3174  }
3175  }
3176 
3177  $opt .= "</option>\n";
3178  $optJson = array(
3179  'key'=>$outkey,
3180  'value'=>$outref,
3181  'label'=>$outval,
3182  'label2'=>$outlabel,
3183  'desc'=>$outdesc,
3184  'type'=>$outtype,
3185  'price_ht'=>price2num($outprice_ht),
3186  'price_ttc'=>price2num($outprice_ttc),
3187  'price_ht_locale'=>price(price2num($outprice_ht)),
3188  'price_ttc_locale'=>price(price2num($outprice_ttc)),
3189  'pricebasetype'=>$outpricebasetype,
3190  'tva_tx'=>$outtva_tx,
3191  'default_vat_code'=>$outdefault_vat_code,
3192  'qty'=>$outqty,
3193  'discount'=>$outdiscount,
3194  'duration_value'=>$outdurationvalue,
3195  'duration_unit'=>$outdurationunit,
3196  'pbq'=>$outpbq,
3197  'labeltrans'=>$outlabel_translated,
3198  'desctrans'=>$outdesc_translated,
3199  'ref_customer'=>$outrefcust
3200  );
3201  }
3202 
3203  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3219  public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '')
3220  {
3221  // phpcs:enable
3222  global $langs, $conf;
3223  global $price_level, $status, $finished;
3224 
3225  if (!isset($status)) {
3226  $status = 1;
3227  }
3228 
3229  $selected_input_value = '';
3230  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
3231  if ($selected > 0) {
3232  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3233  $producttmpselect = new Product($this->db);
3234  $producttmpselect->fetch($selected);
3235  $selected_input_value = $producttmpselect->ref;
3236  unset($producttmpselect);
3237  }
3238 
3239  // mode=2 means suppliers products
3240  $urloption = ($socid > 0 ? 'socid='.$socid.'&' : '').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=2&status='.$status.'&finished='.$finished.'&alsoproductwithnosupplierprice='.$alsoproductwithnosupplierprice;
3241  print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
3242  print ($hidelabel ? '' : $langs->trans("RefOrLabel").' : ').'<input type="text" class="minwidth300" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.($placeholder ? ' placeholder="'.$placeholder.'"' : '').'>';
3243  } else {
3244  print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, 0, $placeholder);
3245  }
3246  }
3247 
3248  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3267  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 = '')
3268  {
3269  // phpcs:enable
3270  global $langs, $conf, $user;
3271  global $hookmanager;
3272 
3273  $out = '';
3274  $outarray = array();
3275 
3276  $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3277 
3278  $langs->load('stocks');
3279  // Units
3280  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3281  $langs->load('other');
3282  }
3283 
3284  $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,";
3285  $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,";
3286  $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.default_vat_code, pfp.fk_soc, s.nom as name,";
3287  $sql .= " pfp.supplier_reputation";
3288  // if we use supplier description of the products
3289  if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3290  $sql .= ", pfp.desc_fourn as description";
3291  } else {
3292  $sql .= ", p.description";
3293  }
3294  // Units
3295  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3296  $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";
3297  }
3298  if (isModEnabled('barcode')) {
3299  $sql .= ", pfp.barcode";
3300  }
3301  $sql .= " FROM ".$this->db->prefix()."product as p";
3302  $sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (".getEntity('product').") )";
3303  if ($socid > 0) {
3304  $sql .= " AND pfp.fk_soc = ".((int) $socid);
3305  }
3306  $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON pfp.fk_soc = s.rowid";
3307  // Units
3308  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3309  $sql .= " LEFT JOIN ".$this->db->prefix()."c_units u ON u.rowid = p.fk_unit";
3310  }
3311  $sql .= " WHERE p.entity IN (".getEntity('product').")";
3312  if ($statut != -1) {
3313  $sql .= " AND p.tobuy = ".((int) $statut);
3314  }
3315  if (strval($filtertype) != '') {
3316  $sql .= " AND p.fk_product_type = ".((int) $filtertype);
3317  }
3318  if (!empty($filtre)) {
3319  $sql .= " ".$filtre;
3320  }
3321  // Add where from hooks
3322  $parameters = array();
3323  $reshook = $hookmanager->executeHooks('selectSuppliersProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3324  $sql .= $hookmanager->resPrint;
3325  // Add criteria on ref/label
3326  if ($filterkey != '') {
3327  $sql .= ' AND (';
3328  $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3329  // For natural search
3330  $scrit = explode(' ', $filterkey);
3331  $i = 0;
3332  if (count($scrit) > 1) {
3333  $sql .= "(";
3334  }
3335  foreach ($scrit as $crit) {
3336  if ($i > 0) {
3337  $sql .= " AND ";
3338  }
3339  $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)."%'";
3340  if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3341  $sql .= " OR pfp.desc_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
3342  }
3343  $sql .= ")";
3344  $i++;
3345  }
3346  if (count($scrit) > 1) {
3347  $sql .= ")";
3348  }
3349  if (isModEnabled('barcode')) {
3350  $sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3351  $sql .= " OR pfp.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3352  }
3353  $sql .= ')';
3354  }
3355  $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
3356  $sql .= $this->db->plimit($limit, 0);
3357 
3358  // Build output string
3359 
3360  dol_syslog(get_class($this)."::select_produits_fournisseurs_list", LOG_DEBUG);
3361  $result = $this->db->query($sql);
3362  if ($result) {
3363  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3364  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
3365 
3366  $num = $this->db->num_rows($result);
3367 
3368  //$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">'; // remove select to have id same with combo and ajax
3369  $out .= '<select class="flat '.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.'">';
3370  if (!$selected) {
3371  $out .= '<option value="-1" selected>'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3372  } else {
3373  $out .= '<option value="-1">'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3374  }
3375 
3376  $i = 0;
3377  while ($i < $num) {
3378  $objp = $this->db->fetch_object($result);
3379 
3380  if (is_null($objp->idprodfournprice)) {
3381  // There is no supplier price found, we will use the vat rate for sale
3382  $objp->tva_tx = $objp->tva_tx_sale;
3383  $objp->default_vat_code = $objp->default_vat_code_sale;
3384  }
3385 
3386  $outkey = $objp->idprodfournprice; // id in table of price
3387  if (!$outkey && $alsoproductwithnosupplierprice) {
3388  $outkey = 'idprod_'.$objp->rowid; // id of product
3389  }
3390 
3391  $outref = $objp->ref;
3392  $outbarcode = $objp->barcode;
3393  $outqty = 1;
3394  $outdiscount = 0;
3395  $outtype = $objp->fk_product_type;
3396  $outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3397  $outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
3398 
3399  // Units
3400  $outvalUnits = '';
3401  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3402  if (!empty($objp->unit_short)) {
3403  $outvalUnits .= ' - '.$objp->unit_short;
3404  }
3405  if (!empty($objp->weight) && $objp->weight_units !== null) {
3406  $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3407  $outvalUnits .= ' - '.$unitToShow;
3408  }
3409  if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3410  $unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
3411  $outvalUnits .= ' - '.$unitToShow;
3412  }
3413  if (!empty($objp->surface) && $objp->surface_units !== null) {
3414  $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3415  $outvalUnits .= ' - '.$unitToShow;
3416  }
3417  if (!empty($objp->volume) && $objp->volume_units !== null) {
3418  $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3419  $outvalUnits .= ' - '.$unitToShow;
3420  }
3421  if ($outdurationvalue && $outdurationunit) {
3422  $da = array(
3423  'h' => $langs->trans('Hour'),
3424  'd' => $langs->trans('Day'),
3425  'w' => $langs->trans('Week'),
3426  'm' => $langs->trans('Month'),
3427  'y' => $langs->trans('Year')
3428  );
3429  if (isset($da[$outdurationunit])) {
3430  $outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
3431  }
3432  }
3433  }
3434 
3435  $objRef = $objp->ref;
3436  if ($filterkey && $filterkey != '') {
3437  $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
3438  }
3439  $objRefFourn = $objp->ref_fourn;
3440  if ($filterkey && $filterkey != '') {
3441  $objRefFourn = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRefFourn, 1);
3442  }
3443  $label = $objp->label;
3444  if ($filterkey && $filterkey != '') {
3445  $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
3446  }
3447 
3448  $optlabel = $objp->ref;
3449  if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3450  $optlabel .= ' <span class="opacitymedium">('.$objp->ref_fourn.')</span>';
3451  }
3452  if (isModEnabled('barcode') && !empty($objp->barcode)) {
3453  $optlabel .= ' ('.$outbarcode.')';
3454  }
3455  $optlabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3456 
3457  $outvallabel = $objRef;
3458  if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3459  $outvallabel .= ' ('.$objRefFourn.')';
3460  }
3461  if (isModEnabled('barcode') && !empty($objp->barcode)) {
3462  $outvallabel .= ' ('.$outbarcode.')';
3463  }
3464  $outvallabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3465 
3466  // Units
3467  $optlabel .= $outvalUnits;
3468  $outvallabel .= $outvalUnits;
3469 
3470  if (!empty($objp->idprodfournprice)) {
3471  $outqty = $objp->quantity;
3472  $outdiscount = $objp->remise_percent;
3473  if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
3474  $prod_supplier = new ProductFournisseur($this->db);
3475  $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3476  $prod_supplier->id = $objp->fk_product;
3477  $prod_supplier->fourn_qty = $objp->quantity;
3478  $prod_supplier->fourn_tva_tx = $objp->tva_tx;
3479  $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3480 
3481  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3482  $priceparser = new PriceParser($this->db);
3483  $price_result = $priceparser->parseProductSupplier($prod_supplier);
3484  if ($price_result >= 0) {
3485  $objp->fprice = $price_result;
3486  if ($objp->quantity >= 1) {
3487  $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
3488  }
3489  }
3490  }
3491  if ($objp->quantity == 1) {
3492  $optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3493  $outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/";
3494  $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3495  $outvallabel .= $langs->transnoentities("Unit");
3496  } else {
3497  $optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3498  $outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3499  $optlabel .= ' '.$langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3500  $outvallabel .= ' '.$langs->transnoentities("Units");
3501  }
3502 
3503  if ($objp->quantity > 1) {
3504  $optlabel .= " (".price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
3505  $outvallabel .= " (".price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
3506  }
3507  if ($objp->remise_percent >= 1) {
3508  $optlabel .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
3509  $outvallabel .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
3510  }
3511  if ($objp->duration) {
3512  $optlabel .= " - ".$objp->duration;
3513  $outvallabel .= " - ".$objp->duration;
3514  }
3515  if (!$socid) {
3516  $optlabel .= " - ".dol_trunc($objp->name, 8);
3517  $outvallabel .= " - ".dol_trunc($objp->name, 8);
3518  }
3519  if ($objp->supplier_reputation) {
3520  //TODO dictionary
3521  $reputations = array(''=>$langs->trans('Standard'), 'FAVORITE'=>$langs->trans('Favorite'), 'NOTTHGOOD'=>$langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER'=>$langs->trans('DoNotOrderThisProductToThisSupplier'));
3522 
3523  $optlabel .= " - ".$reputations[$objp->supplier_reputation];
3524  $outvallabel .= " - ".$reputations[$objp->supplier_reputation];
3525  }
3526  } else {
3527  if (empty($alsoproductwithnosupplierprice)) { // No supplier price defined for couple product/supplier
3528  $optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3529  $outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3530  } else // No supplier price defined for product, even on other suppliers
3531  {
3532  $optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3533  $outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3534  }
3535  }
3536 
3537  if (isModEnabled('stock') && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3538  $novirtualstock = ($showstockinlist == 2);
3539 
3540  if (!empty($user->rights->stock->lire)) {
3541  $outvallabel .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
3542 
3543  if ($objp->stock > 0) {
3544  $optlabel .= ' - <span class="product_line_stock_ok">';
3545  } elseif ($objp->stock <= 0) {
3546  $optlabel .= ' - <span class="product_line_stock_too_low">';
3547  }
3548  $optlabel .= $langs->transnoentities("Stock").':'.price(price2num($objp->stock, 'MS'));
3549  $optlabel .= '</span>';
3550  if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) { // Warning, this option may slow down combo list generation
3551  $langs->load("stocks");
3552 
3553  $tmpproduct = new Product($this->db);
3554  $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3555  $tmpproduct->load_virtual_stock();
3556  $virtualstock = $tmpproduct->stock_theorique;
3557 
3558  $outvallabel .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
3559 
3560  $optlabel .= ' - '.$langs->transnoentities("VirtualStock").':';
3561  if ($virtualstock > 0) {
3562  $optlabel .= '<span class="product_line_stock_ok">';
3563  } elseif ($virtualstock <= 0) {
3564  $optlabel .= '<span class="product_line_stock_too_low">';
3565  }
3566  $optlabel .= $virtualstock;
3567  $optlabel .= '</span>';
3568 
3569  unset($tmpproduct);
3570  }
3571  }
3572  }
3573 
3574  $optstart = '<option value="'.$outkey.'"';
3575  if ($selected && $selected == $objp->idprodfournprice) {
3576  $optstart .= ' selected';
3577  }
3578  if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
3579  $optstart .= ' disabled';
3580  }
3581 
3582  if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
3583  $optstart .= ' data-product-id="'.dol_escape_htmltag($objp->rowid).'"';
3584  $optstart .= ' data-price-id="'.dol_escape_htmltag($objp->idprodfournprice).'"';
3585  $optstart .= ' data-qty="'.dol_escape_htmltag($objp->quantity).'"';
3586  $optstart .= ' data-up="'.dol_escape_htmltag(price2num($objp->unitprice)).'"';
3587  $optstart .= ' data-up-locale="'.dol_escape_htmltag(price($objp->unitprice)).'"';
3588  $optstart .= ' data-discount="'.dol_escape_htmltag($outdiscount).'"';
3589  $optstart .= ' data-tvatx="'.dol_escape_htmltag(price2num($objp->tva_tx)).'"';
3590  $optstart .= ' data-tvatx-formated="'.dol_escape_htmltag(price($objp->tva_tx, 0, $langs, 1, -1, 2)).'"';
3591  $optstart .= ' data-default-vat-code="'.dol_escape_htmltag($objp->default_vat_code).'"';
3592  }
3593  $optstart .= ' data-description="'.dol_escape_htmltag($objp->description, 0, 1).'"';
3594 
3595  $outarrayentry = array(
3596  'key' => $outkey,
3597  'value' => $outref,
3598  'label' => $outvallabel,
3599  'qty' => $outqty,
3600  'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
3601  'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
3602  'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
3603  'tva_tx_formated' => price($objp->tva_tx, 0, $langs, 1, -1, 2),
3604  'tva_tx' => price2num($objp->tva_tx),
3605  'default_vat_code' => $objp->default_vat_code,
3606  'discount' => $outdiscount,
3607  'type' => $outtype,
3608  'duration_value' => $outdurationvalue,
3609  'duration_unit' => $outdurationunit,
3610  'disabled' => (empty($objp->idprodfournprice) ? true : false),
3611  'description' => $objp->description
3612  );
3613 
3614  $parameters = array(
3615  'objp' => &$objp,
3616  'optstart' => &$optstart,
3617  'optlabel' => &$optlabel,
3618  'outvallabel' => &$outvallabel,
3619  'outarrayentry' => &$outarrayentry
3620  );
3621  $reshook = $hookmanager->executeHooks('selectProduitsFournisseurListOption', $parameters, $this);
3622 
3623 
3624  // Add new entry
3625  // "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
3626  // "label" value of json key array is used by jQuery automatically as text for combo box
3627  $out .= $optstart . ' data-html="'.dol_escape_htmltag($optlabel).'">' . $optlabel . "</option>\n";;
3628  array_push(
3629  $outarray,
3630  array('key'=>$outkey,
3631  'value'=>$outref,
3632  'label'=>$outvallabel,
3633  'qty'=>$outqty,
3634  'price_qty_ht'=>price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
3635  'price_qty_ht_locale'=>price($objp->fprice),
3636  'price_unit_ht'=>price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
3637  'price_unit_ht_locale'=>price($objp->unitprice),
3638  'price_ht'=>price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
3639  'tva_tx_formated' => price($objp->tva_tx),
3640  'tva_tx'=>price2num($objp->tva_tx),
3641  'default_vat_code'=>$objp->default_vat_code,
3642  'discount'=>$outdiscount,
3643  'type'=>$outtype,
3644  'duration_value'=>$outdurationvalue,
3645  'duration_unit'=>$outdurationunit,
3646  'disabled'=>(empty($objp->idprodfournprice) ? true : false),
3647  'description'=>$objp->description
3648  )
3649  );
3650  // Exemple of var_dump $outarray
3651  // array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
3652  // ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)"
3653  // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
3654  //}
3655  //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3656  //$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)');
3657  //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3658 
3659  $i++;
3660  }
3661  $out .= '</select>';
3662 
3663  $this->db->free($result);
3664 
3665  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
3666  $out .= ajax_combobox($htmlname);
3667  } else {
3668  dol_print_error($this->db);
3669  }
3670 
3671  if (empty($outputmode)) {
3672  return $out;
3673  }
3674  return $outarray;
3675  }
3676 
3677  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3686  public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = '')
3687  {
3688  // phpcs:enable
3689  global $langs, $conf;
3690 
3691  $langs->load('stocks');
3692 
3693  $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
3694  $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
3695  $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
3696  $sql .= " FROM ".$this->db->prefix()."product as p";
3697  $sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
3698  $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON pfp.fk_soc = s.rowid";
3699  $sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")";
3700  $sql .= " AND p.tobuy = 1";
3701  $sql .= " AND s.fournisseur = 1";
3702  $sql .= " AND p.rowid = ".((int) $productid);
3703  if (empty($conf->global->PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED)) {
3704  $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
3705  } else {
3706  $sql .= " ORDER BY pfp.unitprice ASC";
3707  }
3708 
3709  dol_syslog(get_class($this)."::select_product_fourn_price", LOG_DEBUG);
3710  $result = $this->db->query($sql);
3711 
3712  if ($result) {
3713  $num = $this->db->num_rows($result);
3714 
3715  $form = '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3716 
3717  if (!$num) {
3718  $form .= '<option value="0">-- '.$langs->trans("NoSupplierPriceDefinedForThisProduct").' --</option>';
3719  } else {
3720  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3721  $form .= '<option value="0">&nbsp;</option>';
3722 
3723  $i = 0;
3724  while ($i < $num) {
3725  $objp = $this->db->fetch_object($result);
3726 
3727  $opt = '<option value="'.$objp->idprodfournprice.'"';
3728  //if there is only one supplier, preselect it
3729  if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier) || ($i == 0 && !empty($conf->global->PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED))) {
3730  $opt .= ' selected';
3731  }
3732  $opt .= '>'.$objp->name.' - '.$objp->ref_fourn.' - ';
3733 
3734  if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
3735  $prod_supplier = new ProductFournisseur($this->db);
3736  $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3737  $prod_supplier->id = $productid;
3738  $prod_supplier->fourn_qty = $objp->quantity;
3739  $prod_supplier->fourn_tva_tx = $objp->tva_tx;
3740  $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3741 
3742  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3743  $priceparser = new PriceParser($this->db);
3744  $price_result = $priceparser->parseProductSupplier($prod_supplier);
3745  if ($price_result >= 0) {
3746  $objp->fprice = $price_result;
3747  if ($objp->quantity >= 1) {
3748  $objp->unitprice = $objp->fprice / $objp->quantity;
3749  }
3750  }
3751  }
3752  if ($objp->quantity == 1) {
3753  $opt .= price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3754  }
3755 
3756  $opt .= $objp->quantity.' ';
3757 
3758  if ($objp->quantity == 1) {
3759  $opt .= $langs->trans("Unit");
3760  } else {
3761  $opt .= $langs->trans("Units");
3762  }
3763  if ($objp->quantity > 1) {
3764  $opt .= " - ";
3765  $opt .= price($objp->unitprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit");
3766  }
3767  if ($objp->duration) {
3768  $opt .= " - ".$objp->duration;
3769  }
3770  $opt .= "</option>\n";
3771 
3772  $form .= $opt;
3773  $i++;
3774  }
3775  }
3776 
3777  $form .= '</select>';
3778  $this->db->free($result);
3779  return $form;
3780  } else {
3781  dol_print_error($this->db);
3782  }
3783  }
3784 
3785  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3795  public function select_address($selected, $socid, $htmlname = 'address_id', $showempty = 0)
3796  {
3797  // phpcs:enable
3798  // looking for users
3799  $sql = "SELECT a.rowid, a.label";
3800  $sql .= " FROM ".$this->db->prefix()."societe_address as a";
3801  $sql .= " WHERE a.fk_soc = ".((int) $socid);
3802  $sql .= " ORDER BY a.label ASC";
3803 
3804  dol_syslog(get_class($this)."::select_address", LOG_DEBUG);
3805  $resql = $this->db->query($sql);
3806  if ($resql) {
3807  print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3808  if ($showempty) {
3809  print '<option value="0">&nbsp;</option>';
3810  }
3811  $num = $this->db->num_rows($resql);
3812  $i = 0;
3813  if ($num) {
3814  while ($i < $num) {
3815  $obj = $this->db->fetch_object($resql);
3816 
3817  if ($selected && $selected == $obj->rowid) {
3818  print '<option value="'.$obj->rowid.'" selected>'.$obj->label.'</option>';
3819  } else {
3820  print '<option value="'.$obj->rowid.'">'.$obj->label.'</option>';
3821  }
3822  $i++;
3823  }
3824  }
3825  print '</select>';
3826  return $num;
3827  } else {
3828  dol_print_error($this->db);
3829  }
3830  }
3831 
3832  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3839  {
3840  // phpcs:enable
3841  global $langs;
3842 
3843  $num = count($this->cache_conditions_paiements);
3844  if ($num > 0) {
3845  return 0; // Cache already loaded
3846  }
3847 
3848  dol_syslog(__METHOD__, LOG_DEBUG);
3849 
3850  $sql = "SELECT rowid, code, libelle as label, deposit_percent";
3851  $sql .= " FROM ".$this->db->prefix().'c_payment_term';
3852  $sql .= " WHERE entity IN (".getEntity('c_payment_term').")";
3853  $sql .= " AND active > 0";
3854  $sql .= " ORDER BY sortorder";
3855 
3856  $resql = $this->db->query($sql);
3857  if ($resql) {
3858  $num = $this->db->num_rows($resql);
3859  $i = 0;
3860  while ($i < $num) {
3861  $obj = $this->db->fetch_object($resql);
3862 
3863  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3864  $label = ($langs->trans("PaymentConditionShort".$obj->code) != ("PaymentConditionShort".$obj->code) ? $langs->trans("PaymentConditionShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3865  $this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code;
3866  $this->cache_conditions_paiements[$obj->rowid]['label'] = $label;
3867  $this->cache_conditions_paiements[$obj->rowid]['deposit_percent'] = $obj->deposit_percent;
3868  $i++;
3869  }
3870 
3871  //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table
3872 
3873  return $num;
3874  } else {
3875  dol_print_error($this->db);
3876  return -1;
3877  }
3878  }
3879 
3880  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3886  public function load_cache_availability()
3887  {
3888  // phpcs:enable
3889  global $langs;
3890 
3891  $num = count($this->cache_availability); // TODO Use $conf->cache['availability'] instead of $this->cache_availability
3892  if ($num > 0) {
3893  return 0; // Cache already loaded
3894  }
3895 
3896  dol_syslog(__METHOD__, LOG_DEBUG);
3897 
3898  $langs->load('propal');
3899 
3900  $sql = "SELECT rowid, code, label, position";
3901  $sql .= " FROM ".$this->db->prefix().'c_availability';
3902  $sql .= " WHERE active > 0";
3903 
3904  $resql = $this->db->query($sql);
3905  if ($resql) {
3906  $num = $this->db->num_rows($resql);
3907  $i = 0;
3908  while ($i < $num) {
3909  $obj = $this->db->fetch_object($resql);
3910 
3911  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3912  $label = ($langs->trans("AvailabilityType".$obj->code) != ("AvailabilityType".$obj->code) ? $langs->trans("AvailabilityType".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3913  $this->cache_availability[$obj->rowid]['code'] = $obj->code;
3914  $this->cache_availability[$obj->rowid]['label'] = $label;
3915  $this->cache_availability[$obj->rowid]['position'] = $obj->position;
3916  $i++;
3917  }
3918 
3919  $this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
3920 
3921  return $num;
3922  } else {
3923  dol_print_error($this->db);
3924  return -1;
3925  }
3926  }
3927 
3938  public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '')
3939  {
3940  global $langs, $user;
3941 
3942  $this->load_cache_availability();
3943 
3944  dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
3945 
3946  print '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3947  if ($addempty) {
3948  print '<option value="0">&nbsp;</option>';
3949  }
3950  foreach ($this->cache_availability as $id => $arrayavailability) {
3951  if ($selected == $id) {
3952  print '<option value="'.$id.'" selected>';
3953  } else {
3954  print '<option value="'.$id.'">';
3955  }
3956  print dol_escape_htmltag($arrayavailability['label']);
3957  print '</option>';
3958  }
3959  print '</select>';
3960  if ($user->admin) {
3961  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3962  }
3963  print ajax_combobox($htmlname);
3964  }
3965 
3971  public function loadCacheInputReason()
3972  {
3973  global $langs;
3974 
3975  $num = count($this->cache_demand_reason); // TODO Use $conf->cache['input_reason'] instead of $this->cache_demand_reason
3976  if ($num > 0) {
3977  return 0; // Cache already loaded
3978  }
3979 
3980  $sql = "SELECT rowid, code, label";
3981  $sql .= " FROM ".$this->db->prefix().'c_input_reason';
3982  $sql .= " WHERE active > 0";
3983 
3984  $resql = $this->db->query($sql);
3985  if ($resql) {
3986  $num = $this->db->num_rows($resql);
3987  $i = 0;
3988  $tmparray = array();
3989  while ($i < $num) {
3990  $obj = $this->db->fetch_object($resql);
3991 
3992  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3993  $label = ($obj->label != '-' ? $obj->label : '');
3994  if ($langs->trans("DemandReasonType".$obj->code) != ("DemandReasonType".$obj->code)) {
3995  $label = $langs->trans("DemandReasonType".$obj->code); // So translation key DemandReasonTypeSRC_XXX will work
3996  }
3997  if ($langs->trans($obj->code) != $obj->code) {
3998  $label = $langs->trans($obj->code); // So translation key SRC_XXX will work
3999  }
4000 
4001  $tmparray[$obj->rowid]['id'] = $obj->rowid;
4002  $tmparray[$obj->rowid]['code'] = $obj->code;
4003  $tmparray[$obj->rowid]['label'] = $label;
4004  $i++;
4005  }
4006 
4007  $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
4008 
4009  unset($tmparray);
4010  return $num;
4011  } else {
4012  dol_print_error($this->db);
4013  return -1;
4014  }
4015  }
4016 
4029  public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
4030  {
4031  global $langs, $user;
4032 
4033  $this->loadCacheInputReason();
4034 
4035  print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_'.$htmlname.'" name="'.$htmlname.'">';
4036  if ($addempty) {
4037  print '<option value="0"'.(empty($selected) ? ' selected' : '').'>&nbsp;</option>';
4038  }
4039  foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
4040  if ($arraydemandreason['code'] == $exclude) {
4041  continue;
4042  }
4043 
4044  if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
4045  print '<option value="'.$arraydemandreason['id'].'" selected>';
4046  } else {
4047  print '<option value="'.$arraydemandreason['id'].'">';
4048  }
4049  $label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
4050  print $langs->trans($label);
4051  print '</option>';
4052  }
4053  print '</select>';
4054  if ($user->admin && empty($notooltip)) {
4055  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4056  }
4057  print ajax_combobox('select_'.$htmlname);
4058  }
4059 
4060  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4066  public function load_cache_types_paiements()
4067  {
4068  // phpcs:enable
4069  global $langs;
4070 
4071  $num = count($this->cache_types_paiements); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_types_paiements
4072  if ($num > 0) {
4073  return $num; // Cache already loaded
4074  }
4075 
4076  dol_syslog(__METHOD__, LOG_DEBUG);
4077 
4078  $this->cache_types_paiements = array();
4079 
4080  $sql = "SELECT id, code, libelle as label, type, active";
4081  $sql .= " FROM ".$this->db->prefix()."c_paiement";
4082  $sql .= " WHERE entity IN (".getEntity('c_paiement').")";
4083 
4084  $resql = $this->db->query($sql);
4085  if ($resql) {
4086  $num = $this->db->num_rows($resql);
4087  $i = 0;
4088  while ($i < $num) {
4089  $obj = $this->db->fetch_object($resql);
4090 
4091  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
4092  $label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
4093  $this->cache_types_paiements[$obj->id]['id'] = $obj->id;
4094  $this->cache_types_paiements[$obj->id]['code'] = $obj->code;
4095  $this->cache_types_paiements[$obj->id]['label'] = $label;
4096  $this->cache_types_paiements[$obj->id]['type'] = $obj->type;
4097  $this->cache_types_paiements[$obj->id]['active'] = $obj->active;
4098  $i++;
4099  }
4100 
4101  $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
4102 
4103  return $num;
4104  } else {
4105  dol_print_error($this->db);
4106  return -1;
4107  }
4108  }
4109 
4110 
4111  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4129  public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
4130  {
4131  // phpcs:enable
4132  print $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, $noinfoadmin, $morecss, $deposit_percent);
4133  }
4134 
4135 
4152  public function getSelectConditionsPaiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
4153  {
4154  global $langs, $user, $conf;
4155 
4156  $out = '';
4157  dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
4158 
4160 
4161  // Set default value if not already set by caller
4162  if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) {
4163  $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID;
4164  }
4165 
4166  $out.= '<select id="'.$htmlname.'" class="flat selectpaymentterms'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4167  if ($addempty) {
4168  $out.= '<option value="0">&nbsp;</option>';
4169  }
4170 
4171  $selectedDepositPercent = null;
4172 
4173  foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
4174  if ($filtertype <= 0 && !empty($arrayconditions['deposit_percent'])) {
4175  continue;
4176  }
4177 
4178  if ($selected == $id) {
4179  $selectedDepositPercent = $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'];
4180  $out .= '<option value="'.$id.'" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '" selected>';
4181  } else {
4182  $out .= '<option value="'.$id.'" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '">';
4183  }
4184  $label = $arrayconditions['label'];
4185 
4186  if (!empty($arrayconditions['deposit_percent'])) {
4187  $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'], $label);
4188  }
4189 
4190  $out.= $label;
4191  $out.= '</option>';
4192  }
4193  $out.= '</select>';
4194  if ($user->admin && empty($noinfoadmin)) {
4195  $out.= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4196  }
4197  $out.= ajax_combobox($htmlname);
4198 
4199  if ($deposit_percent >= 0) {
4200  $out .= ' <span id="'.$htmlname.'_deposit_percent_container"' . (empty($selectedDepositPercent) ? ' style="display: none"' : '') . '>';
4201  $out .= $langs->trans('DepositPercent') . ' : ';
4202  $out .= '<input id="'.$htmlname.'_deposit_percent" name="'.$htmlname.'_deposit_percent" class="maxwidth50" value="' . strval($deposit_percent) . '" />';
4203  $out .= '</span>';
4204  $out .= '
4205  <script>
4206  $(document).ready(function () {
4207  $("#' . $htmlname . '").change(function () {
4208  let $selected = $(this).find("option:selected");
4209  let depositPercent = $selected.attr("data-deposit_percent");
4210 
4211  if (depositPercent.length > 0) {
4212  $("#'.$htmlname.'_deposit_percent_container").show().find("#'.$htmlname.'_deposit_percent").val(depositPercent);
4213  } else {
4214  $("#'.$htmlname.'_deposit_percent_container").hide();
4215  }
4216 
4217  return true;
4218  });
4219  });
4220  </script>';
4221  }
4222 
4223  return $out;
4224  }
4225 
4226 
4227  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4244  public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
4245  {
4246  // phpcs:enable
4247  global $langs, $user, $conf;
4248 
4249  $out = '';
4250 
4251  dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
4252 
4253  $filterarray = array();
4254  if ($filtertype == 'CRDT') {
4255  $filterarray = array(0, 2, 3);
4256  } elseif ($filtertype == 'DBIT') {
4257  $filterarray = array(1, 2, 3);
4258  } elseif ($filtertype != '' && $filtertype != '-1') {
4259  $filterarray = explode(',', $filtertype);
4260  }
4261 
4262  $this->load_cache_types_paiements();
4263 
4264  // Set default value if not already set by caller
4265  if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID)) {
4266  $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID;
4267  }
4268 
4269  $out .= '<select id="select'.$htmlname.'" class="flat selectpaymenttypes'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4270  if ($empty) {
4271  $out .= '<option value="">&nbsp;</option>';
4272  }
4273  foreach ($this->cache_types_paiements as $id => $arraytypes) {
4274  // If not good status
4275  if ($active >= 0 && $arraytypes['active'] != $active) {
4276  continue;
4277  }
4278 
4279  // On passe si on a demande de filtrer sur des modes de paiments particuliers
4280  if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
4281  continue;
4282  }
4283 
4284  // We discard empty line if showempty is on because an empty line has already been output.
4285  if ($empty && empty($arraytypes['code'])) {
4286  continue;
4287  }
4288 
4289  if ($format == 0) {
4290  $out .= '<option value="'.$id.'"';
4291  } elseif ($format == 1) {
4292  $out .= '<option value="'.$arraytypes['code'].'"';
4293  } elseif ($format == 2) {
4294  $out .= '<option value="'.$arraytypes['code'].'"';
4295  } elseif ($format == 3) {
4296  $out .= '<option value="'.$id.'"';
4297  }
4298  // Print attribute selected or not
4299  if ($format == 1 || $format == 2) {
4300  if ($selected == $arraytypes['code']) {
4301  $out .= ' selected';
4302  }
4303  } else {
4304  if ($selected == $id) {
4305  $out .= ' selected';
4306  }
4307  }
4308  $out .= '>';
4309  $value = '';
4310  if ($format == 0) {
4311  $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4312  } elseif ($format == 1) {
4313  $value = $arraytypes['code'];
4314  } elseif ($format == 2) {
4315  $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4316  } elseif ($format == 3) {
4317  $value = $arraytypes['code'];
4318  }
4319  $out .= $value ? $value : '&nbsp;';
4320  $out .= '</option>';
4321  }
4322  $out .= '</select>';
4323  if ($user->admin && !$noadmininfo) {
4324  $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4325  }
4326  $out .= ajax_combobox('select'.$htmlname);
4327 
4328  if (empty($nooutput)) {
4329  print $out;
4330  } else {
4331  return $out;
4332  }
4333  }
4334 
4335 
4344  public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
4345  {
4346  global $langs;
4347 
4348  $return = '<select class="flat maxwidth100" id="select_'.$htmlname.'" name="'.$htmlname.'">';
4349  $options = array(
4350  'HT'=>$langs->trans("HT"),
4351  'TTC'=>$langs->trans("TTC")
4352  );
4353  foreach ($options as $id => $value) {
4354  if ($selected == $id) {
4355  $return .= '<option value="'.$id.'" selected>'.$value;
4356  } else {
4357  $return .= '<option value="'.$id.'">'.$value;
4358  }
4359  $return .= '</option>';
4360  }
4361  $return .= '</select>';
4362  if ($addjscombo) {
4363  $return .= ajax_combobox('select_'.$htmlname);
4364  }
4365 
4366  return $return;
4367  }
4368 
4369  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4375  public function load_cache_transport_mode()
4376  {
4377  // phpcs:enable
4378  global $langs;
4379 
4380  $num = count($this->cache_transport_mode); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_transport_mode
4381  if ($num > 0) {
4382  return $num; // Cache already loaded
4383  }
4384 
4385  dol_syslog(__METHOD__, LOG_DEBUG);
4386 
4387  $this->cache_transport_mode = array();
4388 
4389  $sql = "SELECT rowid, code, label, active";
4390  $sql .= " FROM ".$this->db->prefix()."c_transport_mode";
4391  $sql .= " WHERE entity IN (".getEntity('c_transport_mode').")";
4392 
4393  $resql = $this->db->query($sql);
4394  if ($resql) {
4395  $num = $this->db->num_rows($resql);
4396  $i = 0;
4397  while ($i < $num) {
4398  $obj = $this->db->fetch_object($resql);
4399 
4400  // If traduction exist, we use it else we take the default label
4401  $label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
4402  $this->cache_transport_mode[$obj->rowid]['rowid'] = $obj->rowid;
4403  $this->cache_transport_mode[$obj->rowid]['code'] = $obj->code;
4404  $this->cache_transport_mode[$obj->rowid]['label'] = $label;
4405  $this->cache_transport_mode[$obj->rowid]['active'] = $obj->active;
4406  $i++;
4407  }
4408 
4409  $this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
4410 
4411  return $num;
4412  } else {
4413  dol_print_error($this->db);
4414  return -1;
4415  }
4416  }
4417 
4431  public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
4432  {
4433  global $langs, $user;
4434 
4435  dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$format, LOG_DEBUG);
4436 
4437  $this->load_cache_transport_mode();
4438 
4439  print '<select id="select'.$htmlname.'" class="flat selectmodetransport'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4440  if ($empty) {
4441  print '<option value="">&nbsp;</option>';
4442  }
4443  foreach ($this->cache_transport_mode as $id => $arraytypes) {
4444  // If not good status
4445  if ($active >= 0 && $arraytypes['active'] != $active) {
4446  continue;
4447  }
4448 
4449  // We discard empty line if showempty is on because an empty line has already been output.
4450  if ($empty && empty($arraytypes['code'])) {
4451  continue;
4452  }
4453 
4454  if ($format == 0) {
4455  print '<option value="'.$id.'"';
4456  } elseif ($format == 1) {
4457  print '<option value="'.$arraytypes['code'].'"';
4458  } elseif ($format == 2) {
4459  print '<option value="'.$arraytypes['code'].'"';
4460  } elseif ($format == 3) {
4461  print '<option value="'.$id.'"';
4462  }
4463  // If text is selected, we compare with code, else with id
4464  if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
4465  print ' selected';
4466  } elseif ($selected == $id) {
4467  print ' selected';
4468  }
4469  print '>';
4470  $value = '';
4471  if ($format == 0) {
4472  $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4473  } elseif ($format == 1) {
4474  $value = $arraytypes['code'];
4475  } elseif ($format == 2) {
4476  $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4477  } elseif ($format == 3) {
4478  $value = $arraytypes['code'];
4479  }
4480  print $value ? $value : '&nbsp;';
4481  print '</option>';
4482  }
4483  print '</select>';
4484  if ($user->admin && !$noadmininfo) {
4485  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4486  }
4487  }
4488 
4501  public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
4502  {
4503  global $langs, $conf, $user;
4504 
4505  $langs->load("admin");
4506  $langs->load("deliveries");
4507 
4508  $sql = "SELECT rowid, code, libelle as label";
4509  $sql .= " FROM ".$this->db->prefix()."c_shipment_mode";
4510  $sql .= " WHERE active > 0";
4511  if ($filtre) {
4512  $sql .= " AND ".$filtre;
4513  }
4514  $sql .= " ORDER BY libelle ASC";
4515 
4516  dol_syslog(get_class($this)."::selectShippingMode", LOG_DEBUG);
4517  $result = $this->db->query($sql);
4518  if ($result) {
4519  $num = $this->db->num_rows($result);
4520  $i = 0;
4521  if ($num) {
4522  print '<select id="select'.$htmlname.'" class="flat selectshippingmethod'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4523  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4524  print '<option value="-1">&nbsp;</option>';
4525  }
4526  while ($i < $num) {
4527  $obj = $this->db->fetch_object($result);
4528  if ($selected == $obj->rowid) {
4529  print '<option value="'.$obj->rowid.'" selected>';
4530  } else {
4531  print '<option value="'.$obj->rowid.'">';
4532  }
4533  print ($langs->trans("SendingMethod".strtoupper($obj->code)) != "SendingMethod".strtoupper($obj->code)) ? $langs->trans("SendingMethod".strtoupper($obj->code)) : $obj->label;
4534  print '</option>';
4535  $i++;
4536  }
4537  print "</select>";
4538  if ($user->admin && empty($noinfoadmin)) {
4539  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4540  }
4541 
4542  print ajax_combobox('select'.$htmlname);
4543  } else {
4544  print $langs->trans("NoShippingMethodDefined");
4545  }
4546  } else {
4547  dol_print_error($this->db);
4548  }
4549  }
4550 
4560  public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
4561  {
4562  global $langs;
4563 
4564  $langs->load("deliveries");
4565 
4566  if ($htmlname != "none") {
4567  print '<form method="POST" action="'.$page.'">';
4568  print '<input type="hidden" name="action" value="setshippingmethod">';
4569  print '<input type="hidden" name="token" value="'.newToken().'">';
4570  $this->selectShippingMethod($selected, $htmlname, '', $addempty);
4571  print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
4572  print '</form>';
4573  } else {
4574  if ($selected) {
4575  $code = $langs->getLabelFromKey($this->db, $selected, 'c_shipment_mode', 'rowid', 'code');
4576  print $langs->trans("SendingMethod".strtoupper($code));
4577  } else {
4578  print "&nbsp;";
4579  }
4580  }
4581  }
4582 
4591  public function selectSituationInvoices($selected = '', $socid = 0)
4592  {
4593  global $langs;
4594 
4595  $langs->load('bills');
4596 
4597  $opt = '<option value="" selected></option>';
4598  $sql = "SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc";
4599  $sql .= ' FROM '.$this->db->prefix().'facture';
4600  $sql .= ' WHERE entity IN ('.getEntity('invoice').')';
4601  $sql .= ' AND situation_counter >= 1';
4602  $sql .= ' AND fk_soc = '.(int) $socid;
4603  $sql .= ' AND type <> 2';
4604  $sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
4605  $resql = $this->db->query($sql);
4606 
4607  if ($resql && $this->db->num_rows($resql) > 0) {
4608  // Last seen cycle
4609  $ref = 0;
4610  while ($obj = $this->db->fetch_object($resql)) {
4611  //Same cycle ?
4612  if ($obj->situation_cycle_ref != $ref) {
4613  // Just seen this cycle
4614  $ref = $obj->situation_cycle_ref;
4615  //not final ?
4616  if ($obj->situation_final != 1) {
4617  //Not prov?
4618  if (substr($obj->ref, 1, 4) != 'PROV') {
4619  if ($selected == $obj->rowid) {
4620  $opt .= '<option value="'.$obj->rowid.'" selected>'.$obj->ref.'</option>';
4621  } else {
4622  $opt .= '<option value="'.$obj->rowid.'">'.$obj->ref.'</option>';
4623  }
4624  }
4625  }
4626  }
4627  }
4628  } else {
4629  dol_syslog("Error sql=".$sql.", error=".$this->error, LOG_ERR);
4630  }
4631  if ($opt == '<option value ="" selected></option>') {
4632  $opt = '<option value ="0" selected>'.$langs->trans('NoSituations').'</option>';
4633  }
4634  return $opt;
4635  }
4636 
4646  public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
4647  {
4648  global $langs;
4649 
4650  $langs->load('products');
4651 
4652  $return = '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'">';
4653 
4654  $sql = "SELECT rowid, label, code FROM ".$this->db->prefix()."c_units";
4655  $sql .= ' WHERE active > 0';
4656  if (!empty($unit_type)) {
4657  $sql .= " AND unit_type = '".$this->db->escape($unit_type)."'";
4658  }
4659  $sql .= " ORDER BY sortorder";
4660 
4661  $resql = $this->db->query($sql);
4662  if ($resql && $this->db->num_rows($resql) > 0) {
4663  if ($showempty) {
4664  $return .= '<option value="none"></option>';
4665  }
4666 
4667  while ($res = $this->db->fetch_object($resql)) {
4668  $unitLabel = $res->label;
4669  if (!empty($langs->tab_translate['unit'.$res->code])) { // check if Translation is available before
4670  $unitLabel = $langs->trans('unit'.$res->code) != $res->label ? $langs->trans('unit'.$res->code) : $res->label;
4671  }
4672 
4673  if ($selected == $res->rowid) {
4674  $return .= '<option value="'.$res->rowid.'" selected>'.$unitLabel.'</option>';
4675  } else {
4676  $return .= '<option value="'.$res->rowid.'">'.$unitLabel.'</option>';
4677  }
4678  }
4679  $return .= '</select>';
4680  }
4681  return $return;
4682  }
4683 
4684  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4699  public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0)
4700  {
4701  // phpcs:enable
4702  global $langs, $conf;
4703 
4704  $out = '';
4705 
4706  $langs->load("admin");
4707  $num = 0;
4708 
4709  $sql = "SELECT rowid, label, bank, clos as status, currency_code";
4710  $sql .= " FROM ".$this->db->prefix()."bank_account";
4711  $sql .= " WHERE entity IN (".getEntity('bank_account').")";
4712  if ($status != 2) {
4713  $sql .= " AND clos = ".(int) $status;
4714  }
4715  if ($filtre) {
4716  $sql .= " AND ".$filtre;
4717  }
4718  $sql .= " ORDER BY label";
4719 
4720  dol_syslog(get_class($this)."::select_comptes", LOG_DEBUG);
4721  $result = $this->db->query($sql);
4722  if ($result) {
4723  $num = $this->db->num_rows($result);
4724  $i = 0;
4725  if ($num) {
4726  $out .= '<select id="select'.$htmlname.'" class="flat selectbankaccount'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4727  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4728  $out .= '<option value="-1">&nbsp;</option>';
4729  }
4730 
4731  while ($i < $num) {
4732  $obj = $this->db->fetch_object($result);
4733  if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
4734  $out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'" selected>';
4735  } else {
4736  $out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'">';
4737  }
4738  $out .= trim($obj->label);
4739  if ($showcurrency) {
4740  $out .= ' ('.$obj->currency_code.')';
4741  }
4742  if ($status == 2 && $obj->status == 1) {
4743  $out .= ' ('.$langs->trans("Closed").')';
4744  }
4745  $out .= '</option>';
4746  $i++;
4747  }
4748  $out .= "</select>";
4749  $out .= ajax_combobox('select'.$htmlname);
4750  } else {
4751  if ($status == 0) {
4752  $out .= '<span class="opacitymedium">'.$langs->trans("NoActiveBankAccountDefined").'</span>';
4753  } else {
4754  $out .= '<span class="opacitymedium">'.$langs->trans("NoBankAccountFound").'</span>';
4755  }
4756  }
4757  } else {
4758  dol_print_error($this->db);
4759  }
4760 
4761  // Output or return
4762  if (empty($nooutput)) {
4763  print $out;
4764  } else {
4765  return $out;
4766  }
4767 
4768  return $num;
4769  }
4770 
4782  public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
4783  {
4784  global $langs, $conf;
4785 
4786  $langs->load("admin");
4787  $num = 0;
4788 
4789  $sql = "SELECT rowid, name, fk_country, status, entity";
4790  $sql .= " FROM ".$this->db->prefix()."establishment";
4791  $sql .= " WHERE 1=1";
4792  if ($status != 2) {
4793  $sql .= " AND status = ".(int) $status;
4794  }
4795  if ($filtre) {
4796  $sql .= " AND ".$filtre;
4797  }
4798  $sql .= " ORDER BY name";
4799 
4800  dol_syslog(get_class($this)."::select_establishment", LOG_DEBUG);
4801  $result = $this->db->query($sql);
4802  if ($result) {
4803  $num = $this->db->num_rows($result);
4804  $i = 0;
4805  if ($num) {
4806  print '<select id="select'.$htmlname.'" class="flat selectestablishment" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4807  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4808  print '<option value="-1">&nbsp;</option>';
4809  }
4810 
4811  while ($i < $num) {
4812  $obj = $this->db->fetch_object($result);
4813  if ($selected == $obj->rowid) {
4814  print '<option value="'.$obj->rowid.'" selected>';
4815  } else {
4816  print '<option value="'.$obj->rowid.'">';
4817  }
4818  print trim($obj->name);
4819  if ($status == 2 && $obj->status == 1) {
4820  print ' ('.$langs->trans("Closed").')';
4821  }
4822  print '</option>';
4823  $i++;
4824  }
4825  print "</select>";
4826  } else {
4827  if ($status == 0) {
4828  print '<span class="opacitymedium">'.$langs->trans("NoActiveEstablishmentDefined").'</span>';
4829  } else {
4830  print '<span class="opacitymedium">'.$langs->trans("NoEstablishmentFound").'</span>';
4831  }
4832  }
4833  } else {
4834  dol_print_error($this->db);
4835  }
4836  }
4837 
4847  public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
4848  {
4849  global $langs;
4850  if ($htmlname != "none") {
4851  print '<form method="POST" action="'.$page.'">';
4852  print '<input type="hidden" name="action" value="setbankaccount">';
4853  print '<input type="hidden" name="token" value="'.newToken().'">';
4854  print img_picto('', 'bank_account', 'class="pictofixedwidth"');
4855  $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
4856  if ($nbaccountfound > 0) {
4857  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
4858  }
4859  print '</form>';
4860  } else {
4861  $langs->load('banks');
4862 
4863  if ($selected) {
4864  require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
4865  $bankstatic = new Account($this->db);
4866  $result = $bankstatic->fetch($selected);
4867  if ($result) {
4868  print $bankstatic->getNomUrl(1);
4869  }
4870  } else {
4871  print "&nbsp;";
4872  }
4873  }
4874  }
4875 
4876  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4895  public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $markafterid = 0, $outputmode = 0, $include = 0, $morecss = '')
4896  {
4897  // phpcs:enable
4898  global $conf, $langs;
4899  $langs->load("categories");
4900 
4901  include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
4902 
4903  // For backward compatibility
4904  if (is_numeric($type)) {
4905  dol_syslog(__METHOD__.': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING);
4906  }
4907 
4908  if ($type === Categorie::TYPE_BANK_LINE) {
4909  // TODO Move this into common category feature
4910  $cate_arbo = array();
4911  $sql = "SELECT c.label, c.rowid";
4912  $sql .= " FROM ".$this->db->prefix()."bank_categ as c";
4913  $sql .= " WHERE entity = ".$conf->entity;
4914  $sql .= " ORDER BY c.label";
4915  $result = $this->db->query($sql);
4916  if ($result) {
4917  $num = $this->db->num_rows($result);
4918  $i = 0;
4919  while ($i < $num) {
4920  $objp = $this->db->fetch_object($result);
4921  if ($objp) {
4922  $cate_arbo[$objp->rowid] = array('id'=>$objp->rowid, 'fulllabel'=>$objp->label, 'color'=>'', 'picto'=>'category');
4923  }
4924  $i++;
4925  }
4926  $this->db->free($result);
4927  } else {
4928  dol_print_error($this->db);
4929  }
4930  } else {
4931  $cat = new Categorie($this->db);
4932  $cate_arbo = $cat->get_full_arbo($type, $markafterid, $include);
4933  }
4934 
4935  $outarray = array();
4936 
4937  $output = '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
4938  if (is_array($cate_arbo)) {
4939  if (!count($cate_arbo)) {
4940  $output .= '<option value="-1" disabled>'.$langs->trans("NoCategoriesDefined").'</option>';
4941  } else {
4942  $output .= '<option value="-1">&nbsp;</option>';
4943  foreach ($cate_arbo as $key => $value) {
4944  if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
4945  $add = 'selected ';
4946  } else {
4947  $add = '';
4948  }
4949  $output .= '<option '.$add.'value="'.$cate_arbo[$key]['id'].'"';
4950  $output .= ' data-html="'.dol_escape_htmltag(img_picto('', 'category', 'class="pictofixedwidth" style="color: #'.$cate_arbo[$key]['color'].'"').dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle')).'"';
4951  $output .= '>';
4952  $output .= dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle');
4953  $output .= '</option>';
4954 
4955  $outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
4956  }
4957  }
4958  }
4959  $output .= '</select>';
4960  $output .= "\n";
4961 
4962  if ($outputmode == 2) {
4963  return $cate_arbo;
4964  } elseif ($outputmode) {
4965  return $outarray;
4966  }
4967  return $output;
4968  }
4969 
4970  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4987  public function form_confirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
4988  {
4989  // phpcs:enable
4990  dol_syslog(__METHOD__.': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
4991  print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
4992  }
4993 
5021  public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0, $labelbuttonyes = 'Yes', $labelbuttonno = 'No')
5022  {
5023  global $langs, $conf;
5024 
5025  $more = '<!-- formconfirm - before call, page='.dol_escape_htmltag($page).' -->';
5026  $formconfirm = '';
5027  $inputok = array();
5028  $inputko = array();
5029 
5030  // Clean parameters
5031  $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
5032  if ($conf->browser->layout == 'phone') {
5033  $width = '95%';
5034  }
5035 
5036  // Set height automatically if not defined
5037  if (empty($height)) {
5038  $height = 220;
5039  if (is_array($formquestion) && count($formquestion) > 2) {
5040  $height += ((count($formquestion) - 2) * 24);
5041  }
5042  }
5043 
5044  if (is_array($formquestion) && !empty($formquestion)) {
5045  // First add hidden fields and value
5046  foreach ($formquestion as $key => $input) {
5047  if (is_array($input) && !empty($input)) {
5048  if ($input['type'] == 'hidden') {
5049  $moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : '');
5050  $morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : '');
5051 
5052  $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";
5053  }
5054  }
5055  }
5056 
5057  // Now add questions
5058  $moreonecolumn = '';
5059  $more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">'."\n";
5060  foreach ($formquestion as $key => $input) {
5061  if (is_array($input) && !empty($input)) {
5062  $size = (!empty($input['size']) ? ' size="'.$input['size'].'"' : ''); // deprecated. Use morecss instead.
5063  $moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : '');
5064  $morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : '');
5065 
5066  if ($input['type'] == 'text') {
5067  $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";
5068  } elseif ($input['type'] == 'password') {
5069  $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";
5070  } elseif ($input['type'] == 'textarea') {
5071  /*$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd">';
5072  $more .= '<textarea name="'.$input['name'].'" class="'.$morecss.'"'.$moreattr.'>';
5073  $more .= $input['value'];
5074  $more .= '</textarea>';
5075  $more .= '</div></div>'."\n";*/
5076  $moreonecolumn .= '<div class="margintoponly">';
5077  $moreonecolumn .= $input['label'].'<br>';
5078  $moreonecolumn .= '<textarea name="'.dol_escape_htmltag($input['name']).'" id="'.dol_escape_htmltag($input['name']).'" class="'.$morecss.'"'.$moreattr.'>';
5079  $moreonecolumn .= $input['value'];
5080  $moreonecolumn .= '</textarea>';
5081  $moreonecolumn .= '</div>';
5082  } elseif (in_array($input['type'], ['select', 'multiselect'])) {
5083  if (empty($morecss)) {
5084  $morecss = 'minwidth100';
5085  }
5086 
5087  $show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
5088  $key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
5089  $value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
5090  $translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
5091  $maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
5092  $disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
5093  $sort = isset($input['select_sort']) ? $input['select_sort'] : '';
5094 
5095  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
5096  if (!empty($input['label'])) {
5097  $more .= $input['label'].'</div><div class="tagtd left">';
5098  }
5099  if ($input['type'] == 'select') {
5100  $more .= $this->selectarray($input['name'], $input['values'], !empty($input['default']) ? $input['default'] : '-1', $show_empty, $key_in_label, $value_as_key, $moreattr, $translate, $maxlen, $disabled, $sort, $morecss);
5101  } else {
5102  $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);
5103  }
5104  $more .= '</div></div>'."\n";
5105  } elseif ($input['type'] == 'checkbox') {
5106  $more .= '<div class="tagtr">';
5107  $more .= '<div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].' </div><div class="tagtd">';
5108  $more .= '<input type="checkbox" class="flat'.($morecss ? ' '.$morecss : '').'" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'"'.$moreattr;
5109  if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0' && $input['value'] != '') {
5110  $more .= ' checked';
5111  }
5112  if (is_bool($input['value']) && $input['value']) {
5113  $more .= ' checked';
5114  }
5115  if (isset($input['disabled'])) {
5116  $more .= ' disabled';
5117  }
5118  $more .= ' /></div>';
5119  $more .= '</div>'."\n";
5120  } elseif ($input['type'] == 'radio') {
5121  $i = 0;
5122  foreach ($input['values'] as $selkey => $selval) {
5123  $more .= '<div class="tagtr">';
5124  if (isset($input['label'])) {
5125  if ($i == 0) {
5126  $more .= '<div class="tagtd'.(empty($input['tdclass']) ? ' tdtop' : (' tdtop '.$input['tdclass'])).'">'.$input['label'].'</div>';
5127  } else {
5128  $more .= '<div clas="tagtd'.(empty($input['tdclass']) ? '' : (' "'.$input['tdclass'])).'">&nbsp;</div>';
5129  }
5130  }
5131  $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;
5132  if (!empty($input['disabled'])) {
5133  $more .= ' disabled';
5134  }
5135  if (isset($input['default']) && $input['default'] === $selkey) {
5136  $more .= ' checked="checked"';
5137  }
5138  $more .= ' /> ';
5139  $more .= '<label for="'.dol_escape_htmltag($input['name'].$selkey).'" class="valignmiddle">'.$selval.'</label>';
5140  $more .= '</div></div>'."\n";
5141  $i++;
5142  }
5143  } elseif ($input['type'] == 'date' || $input['type'] == 'datetime') {
5144  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div>';
5145  $more .= '<div class="tagtd">';
5146  $addnowlink = (empty($input['datenow']) ? 0 : 1);
5147  $more .= $this->selectDate($input['value'], $input['name'], ($input['type'] == 'datetime' ? 1 : 0), ($input['type'] == 'datetime' ? 1 : 0), 0, '', 1, $addnowlink);
5148  $more .= '</div></div>'."\n";
5149  $formquestion[] = array('name'=>$input['name'].'day');
5150  $formquestion[] = array('name'=>$input['name'].'month');
5151  $formquestion[] = array('name'=>$input['name'].'year');
5152  $formquestion[] = array('name'=>$input['name'].'hour');
5153  $formquestion[] = array('name'=>$input['name'].'min');
5154  } elseif ($input['type'] == 'other') {
5155  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
5156  if (!empty($input['label'])) {
5157  $more .= $input['label'].'</div><div class="tagtd">';
5158  }
5159  $more .= $input['value'];
5160  $more .= '</div></div>'."\n";
5161  } elseif ($input['type'] == 'onecolumn') {
5162  $moreonecolumn .= '<div class="margintoponly">';
5163  $moreonecolumn .= $input['value'];
5164  $moreonecolumn .= '</div>'."\n";
5165  } elseif ($input['type'] == 'hidden') {
5166  // Do nothing more, already added by a previous loop
5167  } elseif ($input['type'] == 'separator') {
5168  $more .= '<br>';
5169  } else {
5170  $more .= 'Error type '.$input['type'].' for the confirm box is not a supported type';
5171  }
5172  }
5173  }
5174  $more .= '</div>'."\n";
5175  $more .= $moreonecolumn;
5176  }
5177 
5178  // JQUERY method dialog is broken with smartphone, we use standard HTML.
5179  // 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
5180  // See page product/card.php for example
5181  if (!empty($conf->dol_use_jmobile)) {
5182  $useajax = 0;
5183  }
5184  if (empty($conf->use_javascript_ajax)) {
5185  $useajax = 0;
5186  }
5187 
5188  if ($useajax) {
5189  $autoOpen = true;
5190  $dialogconfirm = 'dialog-confirm';
5191  $button = '';
5192  if (!is_numeric($useajax)) {
5193  $button = $useajax;
5194  $useajax = 1;
5195  $autoOpen = false;
5196  $dialogconfirm .= '-'.$button;
5197  }
5198  $pageyes = $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.urlencode($action).'&confirm=yes';
5199  $pageno = ($useajax == 2 ? $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.urlencode($action).'&confirm=no' : '');
5200 
5201  // Add input fields into list of fields to read during submit (inputok and inputko)
5202  if (is_array($formquestion)) {
5203  foreach ($formquestion as $key => $input) {
5204  //print "xx ".$key." rr ".is_array($input)."<br>\n";
5205  // Add name of fields to propagate with the GET when submitting the form with button OK.
5206  if (is_array($input) && isset($input['name'])) {
5207  if (strpos($input['name'], ',') > 0) {
5208  $inputok = array_merge($inputok, explode(',', $input['name']));
5209  } else {
5210  array_push($inputok, $input['name']);
5211  }
5212  }
5213  // Add name of fields to propagate with the GET when submitting the form with button KO.
5214  if (isset($input['inputko']) && $input['inputko'] == 1) {
5215  array_push($inputko, $input['name']);
5216  }
5217  }
5218  }
5219 
5220  // Show JQuery confirm box.
5221  $formconfirm .= '<div id="'.$dialogconfirm.'" title="'.dol_escape_htmltag($title).'" style="display: none;">';
5222  if (is_array($formquestion) && !empty($formquestion['text'])) {
5223  $formconfirm .= '<div class="confirmtext">'.$formquestion['text'].'</div>'."\n";
5224  }
5225  if (!empty($more)) {
5226  $formconfirm .= '<div class="confirmquestions">'.$more.'</div>'."\n";
5227  }
5228  $formconfirm .= ($question ? '<div class="confirmmessage">'.img_help('', '').' '.$question.'</div>' : '');
5229  $formconfirm .= '</div>'."\n";
5230 
5231  $formconfirm .= "\n<!-- begin code of popup for formconfirm page=".$page." -->\n";
5232  $formconfirm .= '<script type="text/javascript">'."\n";
5233  $formconfirm .= "/* Code for the jQuery('#dialogforpopup').dialog() */\n";
5234  $formconfirm .= 'jQuery(document).ready(function() {
5235  $(function() {
5236  $( "#'.$dialogconfirm.'" ).dialog(
5237  {
5238  autoOpen: '.($autoOpen ? "true" : "false").',';
5239  if ($newselectedchoice == 'no') {
5240  $formconfirm .= '
5241  open: function() {
5242  $(this).parent().find("button.ui-button:eq(2)").focus();
5243  },';
5244  }
5245 
5246  $jsforcursor = '';
5247  if ($useajax == 1) {
5248  $jsforcursor = '// The call to urljump can be slow, so we set the wait cursor'."\n";
5249  $jsforcursor .= 'jQuery("html,body,#id-container").addClass("cursorwait");'."\n";
5250  }
5251 
5252  $postconfirmas = 'GET';
5253 
5254  $formconfirm .= '
5255  resizable: false,
5256  height: "'.$height.'",
5257  width: "'.$width.'",
5258  modal: true,
5259  closeOnEscape: false,
5260  buttons: {
5261  "'.dol_escape_js($langs->transnoentities($labelbuttonyes)).'": function() {
5262  var options = "token='.urlencode(newToken()).'";
5263  var inputok = '.json_encode($inputok).'; /* List of fields into form */
5264  var page = "'.dol_escape_js(!empty($page) ? $page : '').'";
5265  var pageyes = "'.dol_escape_js(!empty($pageyes) ? $pageyes : '').'";
5266 
5267  if (inputok.length > 0) {
5268  $.each(inputok, function(i, inputname) {
5269  var more = "";
5270  var inputvalue;
5271  if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
5272  inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
5273  } else {
5274  if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5275  inputvalue = $("#" + inputname + more).val();
5276  }
5277  if (typeof inputvalue == "undefined") { inputvalue=""; }
5278  console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
5279  options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5280  });
5281  }
5282  var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "&") + options;
5283  if (pageyes.length > 0) {';
5284  if ($postconfirmas == 'GET') {
5285  $formconfirm .= 'location.href = urljump;';
5286  } else {
5287  $formconfirm .= $jsforcursor;
5288  $formconfirm .= 'var post = $.post(
5289  pageyes,
5290  options,
5291  function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
5292  );';
5293  }
5294  $formconfirm .= '
5295  console.log("after post ok");
5296  }
5297  $(this).dialog("close");
5298  },
5299  "'.dol_escape_js($langs->transnoentities($labelbuttonno)).'": function() {
5300  var options = "token='.urlencode(newToken()).'";
5301  var inputko = '.json_encode($inputko).'; /* List of fields into form */
5302  var page = "'.dol_escape_js(!empty($page) ? $page : '').'";
5303  var pageno="'.dol_escape_js(!empty($pageno) ? $pageno : '').'";
5304  if (inputko.length > 0) {
5305  $.each(inputko, function(i, inputname) {
5306  var more = "";
5307  if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5308  var inputvalue = $("#" + inputname + more).val();
5309  if (typeof inputvalue == "undefined") { inputvalue=""; }
5310  options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5311  });
5312  }
5313  var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "&") + options;
5314  //alert(urljump);
5315  if (pageno.length > 0) {';
5316  if ($postconfirmas == 'GET') {
5317  $formconfirm .= 'location.href = urljump;';
5318  } else {
5319  $formconfirm .= $jsforcursor;
5320  $formconfirm .= 'var post = $.post(
5321  pageno,
5322  options,
5323  function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
5324  );';
5325  }
5326  $formconfirm .= '
5327  console.log("after post ko");
5328  }
5329  $(this).dialog("close");
5330  }
5331  }
5332  }
5333  );
5334 
5335  var button = "'.$button.'";
5336  if (button.length > 0) {
5337  $( "#" + button ).click(function() {
5338  $("#'.$dialogconfirm.'").dialog("open");
5339  });
5340  }
5341  });
5342  });
5343  </script>';
5344  $formconfirm .= "<!-- end ajax formconfirm -->\n";
5345  } else {
5346  $formconfirm .= "\n<!-- begin formconfirm page=".dol_escape_htmltag($page)." -->\n";
5347 
5348  if (empty($disableformtag)) {
5349  $formconfirm .= '<form method="POST" action="'.$page.'" class="notoptoleftroright">'."\n";
5350  }
5351 
5352  $formconfirm .= '<input type="hidden" name="action" value="'.$action.'">'."\n";
5353  $formconfirm .= '<input type="hidden" name="token" value="'.newToken().'">'."\n";
5354 
5355  $formconfirm .= '<table class="valid centpercent">'."\n";
5356 
5357  // Line title
5358  $formconfirm .= '<tr class="validtitre"><td class="validtitre" colspan="2">';
5359  $formconfirm .= img_picto('', 'pictoconfirm').' '.$title;
5360  $formconfirm .= '</td></tr>'."\n";
5361 
5362  // Line text
5363  if (is_array($formquestion) && !empty($formquestion['text'])) {
5364  $formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'.$formquestion['text'].'</td></tr>'."\n";
5365  }
5366 
5367  // Line form fields
5368  if ($more) {
5369  $formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'."\n";
5370  $formconfirm .= $more;
5371  $formconfirm .= '</td></tr>'."\n";
5372  }
5373 
5374  // Line with question
5375  $formconfirm .= '<tr class="valid">';
5376  $formconfirm .= '<td class="valid">'.$question.'</td>';
5377  $formconfirm .= '<td class="valid center">';
5378  $formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly', $labelbuttonyes, $labelbuttonno);
5379  $formconfirm .= '<input class="button valignmiddle confirmvalidatebutton small" type="submit" value="'.$langs->trans("Validate").'">';
5380  $formconfirm .= '</td>';
5381  $formconfirm .= '</tr>'."\n";
5382 
5383  $formconfirm .= '</table>'."\n";
5384 
5385  if (empty($disableformtag)) {
5386  $formconfirm .= "</form>\n";
5387  }
5388  $formconfirm .= '<br>';
5389 
5390  if (!empty($conf->use_javascript_ajax)) {
5391  $formconfirm .= '<!-- code to disable button to avoid double clic -->';
5392  $formconfirm .= '<script type="text/javascript">'."\n";
5393  $formconfirm .= '
5394  $(document).ready(function () {
5395  $(".confirmvalidatebutton").on("click", function() {
5396  console.log("We click on button");
5397  $(this).attr("disabled", "disabled");
5398  setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
5399  //console.log($(this).closest("form"));
5400  $(this).closest("form").submit();
5401  });
5402  });
5403  ';
5404  $formconfirm .= '</script>'."\n";
5405  }
5406 
5407  $formconfirm .= "<!-- end formconfirm -->\n";
5408  }
5409 
5410  return $formconfirm;
5411  }
5412 
5413 
5414  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5430  public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0, $textifnoproject = '', $morecss = '')
5431  {
5432  // phpcs:enable
5433  global $langs;
5434 
5435  require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
5436  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
5437 
5438  $out = '';
5439 
5440  $formproject = new FormProjets($this->db);
5441 
5442  $langs->load("project");
5443  if ($htmlname != "none") {
5444  $out .= '<form method="post" action="'.$page.'">';
5445  $out .= '<input type="hidden" name="action" value="classin">';
5446  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
5447  $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1, 0, $morecss);
5448  $out .= '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5449  $out .= '</form>';
5450  } else {
5451  $out .= '<span class="project_head_block">';
5452  if ($selected) {
5453  $projet = new Project($this->db);
5454  $projet->fetch($selected);
5455  $out .= $projet->getNomUrl(0, '', 1);
5456  } else {
5457  $out .= '<span class="opacitymedium">'.$textifnoproject.'</span>';
5458  }
5459  $out .= '</span>';
5460  }
5461 
5462  if (empty($nooutput)) {
5463  print $out;
5464  return '';
5465  }
5466  return $out;
5467  }
5468 
5469  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5485  public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0, $type = '', $filtertype = -1, $deposit_percent = -1, $nooutput = 0)
5486  {
5487  // phpcs:enable
5488  global $langs;
5489 
5490  $out = '';
5491 
5492  if ($htmlname != "none") {
5493  $out .= '<form method="POST" action="'.$page.'">';
5494  $out .= '<input type="hidden" name="action" value="setconditions">';
5495  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
5496  if ($type) {
5497  $out .= '<input type="hidden" name="type" value="'.dol_escape_htmltag($type).'">';
5498  }
5499  $out .= $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, 0, '', $deposit_percent);
5500  $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
5501  $out .= '</form>';
5502  } else {
5503  if ($selected) {
5505  if (isset($this->cache_conditions_paiements[$selected])) {
5506  $label = $this->cache_conditions_paiements[$selected]['label'];
5507 
5508  if (!empty($this->cache_conditions_paiements[$selected]['deposit_percent'])) {
5509  $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $this->cache_conditions_paiements[$selected]['deposit_percent'], $label);
5510  }
5511 
5512  $out .= $label;
5513  } else {
5514  $langs->load('errors');
5515  $out .= $langs->trans('ErrorNotInDictionaryPaymentConditions');
5516  }
5517  } else {
5518  $out .= '&nbsp;';
5519  }
5520  }
5521 
5522  if (empty($nooutput)) {
5523  print $out;
5524  return '';
5525  }
5526  return $out;
5527  }
5528 
5529  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5539  public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
5540  {
5541  // phpcs:enable
5542  global $langs;
5543  if ($htmlname != "none") {
5544  print '<form method="post" action="'.$page.'">';
5545  print '<input type="hidden" name="action" value="setavailability">';
5546  print '<input type="hidden" name="token" value="'.newToken().'">';
5547  $this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty);
5548  print '<input type="submit" name="modify" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5549  print '<input type="submit" name="cancel" class="button smallpaddingimp" value="'.$langs->trans("Cancel").'">';
5550  print '</form>';
5551  } else {
5552  if ($selected) {
5553  $this->load_cache_availability();
5554  print $this->cache_availability[$selected]['label'];
5555  } else {
5556  print "&nbsp;";
5557  }
5558  }
5559  }
5560 
5571  public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0)
5572  {
5573  global $langs;
5574  if ($htmlname != "none") {
5575  print '<form method="post" action="'.$page.'">';
5576  print '<input type="hidden" name="action" value="setdemandreason">';
5577  print '<input type="hidden" name="token" value="'.newToken().'">';
5578  $this->selectInputReason($selected, $htmlname, -1, $addempty);
5579  print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5580  print '</form>';
5581  } else {
5582  if ($selected) {
5583  $this->loadCacheInputReason();
5584  foreach ($this->cache_demand_reason as $key => $val) {
5585  if ($val['id'] == $selected) {
5586  print $val['label'];
5587  break;
5588  }
5589  }
5590  } else {
5591  print "&nbsp;";
5592  }
5593  }
5594  }
5595 
5596  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5610  public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0, $type = '')
5611  {
5612  // phpcs:enable
5613  global $langs;
5614 
5615  $ret = '';
5616 
5617  if ($htmlname != "none") {
5618  $ret .= '<form method="POST" action="'.$page.'" name="form'.$htmlname.'">';
5619  $ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
5620  $ret .= '<input type="hidden" name="token" value="'.newToken().'">';
5621  if ($type) {
5622  $ret .= '<input type="hidden" name="type" value="'.dol_escape_htmltag($type).'">';
5623  }
5624  $ret .= '<table class="nobordernopadding">';
5625  $ret .= '<tr><td>';
5626  $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form'.$htmlname, 1, 0);
5627  $ret .= '</td>';
5628  $ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5629  $ret .= '</tr></table></form>';
5630  } else {
5631  if ($displayhour) {
5632  $ret .= dol_print_date($selected, 'dayhour');
5633  } else {
5634  $ret .= dol_print_date($selected, 'day');
5635  }
5636  }
5637 
5638  if (empty($nooutput)) {
5639  print $ret;
5640  }
5641  return $ret;
5642  }
5643 
5644 
5645  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5656  public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = '', $include = '')
5657  {
5658  // phpcs:enable
5659  global $langs;
5660 
5661  if ($htmlname != "none") {
5662  print '<form method="POST" action="'.$page.'" name="form'.$htmlname.'">';
5663  print '<input type="hidden" name="action" value="set'.$htmlname.'">';
5664  print '<input type="hidden" name="token" value="'.newToken().'">';
5665  print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
5666  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5667  print '</form>';
5668  } else {
5669  if ($selected) {
5670  require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
5671  $theuser = new User($this->db);
5672  $theuser->fetch($selected);
5673  print $theuser->getNomUrl(1);
5674  } else {
5675  print "&nbsp;";
5676  }
5677  }
5678  }
5679 
5680 
5681  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5695  public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0, $type = '', $nooutput = 0)
5696  {
5697  // phpcs:enable
5698  global $langs;
5699 
5700  $out = '';
5701  if ($htmlname != "none") {
5702  $out .= '<form method="POST" action="'.$page.'">';
5703  $out .= '<input type="hidden" name="action" value="setmode">';
5704  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
5705  if ($type) {
5706  $out .= '<input type="hidden" name="type" value="'.dol_escape_htmltag($type).'">';
5707  }
5708  $out .= $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active, '', 1);
5709  $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5710  $out .= '</form>';
5711  } else {
5712  if ($selected) {
5713  $this->load_cache_types_paiements();
5714  $out .= $this->cache_types_paiements[$selected]['label'];
5715  } else {
5716  $out .= "&nbsp;";
5717  }
5718  }
5719 
5720  if ($nooutput) {
5721  return $out;
5722  } else {
5723  print $out;
5724  }
5725  }
5726 
5737  public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
5738  {
5739  global $langs;
5740  if ($htmlname != "none") {
5741  print '<form method="POST" action="'.$page.'">';
5742  print '<input type="hidden" name="action" value="settransportmode">';
5743  print '<input type="hidden" name="token" value="'.newToken().'">';
5744  $this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
5745  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5746  print '</form>';
5747  } else {
5748  if ($selected) {
5749  $this->load_cache_transport_mode();
5750  print $this->cache_transport_mode[$selected]['label'];
5751  } else {
5752  print "&nbsp;";
5753  }
5754  }
5755  }
5756 
5757  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5766  public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
5767  {
5768  // phpcs:enable
5769  global $langs;
5770  if ($htmlname != "none") {
5771  print '<form method="POST" action="'.$page.'">';
5772  print '<input type="hidden" name="action" value="setmulticurrencycode">';
5773  print '<input type="hidden" name="token" value="'.newToken().'">';
5774  print $this->selectMultiCurrency($selected, $htmlname, 0);
5775  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5776  print '</form>';
5777  } else {
5778  dol_include_once('/core/lib/company.lib.php');
5779  print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
5780  }
5781  }
5782 
5783  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5793  public function form_multicurrency_rate($page, $rate = '', $htmlname = 'multicurrency_tx', $currency = '')
5794  {
5795  // phpcs:enable
5796  global $langs, $mysoc, $conf;
5797 
5798  if ($htmlname != "none") {
5799  print '<form method="POST" action="'.$page.'">';
5800  print '<input type="hidden" name="action" value="setmulticurrencyrate">';
5801  print '<input type="hidden" name="token" value="'.newToken().'">';
5802  print '<input type="text" class="maxwidth100" name="'.$htmlname.'" value="'.(!empty($rate) ? price(price2num($rate, 'CU')) : 1).'" /> ';
5803  print '<select name="calculation_mode">';
5804  print '<option value="1">Change '.$langs->trans("PriceUHT").' of lines</option>';
5805  print '<option value="2">Change '.$langs->trans("PriceUHTCurrency").' of lines</option>';
5806  print '</select> ';
5807  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5808  print '</form>';
5809  } else {
5810  if (!empty($rate)) {
5811  print price($rate, 1, $langs, 1, 0);
5812  if ($currency && $rate != 1) {
5813  print ' &nbsp; ('.price($rate, 1, $langs, 1, 0).' '.$currency.' = 1 '.$conf->currency.')';
5814  }
5815  } else {
5816  print 1;
5817  }
5818  }
5819  }
5820 
5821 
5822  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5838  public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0)
5839  {
5840  // phpcs:enable
5841  global $conf, $langs;
5842  if ($htmlname != "none") {
5843  print '<form method="post" action="'.$page.'">';
5844  print '<input type="hidden" name="action" value="setabsolutediscount">';
5845  print '<input type="hidden" name="token" value="'.newToken().'">';
5846  print '<div class="inline-block">';
5847  if (!empty($discount_type)) {
5848  if (!empty($conf->global->FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS)) {
5849  if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
5850  $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be substracted to payments only and not to total of final invoice
5851  } else {
5852  $translationKey = 'HasCreditNoteFromSupplier';
5853  }
5854  } else {
5855  if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5856  $translationKey = 'HasAbsoluteDiscountFromSupplier';
5857  } else {
5858  $translationKey = 'HasCreditNoteFromSupplier';
5859  }
5860  }
5861  } else {
5862  if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
5863  if (!$filter || $filter == "fk_facture_source IS NULL") {
5864  $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be substracted to payments only and not to total of final invoice
5865  } else {
5866  $translationKey = 'CompanyHasCreditNote';
5867  }
5868  } else {
5869  if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5870  $translationKey = 'CompanyHasAbsoluteDiscount';
5871  } else {
5872  $translationKey = 'CompanyHasCreditNote';
5873  }
5874  }
5875  }
5876  print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
5877  if (empty($hidelist)) {
5878  print ' ';
5879  }
5880  print '</div>';
5881  if (empty($hidelist)) {
5882  print '<div class="inline-block" style="padding-right: 10px">';
5883  $newfilter = 'discount_type='.intval($discount_type);
5884  if (!empty($discount_type)) {
5885  $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
5886  } else {
5887  $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
5888  }
5889  if ($filter) {
5890  $newfilter .= ' AND ('.$filter.')';
5891  }
5892  // output the combo of discounts
5893  $nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue);
5894  if ($nbqualifiedlines > 0) {
5895  print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="'.dol_escape_htmltag($langs->trans("UseLine")).'"';
5896  if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5897  print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5898  }
5899  if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5900  print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5901  }
5902 
5903  print '>';
5904  }
5905  print '</div>';
5906  }
5907  if ($more) {
5908  print '<div class="inline-block">';
5909  print $more;
5910  print '</div>';
5911  }
5912  print '</form>';
5913  } else {
5914  if ($selected) {
5915  print $selected;
5916  } else {
5917  print "0";
5918  }
5919  }
5920  }
5921 
5922 
5923  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5933  public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
5934  {
5935  // phpcs:enable
5936  global $langs, $conf;
5937 
5938  if ($htmlname != "none") {
5939  print '<form method="post" action="'.$page.'">';
5940  print '<input type="hidden" name="action" value="set_contact">';
5941  print '<input type="hidden" name="token" value="'.newToken().'">';
5942  print '<table class="nobordernopadding">';
5943  print '<tr><td>';
5944  print $this->selectcontacts($societe->id, $selected, $htmlname);
5945  $num = $this->num;
5946  if ($num == 0) {
5947  $addcontact = (!empty($conf->global->SOCIETE_ADDRESSES_MANAGEMENT) ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
5948  print '<a href="'.DOL_URL_ROOT.'/contact/card.php?socid='.$societe->id.'&amp;action=create&amp;backtoreferer=1">'.$addcontact.'</a>';
5949  }
5950  print '</td>';
5951  print '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5952  print '</tr></table></form>';
5953  } else {
5954  if ($selected) {
5955  require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
5956  $contact = new Contact($this->db);
5957  $contact->fetch($selected);
5958  print $contact->getFullName($langs);
5959  } else {
5960  print "&nbsp;";
5961  }
5962  }
5963  }
5964 
5965  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5982  public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array(), $textifnothirdparty = '')
5983  {
5984  // phpcs:enable
5985  global $langs;
5986 
5987  $out = '';
5988  if ($htmlname != "none") {
5989  $out .= '<form method="post" action="'.$page.'">';
5990  $out .= '<input type="hidden" name="action" value="set_thirdparty">';
5991  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
5992  $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, 0, 'minwidth100', '', '', 1, array(), false, $excludeids);
5993  $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5994  $out .= '</form>';
5995  } else {
5996  if ($selected) {
5997  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
5998  $soc = new Societe($this->db);
5999  $soc->fetch($selected);
6000  $out .= $soc->getNomUrl(0, '');
6001  } else {
6002  $out .= '<span class="opacitymedium">'.$textifnothirdparty.'</span>';
6003  }
6004  }
6005 
6006  if ($nooutput) {
6007  return $out;
6008  } else {
6009  print $out;
6010  }
6011  }
6012 
6013  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6022  public function select_currency($selected = '', $htmlname = 'currency_id')
6023  {
6024  // phpcs:enable
6025  print $this->selectCurrency($selected, $htmlname);
6026  }
6027 
6037  public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0, $useempty = '')
6038  {
6039  global $conf, $langs, $user;
6040 
6041  $langs->loadCacheCurrencies('');
6042 
6043  $out = '';
6044 
6045  if ($selected == 'euro' || $selected == 'euros') {
6046  $selected = 'EUR'; // Pour compatibilite
6047  }
6048 
6049  $out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="'.$htmlname.'" id="'.$htmlname.'">';
6050  if ($useempty) {
6051  $out .= '<option value="-1" selected></option>';
6052  }
6053  foreach ($langs->cache_currencies as $code_iso => $currency) {
6054  $labeltoshow = $currency['label'];
6055  if ($mode == 1) {
6056  $labeltoshow .= ' <span class="opacitymedium">('.$code_iso.')</span>';
6057  } else {
6058  $labeltoshow .= ' <span class="opacitymedium">('.$langs->getCurrencySymbol($code_iso).')</span>';
6059  }
6060 
6061  if ($selected && $selected == $code_iso) {
6062  $out .= '<option value="'.$code_iso.'" selected data-html="'.dol_escape_htmltag($labeltoshow).'">';
6063  } else {
6064  $out .= '<option value="'.$code_iso.'" data-html="'.dol_escape_htmltag($labeltoshow).'">';
6065  }
6066  $out .= $labeltoshow;
6067  $out .= '</option>';
6068  }
6069  $out .= '</select>';
6070  if ($user->admin) {
6071  $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
6072  }
6073 
6074  // Make select dynamic
6075  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6076  $out .= ajax_combobox($htmlname);
6077 
6078  return $out;
6079  }
6080 
6093  public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false, $morecss = '')
6094  {
6095  global $conf, $langs;
6096 
6097  $langs->loadCacheCurrencies(''); // Load ->cache_currencies
6098 
6099  $TCurrency = array();
6100 
6101  $sql = "SELECT code FROM ".$this->db->prefix()."multicurrency";
6102  $sql .= " WHERE entity IN ('".getEntity('mutlicurrency')."')";
6103  if ($filter) {
6104  $sql .= " AND ".$filter;
6105  }
6106  $resql = $this->db->query($sql);
6107  if ($resql) {
6108  while ($obj = $this->db->fetch_object($resql)) {
6109  $TCurrency[$obj->code] = $obj->code;
6110  }
6111  }
6112 
6113  $out = '';
6114  $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
6115  if ($useempty) {
6116  $out .= '<option value="">&nbsp;</option>';
6117  }
6118  // If company current currency not in table, we add it into list. Should always be available.
6119  if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
6120  $TCurrency[$conf->currency] = $conf->currency;
6121  }
6122  if (count($TCurrency) > 0) {
6123  foreach ($langs->cache_currencies as $code_iso => $currency) {
6124  if (isset($TCurrency[$code_iso])) {
6125  if (!empty($selected) && $selected == $code_iso) {
6126  $out .= '<option value="'.$code_iso.'" selected="selected">';
6127  } else {
6128  $out .= '<option value="'.$code_iso.'">';
6129  }
6130 
6131  $out .= $currency['label'];
6132  $out .= ' ('.$langs->getCurrencySymbol($code_iso).')';
6133  $out .= '</option>';
6134  }
6135  }
6136  }
6137 
6138  $out .= '</select>';
6139 
6140  // Make select dynamic
6141  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6142  $out .= ajax_combobox($htmlname);
6143 
6144  return $out;
6145  }
6146 
6147  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6154  public function load_cache_vatrates($country_code)
6155  {
6156  // phpcs:enable
6157  global $langs;
6158 
6159  $num = count($this->cache_vatrates);
6160  if ($num > 0) {
6161  return $num; // Cache already loaded
6162  }
6163 
6164  dol_syslog(__METHOD__, LOG_DEBUG);
6165 
6166  $sql = "SELECT DISTINCT t.rowid, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly";
6167  $sql .= " FROM ".$this->db->prefix()."c_tva as t, ".$this->db->prefix()."c_country as c";
6168  $sql .= " WHERE t.fk_pays = c.rowid";
6169  $sql .= " AND t.active > 0";
6170  $sql .= " AND c.code IN (".$this->db->sanitize($country_code, 1).")";
6171  $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
6172 
6173  $resql = $this->db->query($sql);
6174  if ($resql) {
6175  $num = $this->db->num_rows($resql);
6176  if ($num) {
6177  for ($i = 0; $i < $num; $i++) {
6178  $obj = $this->db->fetch_object($resql);
6179  $this->cache_vatrates[$i]['rowid'] = $obj->rowid;
6180  $this->cache_vatrates[$i]['code'] = $obj->code;
6181  $this->cache_vatrates[$i]['txtva'] = $obj->taux;
6182  $this->cache_vatrates[$i]['nprtva'] = $obj->recuperableonly;
6183  $this->cache_vatrates[$i]['localtax1'] = $obj->localtax1;
6184  $this->cache_vatrates[$i]['localtax1_type'] = $obj->localtax1_type;
6185  $this->cache_vatrates[$i]['localtax2'] = $obj->localtax2;
6186  $this->cache_vatrates[$i]['localtax2_type'] = $obj->localtax1_type;
6187 
6188  $this->cache_vatrates[$i]['label'] = $obj->taux.'%'.($obj->code ? ' ('.$obj->code.')' : ''); // Label must contains only 0-9 , . % or *
6189  $this->cache_vatrates[$i]['labelallrates'] = $obj->taux.'/'.($obj->localtax1 ? $obj->localtax1 : '0').'/'.($obj->localtax2 ? $obj->localtax2 : '0').($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label
6190  $positiverates = '';
6191  if ($obj->taux) {
6192  $positiverates .= ($positiverates ? '/' : '').$obj->taux;
6193  }
6194  if ($obj->localtax1) {
6195  $positiverates .= ($positiverates ? '/' : '').$obj->localtax1;
6196  }
6197  if ($obj->localtax2) {
6198  $positiverates .= ($positiverates ? '/' : '').$obj->localtax2;
6199  }
6200  if (empty($positiverates)) {
6201  $positiverates = '0';
6202  }
6203  $this->cache_vatrates[$i]['labelpositiverates'] = $positiverates.($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label
6204  }
6205 
6206  return $num;
6207  } else {
6208  $this->error = '<span class="error">'.$langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code).'</span>';
6209  return -1;
6210  }
6211  } else {
6212  $this->error = '<span class="error">'.$this->db->error().'</span>';
6213  return -2;
6214  }
6215  }
6216 
6217  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6239  public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = '', $societe_acheteuse = '', $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0)
6240  {
6241  // phpcs:enable
6242  global $langs, $conf, $mysoc;
6243 
6244  $langs->load('errors');
6245 
6246  $return = '';
6247 
6248  // Define defaultnpr, defaultttx and defaultcode
6249  $defaultnpr = ($info_bits & 0x01);
6250  $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
6251  $defaulttx = str_replace('*', '', $selectedrate);
6252  $defaultcode = '';
6253  $reg = array();
6254  if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
6255  $defaultcode = $reg[1];
6256  $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
6257  }
6258  //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
6259 
6260  // Check parameters
6261  if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
6262  if ($societe_vendeuse->id == $mysoc->id) {
6263  $return .= '<span class="error">'.$langs->trans("ErrorYourCountryIsNotDefined").'</span>';
6264  } else {
6265  $return .= '<span class="error">'.$langs->trans("ErrorSupplierCountryIsNotDefined").'</span>';
6266  }
6267  return $return;
6268  }
6269 
6270  //var_dump($societe_acheteuse);
6271  //print "name=$name, selectedrate=$selectedrate, seller=".$societe_vendeuse->country_code." buyer=".$societe_acheteuse->country_code." buyer is company=".$societe_acheteuse->isACompany()." idprod=$idprod, info_bits=$info_bits type=$type";
6272  //exit;
6273 
6274  // Define list of countries to use to search VAT rates to show
6275  // First we defined code_country to use to find list
6276  if (is_object($societe_vendeuse)) {
6277  $code_country = "'".$societe_vendeuse->country_code."'";
6278  } else {
6279  $code_country = "'".$mysoc->country_code."'"; // Pour compatibilite ascendente
6280  }
6281  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) { // If option to have vat for end customer for services is on
6282  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6283  if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) {
6284  // We also add the buyer country code
6285  if (is_numeric($type)) {
6286  if ($type == 1) { // We know product is a service
6287  $code_country .= ",'".$societe_acheteuse->country_code."'";
6288  }
6289  } elseif (!$idprod) { // We don't know type of product
6290  $code_country .= ",'".$societe_acheteuse->country_code."'";
6291  } else {
6292  $prodstatic = new Product($this->db);
6293  $prodstatic->fetch($idprod);
6294  if ($prodstatic->type == Product::TYPE_SERVICE) { // We know product is a service
6295  $code_country .= ",'".$societe_acheteuse->country_code."'";
6296  }
6297  }
6298  }
6299  }
6300 
6301  // Now we get list
6302  $num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
6303 
6304  if ($num > 0) {
6305  // Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '')
6306  if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
6307  $tmpthirdparty = new Societe($this->db);
6308 
6309  $defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
6310  $defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
6311 
6312  if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
6313  $defaultcode = $reg[1];
6314  $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
6315  }
6316  if (empty($defaulttx)) {
6317  $defaultnpr = 0;
6318  }
6319  }
6320 
6321  // If we fails to find a default vat rate, we take the last one in list
6322  // Because they are sorted in ascending order, the last one will be the higher one (we suppose the higher one is the current rate)
6323  if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
6324  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6325  // We take the last one found in list
6326  $defaulttx = $this->cache_vatrates[$num - 1]['txtva'];
6327  } else {
6328  // We will use the rate defined into MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS
6329  $defaulttx = '';
6330  if ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS != 'none') {
6331  $defaulttx = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;
6332  }
6333  if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
6334  $defaultcode = $reg[1];
6335  $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
6336  }
6337  }
6338  }
6339 
6340  // Disabled if seller is not subject to VAT
6341  $disabled = false;
6342  $title = '';
6343  if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0") {
6344  // Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead
6345  // of using supplier invoices (this is a very bad idea !)
6346  if (empty($conf->global->EXPENSEREPORT_OVERRIDE_VAT)) {
6347  $title = ' title="'.dol_escape_htmltag($langs->trans('VATIsNotUsed')).'"';
6348  $disabled = true;
6349  }
6350  }
6351 
6352  if (!$options_only) {
6353  $return .= '<select class="flat minwidth50imp maxwidth100" id="'.$htmlname.'" name="'.$htmlname.'"'.($disabled ? ' disabled' : '').$title.'>';
6354  }
6355 
6356  $selectedfound = false;
6357  foreach ($this->cache_vatrates as $rate) {
6358  // Keep only 0 if seller is not subject to VAT
6359  if ($disabled && $rate['txtva'] != 0) {
6360  continue;
6361  }
6362 
6363  // Define key to use into select list
6364  $key = $rate['txtva'];
6365  $key .= $rate['nprtva'] ? '*' : '';
6366  if ($mode > 0 && $rate['code']) {
6367  $key .= ' ('.$rate['code'].')';
6368  }
6369  if ($mode < 0) {
6370  $key = $rate['rowid'];
6371  }
6372 
6373  $return .= '<option value="'.$key.'"';
6374  if (!$selectedfound) {
6375  if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
6376  if ($defaultcode == $rate['code']) {
6377  $return .= ' selected';
6378  $selectedfound = true;
6379  }
6380  } elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
6381  $return .= ' selected';
6382  $selectedfound = true;
6383  }
6384  }
6385  $return .= '>';
6386 
6387  // Show label of VAT
6388  if ($mysoc->country_code == 'IN' || !empty($conf->global->MAIN_VAT_LABEL_IS_POSITIVE_RATES)) {
6389  // Label with all localtax and code. For example: x.y / a.b / c.d (CODE)'
6390  $return .= $rate['labelpositiverates'];
6391  } else {
6392  // Simple label
6393  $return .= vatrate($rate['label']);
6394  }
6395 
6396  //$return.=($rate['code']?' '.$rate['code']:'');
6397  $return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the * (old behaviour only if new vat code is not used)
6398 
6399  $return .= '</option>';
6400  }
6401 
6402  if (!$options_only) {
6403  $return .= '</select>';
6404  //$return .= ajax_combobox($htmlname); // This break for the moment the dynamic autoselection of a value when selecting a product in object lines
6405  }
6406  } else {
6407  $return .= $this->error;
6408  }
6409 
6410  $this->num = $num;
6411  return $return;
6412  }
6413 
6414 
6415  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6440  public function select_date($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $nooutput = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '')
6441  {
6442  // phpcs:enable
6443  $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
6444  if (!empty($nooutput)) {
6445  return $retstring;
6446  }
6447  print $retstring;
6448  return;
6449  }
6450 
6466  public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0, $forcenewline = 0)
6467  {
6468  global $langs;
6469 
6470  $ret = $this->selectDate($set_time, $prefix.'_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
6471  if ($forcenewline) {
6472  $ret .= '<br>';
6473  }
6474  $ret .= $this->selectDate($set_time_end, $prefix.'_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
6475  return $ret;
6476  }
6477 
6505  public function selectDate($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '', $openinghours = '', $stepminutes = 1, $labeladddateof = '', $placeholder = '', $gm = 'auto')
6506  {
6507  global $conf, $langs;
6508 
6509  if ($gm === 'auto') {
6510  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
6511  }
6512 
6513  $retstring = '';
6514 
6515  if ($prefix == '') {
6516  $prefix = 're';
6517  }
6518  if ($h == '') {
6519  $h = 0;
6520  }
6521  if ($m == '') {
6522  $m = 0;
6523  }
6524  $emptydate = 0;
6525  $emptyhours = 0;
6526  if ($stepminutes <= 0 || $stepminutes > 30) {
6527  $stepminutes = 1;
6528  }
6529  if ($empty == 1) {
6530  $emptydate = 1;
6531  $emptyhours = 1;
6532  }
6533  if ($empty == 2) {
6534  $emptydate = 0;
6535  $emptyhours = 1;
6536  }
6537  $orig_set_time = $set_time;
6538 
6539  if ($set_time === '' && $emptydate == 0) {
6540  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6541  if ($gm == 'tzuser' || $gm == 'tzuserrel') {
6542  $set_time = dol_now($gm);
6543  } else {
6544  $set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
6545  }
6546  }
6547 
6548  // Analysis of the pre-selection date
6549  $reg = array();
6550  $shour = '';
6551  $smin = '';
6552  $ssec = '';
6553  if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', $set_time, $reg)) { // deprecated usage
6554  // Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
6555  $syear = (!empty($reg[1]) ? $reg[1] : '');
6556  $smonth = (!empty($reg[2]) ? $reg[2] : '');
6557  $sday = (!empty($reg[3]) ? $reg[3] : '');
6558  $shour = (!empty($reg[4]) ? $reg[4] : '');
6559  $smin = (!empty($reg[5]) ? $reg[5] : '');
6560  } elseif (strval($set_time) != '' && $set_time != -1) {
6561  // set_time est un timestamps (0 possible)
6562  $syear = dol_print_date($set_time, "%Y", $gm);
6563  $smonth = dol_print_date($set_time, "%m", $gm);
6564  $sday = dol_print_date($set_time, "%d", $gm);
6565  if ($orig_set_time != '') {
6566  $shour = dol_print_date($set_time, "%H", $gm);
6567  $smin = dol_print_date($set_time, "%M", $gm);
6568  $ssec = dol_print_date($set_time, "%S", $gm);
6569  }
6570  } else {
6571  // Date est '' ou vaut -1
6572  $syear = '';
6573  $smonth = '';
6574  $sday = '';
6575  $shour = !isset($conf->global->MAIN_DEFAULT_DATE_HOUR) ? ($h == -1 ? '23' : '') : $conf->global->MAIN_DEFAULT_DATE_HOUR;
6576  $smin = !isset($conf->global->MAIN_DEFAULT_DATE_MIN) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_MIN;
6577  $ssec = !isset($conf->global->MAIN_DEFAULT_DATE_SEC) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_SEC;
6578  }
6579  if ($h == 3) {
6580  $shour = '';
6581  }
6582  if ($m == 3) {
6583  $smin = '';
6584  }
6585 
6586  $nowgmt = dol_now('gmt');
6587  //var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
6588 
6589  // You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
6590  $usecalendar = 'combo';
6591  if (!empty($conf->use_javascript_ajax) && (empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR != "none")) {
6592  $usecalendar = ((empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR == 'eldy') ? 'jquery' : $conf->global->MAIN_POPUP_CALENDAR);
6593  }
6594 
6595  if ($d) {
6596  // Show date with popup
6597  if ($usecalendar != 'combo') {
6598  $formated_date = '';
6599  //print "e".$set_time." t ".$conf->format_date_short;
6600  if (strval($set_time) != '' && $set_time != -1) {
6601  //$formated_date=dol_print_date($set_time,$conf->format_date_short);
6602  $formated_date = dol_print_date($set_time, $langs->trans("FormatDateShortInput"), $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6603  }
6604 
6605  // Calendrier popup version eldy
6606  if ($usecalendar == "eldy") {
6607  // Input area to enter date manually
6608  $retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
6609  $retstring .= ($disabled ? ' disabled' : '');
6610  $retstring .= ' onChange="dpChangeDay(\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6611  $retstring .= '>';
6612 
6613  // Icon calendar
6614  $retstringbuttom = '';
6615  if (!$disabled) {
6616  $retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons"';
6617  $base = DOL_URL_ROOT.'/core/';
6618  $retstringbuttom .= ' onClick="showDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');"';
6619  $retstringbuttom .= '>'.img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink"').'</button>';
6620  } else {
6621  $retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'cl