dolibarr  16.0.5
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  *
25  * This program is free software; you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License as published by
27  * the Free Software Foundation; either version 3 of the License, or
28  * (at your option) any later version.
29  *
30  * This program is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU General Public License
36  * along with this program. If not, see <https://www.gnu.org/licenses/>.
37  */
38 
52 class Form
53 {
57  public $db;
58 
62  public $error = '';
63 
67  public $errors = array();
68 
69  public $num;
70 
71  // Cache arrays
72  public $cache_types_paiements = array();
73  public $cache_conditions_paiements = array();
74  public $cache_transport_mode = array();
75  public $cache_availability = array();
76  public $cache_demand_reason = array();
77  public $cache_types_fees = array();
78  public $cache_vatrates = array();
79 
80 
86  public function __construct($db)
87  {
88  $this->db = $db;
89  }
90 
107  public function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata = 'string', $moreparam = '', $fieldrequired = 0, $notabletag = 0, $paramid = 'id', $help = '')
108  {
109  global $conf, $langs;
110 
111  $ret = '';
112 
113  // TODO change for compatibility
114  if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;/', $typeofdata)) {
115  if (!empty($perm)) {
116  $tmp = explode(':', $typeofdata);
117  $ret .= '<div class="editkey_'.$tmp[0].(!empty($tmp[1]) ? ' '.$tmp[1] : '').'" id="'.$htmlname.'">';
118  if ($fieldrequired) {
119  $ret .= '<span class="fieldrequired">';
120  }
121  if ($help) {
122  $ret .= $this->textwithpicto($langs->trans($text), $help);
123  } else {
124  $ret .= $langs->trans($text);
125  }
126  if ($fieldrequired) {
127  $ret .= '</span>';
128  }
129  $ret .= '</div>'."\n";
130  } else {
131  if ($fieldrequired) {
132  $ret .= '<span class="fieldrequired">';
133  }
134  if ($help) {
135  $ret .= $this->textwithpicto($langs->trans($text), $help);
136  } else {
137  $ret .= $langs->trans($text);
138  }
139  if ($fieldrequired) {
140  $ret .= '</span>';
141  }
142  }
143  } else {
144  if (empty($notabletag) && $perm) {
145  $ret .= '<table class="nobordernopadding centpercent"><tr><td class="nowrap">';
146  }
147  if ($fieldrequired) {
148  $ret .= '<span class="fieldrequired">';
149  }
150  if ($help) {
151  $ret .= $this->textwithpicto($langs->trans($text), $help);
152  } else {
153  $ret .= $langs->trans($text);
154  }
155  if ($fieldrequired) {
156  $ret .= '</span>';
157  }
158  if (!empty($notabletag)) {
159  $ret .= ' ';
160  }
161  if (empty($notabletag) && $perm) {
162  $ret .= '</td>';
163  }
164  if (empty($notabletag) && $perm) {
165  $ret .= '<td class="right">';
166  }
167  if ($htmlname && GETPOST('action', 'aZ09') != 'edit'.$htmlname && $perm) {
168  $ret .= '<a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=edit'.$htmlname.'&token='.newToken().'&'.$paramid.'='.$object->id.$moreparam.'">'.img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1)).'</a>';
169  }
170  if (!empty($notabletag) && $notabletag == 1) {
171  $ret .= ' : ';
172  }
173  if (!empty($notabletag) && $notabletag == 3) {
174  $ret .= ' ';
175  }
176  if (empty($notabletag) && $perm) {
177  $ret .= '</td>';
178  }
179  if (empty($notabletag) && $perm) {
180  $ret .= '</tr></table>';
181  }
182  }
183 
184  return $ret;
185  }
186 
206  public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 0, $formatfunc = '', $paramid = 'id', $gm = 'auto')
207  {
208  global $conf, $langs;
209 
210  $ret = '';
211 
212  // Check parameters
213  if (empty($typeofdata)) {
214  return 'ErrorBadParameter';
215  }
216 
217  // When option to edit inline is activated
218  if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;|day|datepicker|dayhour|datehourpicker/', $typeofdata)) { // TODO add jquery timepicker and support select
219  $ret .= $this->editInPlace($object, $value, $htmlname, $perm, $typeofdata, $editvalue, $extObject, $custommsg);
220  } else {
221  $editmode = (GETPOST('action', 'aZ09') == 'edit'.$htmlname);
222  if ($editmode) {
223  $ret .= "\n";
224  $ret .= '<form method="post" action="'.$_SERVER["PHP_SELF"].($moreparam ? '?'.$moreparam : '').'">';
225  $ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
226  $ret .= '<input type="hidden" name="token" value="'.newToken().'">';
227  $ret .= '<input type="hidden" name="'.$paramid.'" value="'.$object->id.'">';
228  if (empty($notabletag)) {
229  $ret .= '<table class="nobordernopadding centpercent">';
230  }
231  if (empty($notabletag)) {
232  $ret .= '<tr><td>';
233  }
234  if (preg_match('/^(string|safehtmlstring|email)/', $typeofdata)) {
235  $tmp = explode(':', $typeofdata);
236  $ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.($editvalue ? $editvalue : $value).'"'.(empty($tmp[1]) ? '' : ' size="'.$tmp[1].'"').' autofocus>';
237  } elseif (preg_match('/^(integer)/', $typeofdata)) {
238  $tmp = explode(':', $typeofdata);
239  $valuetoshow = price2num($editvalue ? $editvalue : $value, 0);
240  $ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.$valuetoshow.'"'.(empty($tmp[1]) ? '' : ' size="'.$tmp[1].'"').' autofocus>';
241  } elseif (preg_match('/^(numeric|amount)/', $typeofdata)) {
242  $tmp = explode(':', $typeofdata);
243  $valuetoshow = price2num($editvalue ? $editvalue : $value);
244  $ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.($valuetoshow != '' ? price($valuetoshow) : '').'"'.(empty($tmp[1]) ? '' : ' size="'.$tmp[1].'"').' autofocus>';
245  } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
246  $tmp = explode(':', $typeofdata);
247  $ret .= '<input type="checkbox" id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . (empty($tmp[1]) ? '' : $tmp[1]) . '/>';
248  } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) { // if wysiwyg is enabled $typeofdata = 'ckeditor'
249  $tmp = explode(':', $typeofdata);
250  $cols = (empty($tmp[2]) ? '' : $tmp[2]);
251  $morealt = '';
252  if (preg_match('/%/', $cols)) {
253  $morealt = ' style="width: '.$cols.'"';
254  $cols = '';
255  }
256 
257  $valuetoshow = ($editvalue ? $editvalue : $value);
258  $ret .= '<textarea id="'.$htmlname.'" name="'.$htmlname.'" wrap="soft" rows="'.(empty($tmp[1]) ? '20' : $tmp[1]).'"'.($cols ? ' cols="'.$cols.'"' : 'class="quatrevingtpercent"').$morealt.'" autofocus>';
259  // textarea convert automatically entities chars into simple chars.
260  // 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.
261  $valuetoshow = str_replace('&', '&amp;', $valuetoshow);
262  $ret .= dol_string_neverthesehtmltags($valuetoshow, array('textarea'));
263  $ret .= '</textarea>';
264  } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
265  $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form'.$htmlname, 1, 0, 0, '', '', '', '', 1, '', '', $gm);
266  } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
267  $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form'.$htmlname, 1, 0, 0, '', '', '', '', 1, '', '', $gm);
268  } elseif (preg_match('/^select;/', $typeofdata)) {
269  $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
270  $arraylist = array();
271  foreach ($arraydata as $val) {
272  $tmp = explode(':', $val);
273  $tmpkey = str_replace('|', ':', $tmp[0]);
274  $arraylist[$tmpkey] = $tmp[1];
275  }
276  $ret .= $this->selectarray($htmlname, $arraylist, $value);
277  } elseif (preg_match('/^ckeditor/', $typeofdata)) {
278  $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser
279  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
280  $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]));
281  $ret .= $doleditor->Create(1);
282  }
283  if (empty($notabletag)) {
284  $ret .= '</td>';
285  }
286 
287  // Button save-cancel
288  if (empty($notabletag)) {
289  $ret .= '<td class="left">';
290  }
291  //else $ret.='<div class="clearboth"></div>';
292  $ret .= '<input type="submit" class="smallpaddingimp button'.(empty($notabletag) ? '' : ' ').'" name="modify" value="'.$langs->trans("Modify").'">';
293  if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) {
294  $ret .= '<br>'."\n";
295  }
296  $ret .= '<input type="submit" class="smallpaddingimp button button-cancel'.(empty($notabletag) ? '' : ' ').'" name="cancel" value="'.$langs->trans("Cancel").'">';
297  if (empty($notabletag)) {
298  $ret .= '</td>';
299  }
300 
301  if (empty($notabletag)) {
302  $ret .= '</tr></table>'."\n";
303  }
304  $ret .= '</form>'."\n";
305  } else {
306  if (preg_match('/^(email)/', $typeofdata)) {
307  $ret .= dol_print_email($value, 0, 0, 0, 0, 1);
308  } elseif (preg_match('/^(amount|numeric)/', $typeofdata)) {
309  $ret .= ($value != '' ? price($value, '', $langs, 0, -1, -1, $conf->currency) : '');
310  } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
311  $tmp = explode(':', $typeofdata);
312  $ret .= '<input type="checkbox" disabled id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($tmp[1] ? $tmp[1] : '') . '/>';
313  } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {
314  $ret .= dol_htmlentitiesbr($value);
315  } elseif (preg_match('/^safehtmlstring/', $typeofdata)) {
316  $ret .= dol_string_onlythesehtmltags($value);
317  } elseif (preg_match('/^restricthtml/', $typeofdata)) {
318  $ret .= dol_string_onlythesehtmltags($value);
319  } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
320  $ret .= '<span class="valuedate">'.dol_print_date($value, 'day', $gm).'</span>';
321  } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
322  $ret .= '<span class="valuedate">'.dol_print_date($value, 'dayhour', $gm).'</span>';
323  } elseif (preg_match('/^select;/', $typeofdata)) {
324  $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
325  $arraylist = array();
326  foreach ($arraydata as $val) {
327  $tmp = explode(':', $val);
328  $arraylist[$tmp[0]] = $tmp[1];
329  }
330  $ret .= $arraylist[$value];
331  if ($htmlname == 'fk_product_type') {
332  if ($value == 0) {
333  $ret = img_picto($langs->trans("Product"), 'product', 'class="paddingleftonly paddingrightonly colorgrey"').$ret;
334  } else {
335  $ret = img_picto($langs->trans("Service"), 'service', 'class="paddingleftonly paddingrightonly colorgrey"').$ret;
336  }
337  }
338  } elseif (preg_match('/^ckeditor/', $typeofdata)) {
339  $tmpcontent = dol_htmlentitiesbr($value);
340  if (!empty($conf->global->MAIN_DISABLE_NOTES_TAB)) {
341  $firstline = preg_replace('/<br>.*/', '', $tmpcontent);
342  $firstline = preg_replace('/[\n\r].*/', '', $firstline);
343  $tmpcontent = $firstline.((strlen($firstline) != strlen($tmpcontent)) ? '...' : '');
344  }
345  // We dont use dol_escape_htmltag to get the html formating active, but this need we must also
346  // clean data from some dangerous html
347  $ret .= dol_string_onlythesehtmltags(dol_htmlentitiesbr($tmpcontent));
348  } else {
349  $ret .= dol_escape_htmltag($value);
350  }
351 
352  if ($formatfunc && method_exists($object, $formatfunc)) {
353  $ret = $object->$formatfunc($ret);
354  }
355  }
356  }
357  return $ret;
358  }
359 
371  public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '')
372  {
373  global $conf, $langs, $extralanguages;
374 
375  $result = '';
376 
377  // List of extra languages
378  $arrayoflangcode = array();
379  if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) {
380  $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
381  }
382 
383  if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
384  if (!is_object($extralanguages)) {
385  include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php';
386  $extralanguages = new ExtraLanguages($this->db);
387  }
388  $extralanguages->fetch_name_extralanguages('societe');
389 
390  if (!is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) {
391  return ''; // No extralang field to show
392  }
393 
394  $result .= '<!-- Widget for translation -->'."\n";
395  $result .= '<div class="inline-block paddingleft image-'.$object->element.'-'.$fieldname.'">';
396  $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', false, 0, 0, '', 'fa-15 editfieldlang');
397  $result .= $s;
398  $result .= '</div>';
399 
400  $result .= '<div class="inline-block hidden field-'.$object->element.'-'.$fieldname.'">';
401 
402  $resultforextrlang = '';
403  foreach ($arrayoflangcode as $langcode) {
404  $valuetoshow = GETPOSTISSET('field-'.$object->element."-".$fieldname."-".$langcode) ? GETPOST('field-'.$object->element.'-'.$fieldname."-".$langcode, $check) : '';
405  if (empty($valuetoshow)) {
406  $object->fetchValuesForExtraLanguages();
407  //var_dump($object->array_languages);
408  $valuetoshow = $object->array_languages[$fieldname][$langcode];
409  }
410 
411  $s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"');
412  $resultforextrlang .= $s;
413 
414  // TODO Use the showInputField() method of ExtraLanguages object
415  if ($typeofdata == 'textarea') {
416  $resultforextrlang .= '<textarea name="field-'.$object->element."-".$fieldname."-".$langcode.'" id="'.$fieldname."-".$langcode.'" class="'.$morecss.'" rows="'.ROWS_2.'" wrap="soft">';
417  $resultforextrlang .= $valuetoshow;
418  $resultforextrlang .= '</textarea>';
419  } else {
420  $resultforextrlang .= '<input type="text" class="inputfieldforlang '.($morecss ? ' '.$morecss : '').'" name="field-'.$object->element.'-'.$fieldname.'-'.$langcode.'" value="'.$valuetoshow.'">';
421  }
422  }
423  $result .= $resultforextrlang;
424 
425  $result .= '</div>';
426  $result .= '<script>$(".image-'.$object->element.'-'.$fieldname.'").click(function() { console.log("Toggle lang widget"); jQuery(".field-'.$object->element.'-'.$fieldname.'").toggle(); });</script>';
427  }
428 
429  return $result;
430  }
431 
445  protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null)
446  {
447  global $conf;
448 
449  $out = '';
450 
451  // Check parameters
452  if (preg_match('/^text/', $inputType)) {
453  $value = dol_nl2br($value);
454  } elseif (preg_match('/^numeric/', $inputType)) {
455  $value = price($value);
456  } elseif ($inputType == 'day' || $inputType == 'datepicker') {
457  $value = dol_print_date($value, 'day');
458  }
459 
460  if ($condition) {
461  $element = false;
462  $table_element = false;
463  $fk_element = false;
464  $loadmethod = false;
465  $savemethod = false;
466  $ext_element = false;
467  $button_only = false;
468  $inputOption = '';
469  $rows = '';
470  $cols = '';
471 
472  if (is_object($object)) {
473  $element = $object->element;
474  $table_element = $object->table_element;
475  $fk_element = $object->id;
476  }
477 
478  if (is_object($extObject)) {
479  $ext_element = $extObject->element;
480  }
481 
482  if (preg_match('/^(string|email|numeric)/', $inputType)) {
483  $tmp = explode(':', $inputType);
484  $inputType = $tmp[0];
485  if (!empty($tmp[1])) {
486  $inputOption = $tmp[1];
487  }
488  if (!empty($tmp[2])) {
489  $savemethod = $tmp[2];
490  }
491  $out .= '<input id="width_'.$htmlname.'" value="'.$inputOption.'" type="hidden"/>'."\n";
492  } elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) {
493  $tmp = explode(':', $inputType);
494  $inputType = $tmp[0];
495  if (!empty($tmp[1])) {
496  $inputOption = $tmp[1];
497  }
498  if (!empty($tmp[2])) {
499  $savemethod = $tmp[2];
500  }
501 
502  $out .= '<input id="timestamp" type="hidden"/>'."\n"; // Use for timestamp format
503  } elseif (preg_match('/^(select|autocomplete)/', $inputType)) {
504  $tmp = explode(':', $inputType);
505  $inputType = $tmp[0];
506  $loadmethod = $tmp[1];
507  if (!empty($tmp[2])) {
508  $savemethod = $tmp[2];
509  }
510  if (!empty($tmp[3])) {
511  $button_only = true;
512  }
513  } elseif (preg_match('/^textarea/', $inputType)) {
514  $tmp = explode(':', $inputType);
515  $inputType = $tmp[0];
516  $rows = (empty($tmp[1]) ? '8' : $tmp[1]);
517  $cols = (empty($tmp[2]) ? '80' : $tmp[2]);
518  } elseif (preg_match('/^ckeditor/', $inputType)) {
519  $tmp = explode(':', $inputType);
520  $inputType = $tmp[0];
521  $toolbar = $tmp[1];
522  if (!empty($tmp[2])) {
523  $width = $tmp[2];
524  }
525  if (!empty($tmp[3])) {
526  $heigth = $tmp[3];
527  }
528  if (!empty($tmp[4])) {
529  $savemethod = $tmp[4];
530  }
531 
532  if (!empty($conf->fckeditor->enabled)) {
533  $out .= '<input id="ckeditor_toolbar" value="'.$toolbar.'" type="hidden"/>'."\n";
534  } else {
535  $inputType = 'textarea';
536  }
537  }
538 
539  $out .= '<input id="element_'.$htmlname.'" value="'.$element.'" type="hidden"/>'."\n";
540  $out .= '<input id="table_element_'.$htmlname.'" value="'.$table_element.'" type="hidden"/>'."\n";
541  $out .= '<input id="fk_element_'.$htmlname.'" value="'.$fk_element.'" type="hidden"/>'."\n";
542  $out .= '<input id="loadmethod_'.$htmlname.'" value="'.$loadmethod.'" type="hidden"/>'."\n";
543  if (!empty($savemethod)) {
544  $out .= '<input id="savemethod_'.$htmlname.'" value="'.$savemethod.'" type="hidden"/>'."\n";
545  }
546  if (!empty($ext_element)) {
547  $out .= '<input id="ext_element_'.$htmlname.'" value="'.$ext_element.'" type="hidden"/>'."\n";
548  }
549  if (!empty($custommsg)) {
550  if (is_array($custommsg)) {
551  if (!empty($custommsg['success'])) {
552  $out .= '<input id="successmsg_'.$htmlname.'" value="'.$custommsg['success'].'" type="hidden"/>'."\n";
553  }
554  if (!empty($custommsg['error'])) {
555  $out .= '<input id="errormsg_'.$htmlname.'" value="'.$custommsg['error'].'" type="hidden"/>'."\n";
556  }
557  } else {
558  $out .= '<input id="successmsg_'.$htmlname.'" value="'.$custommsg.'" type="hidden"/>'."\n";
559  }
560  }
561  if ($inputType == 'textarea') {
562  $out .= '<input id="textarea_'.$htmlname.'_rows" value="'.$rows.'" type="hidden"/>'."\n";
563  $out .= '<input id="textarea_'.$htmlname.'_cols" value="'.$cols.'" type="hidden"/>'."\n";
564  }
565  $out .= '<span id="viewval_'.$htmlname.'" class="viewval_'.$inputType.($button_only ? ' inactive' : ' active').'">'.$value.'</span>'."\n";
566  $out .= '<span id="editval_'.$htmlname.'" class="editval_'.$inputType.($button_only ? ' inactive' : ' active').' hideobject">'.(!empty($editvalue) ? $editvalue : $value).'</span>'."\n";
567  } else {
568  $out = $value;
569  }
570 
571  return $out;
572  }
573 
592  public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
593  {
594  if ($incbefore) {
595  $text = $incbefore.$text;
596  }
597  if (!$htmltext) {
598  return $text;
599  }
600  $direction = (int) $direction; // For backward compatibility when $direction was set to '' instead of 0
601 
602  $tag = 'td';
603  if ($notabs == 2) {
604  $tag = 'div';
605  }
606  if ($notabs == 3) {
607  $tag = 'span';
608  }
609  // Sanitize tooltip
610  $htmltext = str_replace(array("\r", "\n"), '', $htmltext);
611 
612  $extrastyle = '';
613  if ($direction < 0) {
614  $extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : '');
615  $extrastyle = 'padding: 0px; padding-left: 3px !important;';
616  }
617  if ($direction > 0) {
618  $extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : '');
619  $extrastyle = 'padding: 0px; padding-right: 3px !important;';
620  }
621 
622  $classfortooltip = 'classfortooltip';
623 
624  $s = '';
625  $textfordialog = '';
626 
627  if ($tooltiptrigger == '') {
628  $htmltext = str_replace('"', '&quot;', $htmltext);
629  } else {
630  $classfortooltip = 'classfortooltiponclick';
631  $textfordialog .= '<div style="display: none;" id="idfortooltiponclick_'.$tooltiptrigger.'" class="classfortooltiponclicktext">'.$htmltext.'</div>';
632  }
633  if ($tooltipon == 2 || $tooltipon == 3) {
634  $paramfortooltipimg = ' class="'.$classfortooltip.($notabs != 3 ? ' inline-block' : '').($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'"';
635  if ($tooltiptrigger == '') {
636  $paramfortooltipimg .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on img tag to store tooltip
637  } else {
638  $paramfortooltipimg .= ' dolid="'.$tooltiptrigger.'"';
639  }
640  } else {
641  $paramfortooltipimg = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag
642  }
643  if ($tooltipon == 1 || $tooltipon == 3) {
644  $paramfortooltiptd = ' class="'.($tooltipon == 3 ? 'cursorpointer ' : '').$classfortooltip.' inline-block'.($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'" ';
645  if ($tooltiptrigger == '') {
646  $paramfortooltiptd .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on td tag to store tooltip
647  } else {
648  $paramfortooltiptd .= ' dolid="'.$tooltiptrigger.'"';
649  }
650  } else {
651  $paramfortooltiptd = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag
652  }
653  if (empty($notabs)) {
654  $s .= '<table class="nobordernopadding"><tr style="height: auto;">';
655  } elseif ($notabs == 2) {
656  $s .= '<div class="inline-block'.($forcenowrap ? ' nowrap' : '').'">';
657  }
658  // Define value if value is before
659  if ($direction < 0) {
660  $s .= '<'.$tag.$paramfortooltipimg;
661  if ($tag == 'td') {
662  $s .= ' class=valigntop" width="14"';
663  }
664  $s .= '>'.$textfordialog.$img.'</'.$tag.'>';
665  }
666  // Use another method to help avoid having a space in value in order to use this value with jquery
667  // Define label
668  if ((string) $text != '') {
669  $s .= '<'.$tag.$paramfortooltiptd.'>'.$text.'</'.$tag.'>';
670  }
671  // Define value if value is after
672  if ($direction > 0) {
673  $s .= '<'.$tag.$paramfortooltipimg;
674  if ($tag == 'td') {
675  $s .= ' class="valignmiddle" width="14"';
676  }
677  $s .= '>'.$textfordialog.$img.'</'.$tag.'>';
678  }
679  if (empty($notabs)) {
680  $s .= '</tr></table>';
681  } elseif ($notabs == 2) {
682  $s .= '</div>';
683  }
684 
685  return $s;
686  }
687 
702  public function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
703  {
704  global $conf, $langs;
705 
706  $alt = '';
707  if ($tooltiptrigger) {
708  $alt = $langs->transnoentitiesnoconv("ClickToShowHelp");
709  }
710 
711  //For backwards compatibility
712  if ($type == '0') {
713  $type = 'info';
714  } elseif ($type == '1') {
715  $type = 'help';
716  }
717 
718  // If info or help with no javascript, show only text
719  if (empty($conf->use_javascript_ajax)) {
720  if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
721  return $text;
722  } else {
723  $alt = $htmltext;
724  $htmltext = '';
725  }
726  }
727 
728  // If info or help with smartphone, show only text (tooltip hover can't works)
729  if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) {
730  if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
731  return $text;
732  }
733  }
734  // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone)
735  //if (! empty($conf->dol_no_mouse_hover) && ! empty($tooltiptrigger))
736  //{
737  //if ($type == 'info' || $type == 'help') return '<a href="'..'">'.$text.''</a>';
738  //}
739 
740  $img = '';
741  if ($type == 'info') {
742  $img = img_help(0, $alt);
743  } elseif ($type == 'help') {
744  $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
745  } elseif ($type == 'helpclickable') {
746  $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
747  } elseif ($type == 'superadmin') {
748  $img = img_picto($alt, 'redstar');
749  } elseif ($type == 'admin') {
750  $img = img_picto($alt, 'star');
751  } elseif ($type == 'warning') {
752  $img = img_warning($alt);
753  } elseif ($type != 'none') {
754  $img = img_picto($alt, $type); // $type can be an image path
755  }
756 
757  return $this->textwithtooltip($text, $htmltext, ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2), $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
758  }
759 
770  public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect')
771  {
772  global $conf, $langs, $hookmanager;
773 
774 
775  $disabled = 0;
776  $ret = '<div class="centpercent center">';
777  $ret .= '<select class="flat'.(empty($conf->use_javascript_ajax) ? '' : ' hideobject').' '.$name.' '.$name.'select valignmiddle alignstart" id="'.$name.'" name="'.$name.'"'.($disabled ? ' disabled="disabled"' : '').'>';
778 
779  // 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.
780  $parameters = array();
781  $reshook = $hookmanager->executeHooks('addMoreMassActions', $parameters); // Note that $action and $object may have been modified by hook
782  // check if there is a mass action
783  if (count($arrayofaction) == 0 && empty($hookmanager->resPrint)) {
784  return;
785  }
786  if (empty($reshook)) {
787  $ret .= '<option value="0"'.($disabled ? ' disabled="disabled"' : '').'>-- '.$langs->trans("SelectAction").' --</option>';
788  foreach ($arrayofaction as $code => $label) {
789  $ret .= '<option value="'.$code.'"'.($disabled ? ' disabled="disabled"' : '').' data-html="'.dol_escape_htmltag($label).'">'.$label.'</option>';
790  }
791  }
792  $ret .= $hookmanager->resPrint;
793 
794  $ret .= '</select>';
795 
796  if (empty($conf->dol_optimize_smallscreen)) {
797  $ret .= ajax_combobox('.'.$name.'select');
798  }
799 
800  // 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
801  $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.
802  $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")).'">';
803  $ret .= '</div>';
804 
805  if (!empty($conf->use_javascript_ajax)) {
806  $ret .= '<!-- JS CODE TO ENABLE mass action select -->
807  <script>
808  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 */
809  {
810  atleastoneselected=0;
811  jQuery("."+cssclass).each(function( index ) {
812  /* console.log( index + ": " + $( this ).text() ); */
813  if ($(this).is(\':checked\')) atleastoneselected++;
814  });
815 
816  console.log("initCheckForSelect mode="+mode+" name="+name+" cssclass="+cssclass+" atleastoneselected="+atleastoneselected);
817 
818  if (atleastoneselected || '.$alwaysvisible.')
819  {
820  jQuery("."+name).show();
821  '.($selected ? 'if (atleastoneselected) { jQuery("."+name+"select").val("'.$selected.'").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', false); }' : '').'
822  '.($selected ? 'if (! atleastoneselected) { jQuery("."+name+"select").val("0").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', true); } ' : '').'
823  }
824  else
825  {
826  jQuery("."+name).hide();
827  jQuery("."+name+"other").hide();
828  }
829  }
830 
831  jQuery(document).ready(function () {
832  initCheckForSelect(0, "' . $name.'", "'.$cssclass.'");
833  jQuery(".' . $cssclass.'").click(function() {
834  initCheckForSelect(1, "'.$name.'", "'.$cssclass.'");
835  });
836  jQuery(".' . $name.'select").change(function() {
837  var massaction = $( this ).val();
838  var urlform = $( this ).closest("form").attr("action").replace("#show_files","");
839  if (massaction == "builddoc")
840  {
841  urlform = urlform + "#show_files";
842  }
843  $( this ).closest("form").attr("action", urlform);
844  console.log("we select a mass action name='.$name.' massaction="+massaction+" - "+urlform);
845  /* Warning: if you set submit button to disabled, post using Enter will no more work if there is no other button */
846  if ($(this).val() != \'0\')
847  {
848  jQuery(".' . $name.'confirmed").prop(\'disabled\', false);
849  jQuery(".' . $name.'other").hide(); /* To disable if another div was open */
850  jQuery(".' . $name.'"+massaction).show();
851  }
852  else
853  {
854  jQuery(".' . $name.'confirmed").prop(\'disabled\', true);
855  jQuery(".' . $name.'other").hide(); /* To disable any div open */
856  }
857  });
858  });
859  </script>
860  ';
861  }
862 
863  return $ret;
864  }
865 
866  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
883  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)
884  {
885  // phpcs:enable
886  global $conf, $langs, $mysoc;
887 
888  $langs->load("dict");
889 
890  $out = '';
891  $countryArray = array();
892  $favorite = array();
893  $label = array();
894  $atleastonefavorite = 0;
895 
896  $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite, eec";
897  $sql .= " FROM ".$this->db->prefix()."c_country";
898  $sql .= " WHERE active > 0";
899  //$sql.= " ORDER BY code ASC";
900 
901  dol_syslog(get_class($this)."::select_country", LOG_DEBUG);
902  $resql = $this->db->query($sql);
903  if ($resql) {
904  $out .= '<select id="select'.$htmlname.'" class="flat maxwidth200onsmartphone selectcountry'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" '.$htmloption.'>';
905  $num = $this->db->num_rows($resql);
906  $i = 0;
907  if ($num) {
908  while ($i < $num) {
909  $obj = $this->db->fetch_object($resql);
910 
911  $countryArray[$i]['rowid'] = $obj->rowid;
912  $countryArray[$i]['code_iso'] = $obj->code_iso;
913  $countryArray[$i]['code_iso3'] = $obj->code_iso3;
914  $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 : ''));
915  $countryArray[$i]['favorite'] = $obj->favorite;
916  $countryArray[$i]['eec'] = $obj->eec;
917  $favorite[$i] = $obj->favorite;
918  $label[$i] = dol_string_unaccent($countryArray[$i]['label']);
919  $i++;
920  }
921 
922  if (empty($disablefavorites)) {
923  $array1_sort_order = SORT_DESC;
924  $array2_sort_order = SORT_ASC;
925  array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $countryArray);
926  } else {
927  $countryArray = dol_sort_array($countryArray, 'label');
928  }
929 
930  if ($showempty) {
931  if (is_numeric($showempty)) {
932  $out .= '<option value="">&nbsp;</option>'."\n";
933  } else {
934  $out .= '<option value="">'.$langs->trans($showempty).'</option>'."\n";
935  }
936  }
937 
938  if ($addspecialentries) { // Add dedicated entries for groups of countries
939  //if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
940  $out .= '<option value="special_allnotme"'.($selected == 'special_allnotme' ? ' selected' : '').'>'.$langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
941  $out .= '<option value="special_eec"'.($selected == 'special_eec' ? ' selected' : '').'>'.$langs->trans("CountriesInEEC").'</option>';
942  if ($mysoc->isInEEC()) {
943  $out .= '<option value="special_eecnotme"'.($selected == 'special_eecnotme' ? ' selected' : '').'>'.$langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
944  }
945  $out .= '<option value="special_noteec"'.($selected == 'special_noteec' ? ' selected' : '').'>'.$langs->trans("CountriesNotInEEC").'</option>';
946  $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
947  }
948 
949  foreach ($countryArray as $row) {
950  //if (empty($showempty) && empty($row['rowid'])) continue;
951  if (empty($row['rowid'])) {
952  continue;
953  }
954  if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
955  continue; // exclude some countries
956  }
957 
958  if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
959  $atleastonefavorite++;
960  }
961  if (empty($row['favorite']) && $atleastonefavorite) {
962  $atleastonefavorite = 0;
963  $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
964  }
965 
966  $labeltoshow = '';
967  if ($row['label']) {
968  $labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
969  } else {
970  $labeltoshow .= '&nbsp;';
971  }
972  if ($row['code_iso']) {
973  $labeltoshow .= ' <span class="opacitymedium">('.$row['code_iso'].')</span>';
974  if (empty($hideflags)) {
975  $tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
976  $labeltoshow = $tmpflag.' '.$labeltoshow;
977  }
978  }
979 
980  if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
981  $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']).'">';
982  } else {
983  $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']).'">';
984  }
985  $out .= $labeltoshow;
986  $out .= '</option>'."\n";
987  }
988  }
989  $out .= '</select>';
990  } else {
991  dol_print_error($this->db);
992  }
993 
994  // Make select dynamic
995  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
996  $out .= ajax_combobox('select'.$htmlname, array(), 0, 0, 'resolve');
997 
998  return $out;
999  }
1000 
1001  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1015  public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array(), $disableautocomplete = 0)
1016  {
1017  // phpcs:enable
1018  global $conf, $langs;
1019 
1020  $langs->load("dict");
1021 
1022  $out = '';
1023  $moreattrib = '';
1024  $incotermArray = array();
1025 
1026  $sql = "SELECT rowid, code";
1027  $sql .= " FROM ".$this->db->prefix()."c_incoterms";
1028  $sql .= " WHERE active > 0";
1029  $sql .= " ORDER BY code ASC";
1030 
1031  dol_syslog(get_class($this)."::select_incoterm", LOG_DEBUG);
1032  $resql = $this->db->query($sql);
1033  if ($resql) {
1034  if ($conf->use_javascript_ajax && !$forcecombo) {
1035  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1036  $out .= ajax_combobox($htmlname, $events);
1037  }
1038 
1039  if (!empty($page)) {
1040  $out .= '<form method="post" action="'.$page.'">';
1041  $out .= '<input type="hidden" name="action" value="set_incoterms">';
1042  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
1043  }
1044 
1045  $out .= '<select id="'.$htmlname.'" class="flat selectincoterm width75" name="'.$htmlname.'" '.$htmloption.'>';
1046  $out .= '<option value="0">&nbsp;</option>';
1047  $num = $this->db->num_rows($resql);
1048  $i = 0;
1049  if ($num) {
1050  while ($i < $num) {
1051  $obj = $this->db->fetch_object($resql);
1052  $incotermArray[$i]['rowid'] = $obj->rowid;
1053  $incotermArray[$i]['code'] = $obj->code;
1054  $i++;
1055  }
1056 
1057  foreach ($incotermArray as $row) {
1058  if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1059  $out .= '<option value="'.$row['rowid'].'" selected>';
1060  } else {
1061  $out .= '<option value="'.$row['rowid'].'">';
1062  }
1063 
1064  if ($row['code']) {
1065  $out .= $row['code'];
1066  }
1067 
1068  $out .= '</option>';
1069  }
1070  }
1071  $out .= '</select>';
1072 
1073  if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
1074  $out .= ajax_multiautocompleter('location_incoterms', '', DOL_URL_ROOT.'/core/ajax/locationincoterms.php')."\n";
1075  $moreattrib .= ' autocomplete="off"';
1076  }
1077  $out .= '<input id="location_incoterms" class="maxwidthonsmartphone type="text" name="location_incoterms" value="'.$location_incoterms.'">'."\n";
1078 
1079  if (!empty($page)) {
1080  $out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="'.$langs->trans("Modify").'"></form>';
1081  }
1082  } else {
1083  dol_print_error($this->db);
1084  }
1085 
1086  return $out;
1087  }
1088 
1089  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1101  public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0)
1102  {
1103  // phpcs:enable
1104  global $langs, $conf;
1105 
1106  // If product & services are enabled or both disabled.
1107  if ($forceall == 1 || (empty($forceall) && !empty($conf->product->enabled) && !empty($conf->service->enabled))
1108  || (empty($forceall) && empty($conf->product->enabled) && empty($conf->service->enabled))) {
1109  if (empty($hidetext)) {
1110  print $langs->trans("Type").': ';
1111  }
1112  print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
1113  if ($showempty) {
1114  print '<option value="-1"';
1115  if ($selected == -1) {
1116  print ' selected';
1117  }
1118  print '>&nbsp;</option>';
1119  }
1120 
1121  print '<option value="0"';
1122  if (0 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'product')) {
1123  print ' selected';
1124  }
1125  print '>'.$langs->trans("Product");
1126 
1127  print '<option value="1"';
1128  if (1 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'service')) {
1129  print ' selected';
1130  }
1131  print '>'.$langs->trans("Service");
1132 
1133  print '</select>';
1134  print ajax_combobox('select_'.$htmlname);
1135  //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1136  }
1137  if ((empty($forceall) && empty($conf->product->enabled) && !empty($conf->service->enabled)) || $forceall == 3) {
1138  print $langs->trans("Service");
1139  print '<input type="hidden" name="'.$htmlname.'" value="1">';
1140  }
1141  if ((empty($forceall) && !empty($conf->product->enabled) && empty($conf->service->enabled)) || $forceall == 2) {
1142  print $langs->trans("Product");
1143  print '<input type="hidden" name="'.$htmlname.'" value="0">';
1144  }
1145  if ($forceall < 0) { // This should happened only for contracts when both predefined product and service are disabled.
1146  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
1147  }
1148  }
1149 
1150  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1156  public function load_cache_types_fees()
1157  {
1158  // phpcs:enable
1159  global $langs;
1160 
1161  $num = count($this->cache_types_fees);
1162  if ($num > 0) {
1163  return 0; // Cache already loaded
1164  }
1165 
1166  dol_syslog(__METHOD__, LOG_DEBUG);
1167 
1168  $langs->load("trips");
1169 
1170  $sql = "SELECT c.code, c.label";
1171  $sql .= " FROM ".$this->db->prefix()."c_type_fees as c";
1172  $sql .= " WHERE active > 0";
1173 
1174  $resql = $this->db->query($sql);
1175  if ($resql) {
1176  $num = $this->db->num_rows($resql);
1177  $i = 0;
1178 
1179  while ($i < $num) {
1180  $obj = $this->db->fetch_object($resql);
1181 
1182  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1183  $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1184  $this->cache_types_fees[$obj->code] = $label;
1185  $i++;
1186  }
1187 
1188  asort($this->cache_types_fees);
1189 
1190  return $num;
1191  } else {
1192  dol_print_error($this->db);
1193  return -1;
1194  }
1195  }
1196 
1197  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1206  public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1207  {
1208  // phpcs:enable
1209  global $user, $langs;
1210 
1211  dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
1212 
1213  $this->load_cache_types_fees();
1214 
1215  print '<select id="select_'.$htmlname.'" class="flat" name="'.$htmlname.'">';
1216  if ($showempty) {
1217  print '<option value="-1"';
1218  if ($selected == -1) {
1219  print ' selected';
1220  }
1221  print '>&nbsp;</option>';
1222  }
1223 
1224  foreach ($this->cache_types_fees as $key => $value) {
1225  print '<option value="'.$key.'"';
1226  if ($key == $selected) {
1227  print ' selected';
1228  }
1229  print '>';
1230  print $value;
1231  print '</option>';
1232  }
1233 
1234  print '</select>';
1235  if ($user->admin) {
1236  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1237  }
1238  }
1239 
1240 
1241  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1263  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)
1264  {
1265  // phpcs:enable
1266  global $conf, $user, $langs;
1267 
1268  $out = '';
1269 
1270  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && !$forcecombo) {
1271  if (is_null($ajaxoptions)) {
1272  $ajaxoptions = array();
1273  }
1274 
1275  require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1276 
1277  // No immediate load of all database
1278  $placeholder = '';
1279  if ($selected && empty($selected_input_value)) {
1280  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1281  $societetmp = new Societe($this->db);
1282  $societetmp->fetch($selected);
1283  $selected_input_value = $societetmp->name;
1284  unset($societetmp);
1285  }
1286 
1287  // mode 1
1288  $urloption = 'htmlname='.urlencode(str_replace('.', '_', $htmlname)).'&outjson=1&filter='.urlencode($filter).(empty($excludeids) ? '' : '&excludeids='.join(',', $excludeids)).($showtype ? '&showtype='.urlencode($showtype) : '').($showcode ? '&showcode='.urlencode($showcode) : '');
1289 
1290  $out .= '<style type="text/css">.ui-autocomplete { z-index: 1003; }</style>';
1291  if (empty($hidelabel)) {
1292  print $langs->trans("RefOrLabel").' : ';
1293  } elseif ($hidelabel > 1) {
1294  $placeholder = $langs->trans("RefOrLabel");
1295  if ($hidelabel == 2) {
1296  $out .= img_picto($langs->trans("Search"), 'search');
1297  }
1298  }
1299  $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' : '').' />';
1300  if ($hidelabel == 3) {
1301  $out .= img_picto($langs->trans("Search"), 'search');
1302  }
1303 
1304  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, $conf->global->COMPANY_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
1305  } else {
1306  // Immediate load of all database
1307  $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids, $showcode);
1308  }
1309 
1310  return $out;
1311  }
1312 
1313  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1335  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)
1336  {
1337  // phpcs:enable
1338  global $conf, $user, $langs;
1339  global $hookmanager;
1340 
1341  $out = '';
1342  $num = 0;
1343  $outarray = array();
1344 
1345  if ($selected === '') {
1346  $selected = array();
1347  } elseif (!is_array($selected)) {
1348  $selected = array($selected);
1349  }
1350 
1351  // Clean $filter that may contains sql conditions so sql code
1352  if (function_exists('testSqlAndScriptInject')) {
1353  if (testSqlAndScriptInject($filter, 3) > 0) {
1354  $filter = '';
1355  }
1356  }
1357 
1358  // We search companies
1359  $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
1360  if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1361  $sql .= ", s.address, s.zip, s.town";
1362  $sql .= ", dictp.code as country_code";
1363  }
1364  $sql .= " FROM ".$this->db->prefix()."societe as s";
1365  if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1366  $sql .= " LEFT JOIN ".$this->db->prefix()."c_country as dictp ON dictp.rowid = s.fk_pays";
1367  }
1368  if (empty($user->rights->societe->client->voir) && !$user->socid) {
1369  $sql .= ", ".$this->db->prefix()."societe_commerciaux as sc";
1370  }
1371  $sql .= " WHERE s.entity IN (".getEntity('societe').")";
1372  if (!empty($user->socid)) {
1373  $sql .= " AND s.rowid = ".((int) $user->socid);
1374  }
1375  if ($filter) {
1376  $sql .= " AND (".$filter.")";
1377  }
1378  if (empty($user->rights->societe->client->voir) && !$user->socid) {
1379  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
1380  }
1381  if (!empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) {
1382  $sql .= " AND s.status <> 0";
1383  }
1384  if (!empty($excludeids)) {
1385  $sql .= " AND s.rowid NOT IN (".$this->db->sanitize(join(',', $excludeids)).")";
1386  }
1387  // Add where from hooks
1388  $parameters = array();
1389  $reshook = $hookmanager->executeHooks('selectThirdpartyListWhere', $parameters); // Note that $action and $object may have been modified by hook
1390  $sql .= $hookmanager->resPrint;
1391  // Add criteria
1392  if ($filterkey && $filterkey != '') {
1393  $sql .= " AND (";
1394  $prefix = empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
1395  // For natural search
1396  $scrit = explode(' ', $filterkey);
1397  $i = 0;
1398  if (count($scrit) > 1) {
1399  $sql .= "(";
1400  }
1401  foreach ($scrit as $crit) {
1402  if ($i > 0) {
1403  $sql .= " AND ";
1404  }
1405  $sql .= "(s.nom LIKE '".$this->db->escape($prefix.$crit)."%')";
1406  $i++;
1407  }
1408  if (count($scrit) > 1) {
1409  $sql .= ")";
1410  }
1411  if (!empty($conf->barcode->enabled)) {
1412  $sql .= " OR s.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1413  }
1414  $sql .= " OR s.code_client LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.code_fournisseur LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1415  $sql .= " OR s.name_alias LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.tva_intra LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1416  $sql .= ")";
1417  }
1418  $sql .= $this->db->order("nom", "ASC");
1419  $sql .= $this->db->plimit($limit, 0);
1420 
1421  // Build output string
1422  dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
1423  $resql = $this->db->query($sql);
1424  if ($resql) {
1425  if (!$forcecombo) {
1426  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1427  $out .= ajax_combobox($htmlname, $events, getDolGlobalString("COMPANY_USE_SEARCH_TO_SELECT"));
1428  }
1429 
1430  // Construct $out and $outarray
1431  $out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').'>'."\n";
1432 
1433  $textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
1434  if (!empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT)) {
1435  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
1436  //if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
1437  if ($showempty && !is_numeric($showempty)) {
1438  $textifempty = $langs->trans($showempty);
1439  } else {
1440  $textifempty .= $langs->trans("All");
1441  }
1442  }
1443  if ($showempty) {
1444  $out .= '<option value="-1" data-html="'.dol_escape_htmltag('<span class="opacitymedium">'.($textifempty ? $textifempty : '&nbsp;').'</span>').'">'.$textifempty.'</option>'."\n";
1445  }
1446 
1447  $companytemp = new Societe($this->db);
1448 
1449  $num = $this->db->num_rows($resql);
1450  $i = 0;
1451  if ($num) {
1452  while ($i < $num) {
1453  $obj = $this->db->fetch_object($resql);
1454  $label = '';
1455  if ($showcode || !empty($conf->global->SOCIETE_ADD_REF_IN_LIST)) {
1456  if (($obj->client) && (!empty($obj->code_client))) {
1457  $label = $obj->code_client.' - ';
1458  }
1459  if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
1460  $label .= $obj->code_fournisseur.' - ';
1461  }
1462  $label .= ' '.$obj->name;
1463  } else {
1464  $label = $obj->name;
1465  }
1466 
1467  if (!empty($obj->name_alias)) {
1468  $label .= ' ('.$obj->name_alias.')';
1469  }
1470 
1471  if (!empty($conf->global->SOCIETE_SHOW_VAT_IN_LIST) && !empty($obj->tva_intra)) {
1472  $label .= ' - '.$obj->tva_intra.'';
1473  }
1474 
1475  $labelhtml = $label;
1476 
1477  if ($showtype) {
1478  $companytemp->id = $obj->rowid;
1479  $companytemp->client = $obj->client;
1480  $companytemp->fournisseur = $obj->fournisseur;
1481  $tmptype = $companytemp->getTypeUrl(1, '', 0, 'span');
1482  if ($tmptype) {
1483  $labelhtml .= ' '.$tmptype;
1484  }
1485 
1486  if ($obj->client || $obj->fournisseur) {
1487  $label .= ' (';
1488  }
1489  if ($obj->client == 1 || $obj->client == 3) {
1490  $label .= $langs->trans("Customer");
1491  }
1492  if ($obj->client == 2 || $obj->client == 3) {
1493  $label .= ($obj->client == 3 ? ', ' : '').$langs->trans("Prospect");
1494  }
1495  if ($obj->fournisseur) {
1496  $label .= ($obj->client ? ', ' : '').$langs->trans("Supplier");
1497  }
1498  if ($obj->client || $obj->fournisseur) {
1499  $label .= ')';
1500  }
1501  }
1502 
1503  if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1504  $s = ($obj->address ? ' - '.$obj->address : '').($obj->zip ? ' - '.$obj->zip : '').($obj->town ? ' '.$obj->town : '');
1505  if (!empty($obj->country_code)) {
1506  $s .= ', '.$langs->trans('Country'.$obj->country_code);
1507  }
1508  $label .= $s;
1509  $labelhtml .= $s;
1510  }
1511 
1512  if (empty($outputmode)) {
1513  if (in_array($obj->rowid, $selected)) {
1514  $out .= '<option value="'.$obj->rowid.'" selected data-html="'.dol_escape_htmltag($labelhtml).'">'.$label.'</option>';
1515  } else {
1516  $out .= '<option value="'.$obj->rowid.'" data-html="'.dol_escape_htmltag($labelhtml).'">'.$label.'</option>';
1517  }
1518  } else {
1519  array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label, 'labelhtml'=>$labelhtml));
1520  }
1521 
1522  $i++;
1523  if (($i % 10) == 0) {
1524  $out .= "\n";
1525  }
1526  }
1527  }
1528  $out .= '</select>'."\n";
1529  } else {
1530  dol_print_error($this->db);
1531  }
1532 
1533  $this->result = array('nbofthirdparties'=>$num);
1534 
1535  if ($outputmode) {
1536  return $outarray;
1537  }
1538  return $out;
1539  }
1540 
1541 
1542  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1553  public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
1554  {
1555  // phpcs:enable
1556  global $langs, $conf;
1557 
1558  // On recherche les remises
1559  $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1560  $sql .= " re.description, re.fk_facture_source";
1561  $sql .= " FROM ".$this->db->prefix()."societe_remise_except as re";
1562  $sql .= " WHERE re.fk_soc = ".(int) $socid;
1563  $sql .= " AND re.entity = ".$conf->entity;
1564  if ($filter) {
1565  $sql .= " AND ".$filter;
1566  }
1567  $sql .= " ORDER BY re.description ASC";
1568 
1569  dol_syslog(get_class($this)."::select_remises", LOG_DEBUG);
1570  $resql = $this->db->query($sql);
1571  if ($resql) {
1572  print '<select id="select_'.$htmlname.'" class="flat maxwidthonsmartphone" name="'.$htmlname.'">';
1573  $num = $this->db->num_rows($resql);
1574 
1575  $qualifiedlines = $num;
1576 
1577  $i = 0;
1578  if ($num) {
1579  print '<option value="0">&nbsp;</option>';
1580  while ($i < $num) {
1581  $obj = $this->db->fetch_object($resql);
1582  $desc = dol_trunc($obj->description, 40);
1583  if (preg_match('/\(CREDIT_NOTE\)/', $desc)) {
1584  $desc = preg_replace('/\(CREDIT_NOTE\)/', $langs->trans("CreditNote"), $desc);
1585  }
1586  if (preg_match('/\(DEPOSIT\)/', $desc)) {
1587  $desc = preg_replace('/\(DEPOSIT\)/', $langs->trans("Deposit"), $desc);
1588  }
1589  if (preg_match('/\(EXCESS RECEIVED\)/', $desc)) {
1590  $desc = preg_replace('/\(EXCESS RECEIVED\)/', $langs->trans("ExcessReceived"), $desc);
1591  }
1592  if (preg_match('/\(EXCESS PAID\)/', $desc)) {
1593  $desc = preg_replace('/\(EXCESS PAID\)/', $langs->trans("ExcessPaid"), $desc);
1594  }
1595 
1596  $selectstring = '';
1597  if ($selected > 0 && $selected == $obj->rowid) {
1598  $selectstring = ' selected';
1599  }
1600 
1601  $disabled = '';
1602  if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
1603  $qualifiedlines--;
1604  $disabled = ' disabled';
1605  }
1606 
1607  if (!empty($conf->global->MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST) && !empty($obj->fk_facture_source)) {
1608  $tmpfac = new Facture($this->db);
1609  if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
1610  $desc = $desc.' - '.$tmpfac->ref;
1611  }
1612  }
1613 
1614  print '<option value="'.$obj->rowid.'"'.$selectstring.$disabled.'>'.$desc.' ('.price($obj->amount_ht).' '.$langs->trans("HT").' - '.price($obj->amount_ttc).' '.$langs->trans("TTC").')</option>';
1615  $i++;
1616  }
1617  }
1618  print '</select>';
1619  print ajax_combobox('select_'.$htmlname);
1620 
1621  return $qualifiedlines;
1622  } else {
1623  dol_print_error($this->db);
1624  return -1;
1625  }
1626  }
1627 
1628  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1649  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 = '')
1650  {
1651  // phpcs:enable
1652  print $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $morecss, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid);
1653  return $this->num;
1654  }
1655 
1680  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)
1681  {
1682  global $conf, $langs, $hookmanager, $action;
1683 
1684  $langs->load('companies');
1685 
1686  if (empty($htmlid)) {
1687  $htmlid = $htmlname;
1688  }
1689  $num = 0;
1690 
1691  if ($selected === '') {
1692  $selected = array();
1693  } elseif (!is_array($selected)) {
1694  $selected = array($selected);
1695  }
1696  $out = '';
1697 
1698  if (!is_object($hookmanager)) {
1699  include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1700  $hookmanager = new HookManager($this->db);
1701  }
1702 
1703  // We search third parties
1704  $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";
1705  if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1706  $sql .= ", s.nom as company, s.town AS company_town";
1707  }
1708  $sql .= " FROM ".$this->db->prefix()."socpeople as sp";
1709  if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1710  $sql .= " LEFT OUTER JOIN ".$this->db->prefix()."societe as s ON s.rowid=sp.fk_soc";
1711  }
1712  $sql .= " WHERE sp.entity IN (".getEntity('contact').")";
1713  if ($socid > 0 || $socid == -1) {
1714  $sql .= " AND sp.fk_soc = ".((int) $socid);
1715  }
1716  if (!empty($conf->global->CONTACT_HIDE_INACTIVE_IN_COMBOBOX)) {
1717  $sql .= " AND sp.statut <> 0";
1718  }
1719  // Add where from hooks
1720  $parameters = array();
1721  $reshook = $hookmanager->executeHooks('selectContactListWhere', $parameters); // Note that $action and $object may have been modified by hook
1722  $sql .= $hookmanager->resPrint;
1723  $sql .= " ORDER BY sp.lastname ASC";
1724 
1725  dol_syslog(get_class($this)."::selectcontacts", LOG_DEBUG);
1726  $resql = $this->db->query($sql);
1727  if ($resql) {
1728  $num = $this->db->num_rows($resql);
1729 
1730  if ($htmlname != 'none' && !$options_only) {
1731  $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="'.$htmlid.'" name="'.$htmlname.(($num || empty($disableifempty)) ? '' : ' disabled').($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.(!empty($moreparam) ? $moreparam : '').'>';
1732  }
1733 
1734  if ($showempty && ! is_numeric($showempty)) {
1735  $textforempty = $showempty;
1736  $out .= '<option class="optiongrey" value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'>'.$textforempty.'</option>';
1737  } else {
1738  if (($showempty == 1 || ($showempty == 3 && $num > 1)) && ! $multiple) {
1739  $out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>&nbsp;</option>';
1740  }
1741  if ($showempty == 2) {
1742  $out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>-- '.$langs->trans("Internal").' --</option>';
1743  }
1744  }
1745 
1746  $i = 0;
1747  if ($num) {
1748  include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1749  $contactstatic = new Contact($this->db);
1750 
1751  while ($i < $num) {
1752  $obj = $this->db->fetch_object($resql);
1753 
1754  // Set email (or phones) and town extended infos
1755  $extendedInfos = '';
1756  if (!empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1757  $extendedInfos = array();
1758  $email = trim($obj->email);
1759  if (!empty($email)) {
1760  $extendedInfos[] = $email;
1761  } else {
1762  $phone = trim($obj->phone);
1763  $phone_perso = trim($obj->phone_perso);
1764  $phone_mobile = trim($obj->phone_mobile);
1765  if (!empty($phone)) {
1766  $extendedInfos[] = $phone;
1767  }
1768  if (!empty($phone_perso)) {
1769  $extendedInfos[] = $phone_perso;
1770  }
1771  if (!empty($phone_mobile)) {
1772  $extendedInfos[] = $phone_mobile;
1773  }
1774  }
1775  $contact_town = trim($obj->contact_town);
1776  $company_town = trim($obj->company_town);
1777  if (!empty($contact_town)) {
1778  $extendedInfos[] = $contact_town;
1779  } elseif (!empty($company_town)) {
1780  $extendedInfos[] = $company_town;
1781  }
1782  $extendedInfos = implode(' - ', $extendedInfos);
1783  if (!empty($extendedInfos)) {
1784  $extendedInfos = ' - '.$extendedInfos;
1785  }
1786  }
1787 
1788  $contactstatic->id = $obj->rowid;
1789  $contactstatic->lastname = $obj->lastname;
1790  $contactstatic->firstname = $obj->firstname;
1791  if ($obj->statut == 1) {
1792  if ($htmlname != 'none') {
1793  $disabled = 0;
1794  if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
1795  $disabled = 1;
1796  }
1797  if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
1798  $disabled = 1;
1799  }
1800  if (!empty($selected) && in_array($obj->rowid, $selected)) {
1801  $out .= '<option value="'.$obj->rowid.'"';
1802  if ($disabled) {
1803  $out .= ' disabled';
1804  }
1805  $out .= ' selected>';
1806  $out .= $contactstatic->getFullName($langs).$extendedInfos;
1807  if ($showfunction && $obj->poste) {
1808  $out .= ' ('.$obj->poste.')';
1809  }
1810  if (($showsoc > 0) && $obj->company) {
1811  $out .= ' - ('.$obj->company.')';
1812  }
1813  $out .= '</option>';
1814  } else {
1815  $out .= '<option value="'.$obj->rowid.'"';
1816  if ($disabled) {
1817  $out .= ' disabled';
1818  }
1819  $out .= '>';
1820  $out .= $contactstatic->getFullName($langs).$extendedInfos;
1821  if ($showfunction && $obj->poste) {
1822  $out .= ' ('.$obj->poste.')';
1823  }
1824  if (($showsoc > 0) && $obj->company) {
1825  $out .= ' - ('.$obj->company.')';
1826  }
1827  $out .= '</option>';
1828  }
1829  } else {
1830  if (in_array($obj->rowid, $selected)) {
1831  $out .= $contactstatic->getFullName($langs).$extendedInfos;
1832  if ($showfunction && $obj->poste) {
1833  $out .= ' ('.$obj->poste.')';
1834  }
1835  if (($showsoc > 0) && $obj->company) {
1836  $out .= ' - ('.$obj->company.')';
1837  }
1838  }
1839  }
1840  }
1841  $i++;
1842  }
1843  } else {
1844  $labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
1845  $out .= '<option class="disabled" value="-1"'.(($showempty == 2 || $multiple) ? '' : ' selected').' disabled="disabled">';
1846  $out .= $labeltoshow;
1847  $out .= '</option>';
1848  }
1849 
1850  $parameters = array(
1851  'socid'=>$socid,
1852  'htmlname'=>$htmlname,
1853  'resql'=>$resql,
1854  'out'=>&$out,
1855  'showfunction'=>$showfunction,
1856  'showsoc'=>$showsoc,
1857  );
1858 
1859  $reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1860 
1861  if ($htmlname != 'none' && !$options_only) {
1862  $out .= '</select>';
1863  }
1864 
1865  if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
1866  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1867  $out .= ajax_combobox($htmlid, $events, getDolGlobalString("CONTACT_USE_SEARCH_TO_SELECT"));
1868  }
1869 
1870  $this->num = $num;
1871  return $out;
1872  } else {
1873  dol_print_error($this->db);
1874  return -1;
1875  }
1876  }
1877 
1878  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1894  public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0')
1895  {
1896  // phpcs:enable
1897  print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
1898  }
1899 
1900  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1925  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 = '', $noactive = 0, $outputmode = 0, $multiple = false, $forcecombo = 0)
1926  {
1927  // phpcs:enable
1928  global $conf, $user, $langs, $hookmanager;
1929  global $action;
1930 
1931  // If no preselected user defined, we take current user
1932  if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && empty($conf->global->SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE)) {
1933  $selected = $user->id;
1934  }
1935 
1936  if ($selected === '') {
1937  $selected = array();
1938  } elseif (!is_array($selected)) {
1939  $selected = array($selected);
1940  }
1941 
1942  $excludeUsers = null;
1943  $includeUsers = null;
1944 
1945  // Permettre l'exclusion d'utilisateurs
1946  if (is_array($exclude)) {
1947  $excludeUsers = implode(",", $exclude);
1948  }
1949  // Permettre l'inclusion d'utilisateurs
1950  if (is_array($include)) {
1951  $includeUsers = implode(",", $include);
1952  } elseif ($include == 'hierarchy') {
1953  // Build list includeUsers to have only hierarchy
1954  $includeUsers = implode(",", $user->getAllChildIds(0));
1955  } elseif ($include == 'hierarchyme') {
1956  // Build list includeUsers to have only hierarchy and current user
1957  $includeUsers = implode(",", $user->getAllChildIds(1));
1958  }
1959 
1960  $out = '';
1961  $outarray = array();
1962 
1963  // Forge request to select users
1964  $sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.photo";
1965  if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
1966  $sql .= ", e.label";
1967  }
1968  $sql .= " FROM ".$this->db->prefix()."user as u";
1969  if (!empty($conf->multicompany->enabled) && $conf->entity == 1 && $user->admin && !$user->entity) {
1970  $sql .= " LEFT JOIN ".$this->db->prefix()."entity as e ON e.rowid = u.entity";
1971  if ($force_entity) {
1972  $sql .= " WHERE u.entity IN (0, ".$this->db->sanitize($force_entity).")";
1973  } else {
1974  $sql .= " WHERE u.entity IS NOT NULL";
1975  }
1976  } else {
1977  if (!empty($conf->multicompany->enabled) && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1978  $sql .= " LEFT JOIN ".$this->db->prefix()."usergroup_user as ug";
1979  $sql .= " ON ug.fk_user = u.rowid";
1980  $sql .= " WHERE ug.entity = ".$conf->entity;
1981  } else {
1982  $sql .= " WHERE u.entity IN (0, ".$conf->entity.")";
1983  }
1984  }
1985  if (!empty($user->socid)) {
1986  $sql .= " AND u.fk_soc = ".((int) $user->socid);
1987  }
1988  if (is_array($exclude) && $excludeUsers) {
1989  $sql .= " AND u.rowid NOT IN (".$this->db->sanitize($excludeUsers).")";
1990  }
1991  if ($includeUsers) {
1992  $sql .= " AND u.rowid IN (".$this->db->sanitize($includeUsers).")";
1993  }
1994  if (!empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX) || $noactive) {
1995  $sql .= " AND u.statut <> 0";
1996  }
1997  if (!empty($morefilter)) {
1998  $sql .= " ".$morefilter;
1999  }
2000 
2001  //Add hook to filter on user (for exemple on usergroup define in custom modules)
2002  $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
2003  if (!empty($reshook)) {
2004  $sql .= $hookmanager->resPrint;
2005  }
2006 
2007  if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) { // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
2008  $sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
2009  } else {
2010  $sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
2011  }
2012 
2013  dol_syslog(get_class($this)."::select_dolusers", LOG_DEBUG);
2014 
2015  $resql = $this->db->query($sql);
2016  if ($resql) {
2017  $num = $this->db->num_rows($resql);
2018  $i = 0;
2019  if ($num) {
2020  // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
2021  $out .= '<select class="flat'.($morecss ? ' '.$morecss : ' minwidth200').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
2022  if ($show_empty && !$multiple) {
2023  $textforempty = ' ';
2024  if (!empty($conf->use_javascript_ajax)) {
2025  $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
2026  }
2027  if (!is_numeric($show_empty)) {
2028  $textforempty = $show_empty;
2029  }
2030  $out .= '<option class="optiongrey" value="'.($show_empty < 0 ? $show_empty : -1).'"'.((empty($selected) || in_array(-1, $selected)) ? ' selected' : '').'>'.$textforempty.'</option>'."\n";
2031  }
2032  if ($show_every) {
2033  $out .= '<option value="-2"'.((in_array(-2, $selected)) ? ' selected' : '').'>-- '.$langs->trans("Everybody").' --</option>'."\n";
2034  }
2035 
2036  $userstatic = new User($this->db);
2037 
2038  while ($i < $num) {
2039  $obj = $this->db->fetch_object($resql);
2040 
2041  $userstatic->id = $obj->rowid;
2042  $userstatic->lastname = $obj->lastname;
2043  $userstatic->firstname = $obj->firstname;
2044  $userstatic->photo = $obj->photo;
2045  $userstatic->statut = $obj->status;
2046  $userstatic->entity = $obj->entity;
2047  $userstatic->admin = $obj->admin;
2048 
2049  $disableline = '';
2050  if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
2051  $disableline = ($enableonlytext ? $enableonlytext : '1');
2052  }
2053 
2054  $labeltoshow = '';
2055 
2056  // $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2057  $fullNameMode = 0;
2058  if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) {
2059  $fullNameMode = 1; //Firstname+lastname
2060  }
2061  $labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2062  if (empty($obj->firstname) && empty($obj->lastname)) {
2063  $labeltoshow .= $obj->login;
2064  }
2065 
2066  // Complete name with more info
2067  $moreinfo = '';
2068  if (!empty($conf->global->MAIN_SHOW_LOGIN)) {
2069  $moreinfo .= ($moreinfo ? ' - ' : ' (').$obj->login;
2070  }
2071  if ($showstatus >= 0) {
2072  if ($obj->status == 1 && $showstatus == 1) {
2073  $moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Enabled');
2074  }
2075  if ($obj->status == 0 && $showstatus == 1) {
2076  $moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Disabled');
2077  }
2078  }
2079  if (!empty($conf->multicompany->enabled) && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1 && $user->admin && !$user->entity) {
2080  if (!$obj->entity) {
2081  $moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans("AllEntities");
2082  } else {
2083  if ($obj->entity != $conf->entity) {
2084  $moreinfo .= ($moreinfo ? ' - ' : ' (').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2085  }
2086  }
2087  }
2088  $moreinfo .= ($moreinfo ? ')' : '');
2089  if ($disableline && $disableline != '1') {
2090  $moreinfo .= ' - '.$disableline; // This is text from $enableonlytext parameter
2091  }
2092  $labeltoshow .= $moreinfo;
2093 
2094  $out .= '<option value="'.$obj->rowid.'"';
2095  if ($disableline) {
2096  $out .= ' disabled';
2097  }
2098  if ((is_object($selected) && $selected->id == $obj->rowid) || (!is_object($selected) && in_array($obj->rowid, $selected))) {
2099  $out .= ' selected';
2100  }
2101  $out .= ' data-html="';
2102  $outhtml = '';
2103  // if (!empty($obj->photo)) {
2104  $outhtml .= $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1).' ';
2105  // }
2106  if ($showstatus >= 0 && $obj->status == 0) {
2107  $outhtml .= '<strike class="opacitymediumxxx">';
2108  }
2109  $outhtml .= $labeltoshow;
2110  if ($showstatus >= 0 && $obj->status == 0) {
2111  $outhtml .= '</strike>';
2112  }
2113  $out .= dol_escape_htmltag($outhtml);
2114  $out .= '">';
2115  $out .= $labeltoshow;
2116  $out .= '</option>';
2117 
2118  $outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength).$moreinfo;
2119 
2120  $i++;
2121  }
2122  } else {
2123  $out .= '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'" disabled>';
2124  $out .= '<option value="">'.$langs->trans("None").'</option>';
2125  }
2126  $out .= '</select>';
2127 
2128  if ($num && !$forcecombo) {
2129  // Enhance with select2
2130  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2131  $out .= ajax_combobox($htmlname);
2132  }
2133  } else {
2134  dol_print_error($this->db);
2135  }
2136 
2137  if ($outputmode) {
2138  return $outarray;
2139  }
2140 
2141  return $out;
2142  }
2143 
2144 
2145  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2168  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())
2169  {
2170  // phpcs:enable
2171  global $conf, $user, $langs;
2172 
2173  $userstatic = new User($this->db);
2174  $out = '';
2175 
2176 
2177  $assignedtouser = array();
2178  if (!empty($_SESSION['assignedtouser'])) {
2179  $assignedtouser = json_decode($_SESSION['assignedtouser'], true);
2180  }
2181  $nbassignetouser = count($assignedtouser);
2182 
2183  //if ($nbassignetouser && $action != 'view') $out .= '<br>';
2184  if ($nbassignetouser) {
2185  $out .= '<ul class="attendees">';
2186  }
2187  $i = 0;
2188  $ownerid = 0;
2189  foreach ($assignedtouser as $key => $value) {
2190  if ($value['id'] == $ownerid) {
2191  continue;
2192  }
2193 
2194  $out .= '<li>';
2195  $userstatic->fetch($value['id']);
2196  $out .= $userstatic->getNomUrl(-1);
2197  if ($i == 0) {
2198  $ownerid = $value['id'];
2199  $out .= ' ('.$langs->trans("Owner").')';
2200  }
2201  if ($nbassignetouser > 1 && $action != 'view') {
2202  $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.'">';
2203  }
2204  // Show my availability
2205  if ($showproperties) {
2206  if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
2207  $out .= '<div class="myavailability inline-block">';
2208  $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>';
2209  $out .= '</div>';
2210  }
2211  }
2212  //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
2213  //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
2214 
2215  $out .= '</li>';
2216  $i++;
2217  }
2218  if ($nbassignetouser) {
2219  $out .= '</ul>';
2220  }
2221 
2222  // Method with no ajax
2223  if ($action != 'view') {
2224  $out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
2225  $out .= '<script type="text/javascript">jQuery(document).ready(function () {';
2226  $out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
2227  $out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
2228  $out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#'.$action.'assignedtouser").attr("disabled", false); }';
2229  $out .= ' else { jQuery("#'.$action.'assignedtouser").attr("disabled", true); }';
2230  $out .= '});';
2231  $out .= '})</script>';
2232  $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
2233  $out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp reposition" id="'.$action.'assignedtouser" name="'.$action.'assignedtouser" value="'.dol_escape_htmltag($langs->trans("Add")).'">';
2234  $out .= '<br>';
2235  }
2236 
2237  return $out;
2238  }
2239 
2240 
2241  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2269  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)
2270  {
2271  // phpcs:enable
2272  global $langs, $conf;
2273 
2274  $out = '';
2275 
2276  // check parameters
2277  $price_level = (!empty($price_level) ? $price_level : 0);
2278  if (is_null($ajaxoptions)) {
2279  $ajaxoptions = array();
2280  }
2281 
2282  if (strval($filtertype) === '' && (!empty($conf->product->enabled) || !empty($conf->service->enabled))) {
2283  if (!empty($conf->product->enabled) && empty($conf->service->enabled)) {
2284  $filtertype = '0';
2285  } elseif (empty($conf->product->enabled) && !empty($conf->service->enabled)) {
2286  $filtertype = '1';
2287  }
2288  }
2289 
2290  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2291  $placeholder = '';
2292 
2293  if ($selected && empty($selected_input_value)) {
2294  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2295  $producttmpselect = new Product($this->db);
2296  $producttmpselect->fetch($selected);
2297  $selected_input_value = $producttmpselect->ref;
2298  unset($producttmpselect);
2299  }
2300  // handle case where product or service module is disabled + no filter specified
2301  if ($filtertype == '') {
2302  if (empty($conf->product->enabled)) { // when product module is disabled, show services only
2303  $filtertype = 1;
2304  } elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only
2305  $filtertype = 0;
2306  }
2307  }
2308  // mode=1 means customers products
2309  $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;
2310  //Price by customer
2311  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2312  $urloption .= '&socid='.$socid;
2313  }
2314  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
2315 
2316  if (!empty($conf->variants->enabled) && is_array($selected_combinations)) {
2317  // Code to automatically insert with javascript the select of attributes under the select of product
2318  // when a parent of variant has been selected.
2319  $out .= '
2320  <!-- script to auto show attributes select tags if a variant was selected -->
2321  <script>
2322  // auto show attributes fields
2323  selected = '.json_encode($selected_combinations).';
2324  combvalues = {};
2325 
2326  jQuery(document).ready(function () {
2327 
2328  jQuery("input[name=\'prod_entry_mode\']").change(function () {
2329  if (jQuery(this).val() == \'free\') {
2330  jQuery(\'div#attributes_box\').empty();
2331  }
2332  });
2333 
2334  jQuery("input#'.$htmlname.'").change(function () {
2335 
2336  if (!jQuery(this).val()) {
2337  jQuery(\'div#attributes_box\').empty();
2338  return;
2339  }
2340 
2341  console.log("A change has started. We get variants fields to inject html select");
2342 
2343  jQuery.getJSON("'.DOL_URL_ROOT.'/variants/ajax/getCombinations.php", {
2344  id: jQuery(this).val()
2345  }, function (data) {
2346  jQuery(\'div#attributes_box\').empty();
2347 
2348  jQuery.each(data, function (key, val) {
2349 
2350  combvalues[val.id] = val.values;
2351 
2352  var span = jQuery(document.createElement(\'div\')).css({
2353  \'display\': \'table-row\'
2354  });
2355 
2356  span.append(
2357  jQuery(document.createElement(\'div\')).text(val.label).css({
2358  \'font-weight\': \'bold\',
2359  \'display\': \'table-cell\'
2360  })
2361  );
2362 
2363  var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
2364  \'margin-left\': \'15px\',
2365  \'white-space\': \'pre\'
2366  }).append(
2367  jQuery(document.createElement(\'option\')).val(\'\')
2368  );
2369 
2370  jQuery.each(combvalues[val.id], function (key, val) {
2371  var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
2372 
2373  if (selected[val.fk_product_attribute] == val.id) {
2374  tag.attr(\'selected\', \'selected\');
2375  }
2376 
2377  html.append(tag);
2378  });
2379 
2380  span.append(html);
2381  jQuery(\'div#attributes_box\').append(span);
2382  });
2383  })
2384  });
2385 
2386  '.($selected ? 'jQuery("input#'.$htmlname.'").change();' : '').'
2387  });
2388  </script>
2389  ';
2390  }
2391 
2392  if (empty($hidelabel)) {
2393  $out .= $langs->trans("RefOrLabel").' : ';
2394  } elseif ($hidelabel > 1) {
2395  $placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
2396  if ($hidelabel == 2) {
2397  $out .= img_picto($langs->trans("Search"), 'search');
2398  }
2399  }
2400  $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' : '').' />';
2401  if ($hidelabel == 3) {
2402  $out .= img_picto($langs->trans("Search"), 'search');
2403  }
2404  } else {
2405  $out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus, $status_purchase);
2406  }
2407 
2408  if (empty($nooutput)) {
2409  print $out;
2410  } else {
2411  return $out;
2412  }
2413  }
2414 
2415  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2416 
2432  public function select_bom($selected = '', $htmlname = 'bom_id', $limit = 0, $status = 1, $type = 0, $showempty = '1', $morecss = '', $nooutput = '', $forcecombo = 0, $TProducts = [])
2433  {
2434  // phpcs:enable
2435  global $conf, $user, $langs, $db;
2436 
2437  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2438 
2439  $error = 0;
2440  $out = '';
2441 
2442  if (!$forcecombo) {
2443  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2444  $events = array();
2445  $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
2446  }
2447 
2448  $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
2449 
2450  $sql = 'SELECT b.rowid, b.ref, b.label, b.fk_product';
2451  $sql.= ' FROM '.MAIN_DB_PREFIX.'bom_bom as b';
2452  $sql.= ' WHERE b.entity IN ('.getEntity('bom').')';
2453  if (!empty($status)) $sql.= ' AND status = '. (int) $status;
2454  if (!empty($type)) $sql.= ' AND bomtype = '. (int) $type;
2455  if (!empty($TProducts)) $sql .= ' AND fk_product IN ('.$this->db->sanitize(implode(',', $TProducts)).')';
2456  if (!empty($limit)) $sql.= ' LIMIT '. (int) $limit;
2457  $resql = $db->query($sql);
2458  if ($resql) {
2459  if ($showempty) {
2460  $out .= '<option value="-1"';
2461  if (empty($selected)) $out .= ' selected';
2462  $out .= '>&nbsp;</option>';
2463  }
2464  while ($obj = $db->fetch_object($resql)) {
2465  $product = new Product($db);
2466  $res = $product->fetch($obj->fk_product);
2467  $out .= '<option value="'.$obj->rowid.'"';
2468  if ($obj->rowid == $selected) $out .= 'selected';
2469  $out .= '>'.$obj->ref.' - '.$product->label .' - '. $obj->label.'</option>';
2470  }
2471  } else {
2472  $error++;
2473  dol_print_error($db);
2474  }
2475  if (empty($nooutput)) {
2476  print $out;
2477  } else {
2478  return $out;
2479  }
2480  }
2481 
2482  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2508  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)
2509  {
2510  // phpcs:enable
2511  global $langs, $conf;
2512  global $hookmanager;
2513 
2514  $out = '';
2515  $outarray = array();
2516 
2517  // Units
2518  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2519  $langs->load('other');
2520  }
2521 
2522  $warehouseStatusArray = array();
2523  if (!empty($warehouseStatus)) {
2524  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
2525  if (preg_match('/warehouseclosed/', $warehouseStatus)) {
2526  $warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
2527  }
2528  if (preg_match('/warehouseopen/', $warehouseStatus)) {
2529  $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
2530  }
2531  if (preg_match('/warehouseinternal/', $warehouseStatus)) {
2532  $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
2533  }
2534  }
2535 
2536  $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";
2537  if (count($warehouseStatusArray)) {
2538  $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
2539  } else {
2540  $selectFieldsGrouped = ", ".$this->db->ifsql("p.stock IS NULL", 0, "p.stock")." AS stock";
2541  }
2542 
2543  $sql = "SELECT ";
2544  $sql .= $selectFields.$selectFieldsGrouped;
2545 
2546  if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2547  //Product category
2548  $sql .= ", (SELECT ".$this->db->prefix()."categorie_product.fk_categorie
2549  FROM ".$this->db->prefix()."categorie_product
2550  WHERE ".$this->db->prefix()."categorie_product.fk_product=p.rowid
2551  LIMIT 1
2552  ) AS categorie_product_id ";
2553  }
2554 
2555  //Price by customer
2556  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2557  $sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
2558  $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';
2559  $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custdefault_vat_code, custref";
2560  }
2561  // Units
2562  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2563  $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";
2564  $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';
2565  }
2566 
2567  // Multilang : we add translation
2568  if (!empty($conf->global->MAIN_MULTILANGS)) {
2569  $sql .= ", pl.label as label_translated";
2570  $sql .= ", pl.description as description_translated";
2571  $selectFields .= ", label_translated";
2572  $selectFields .= ", description_translated";
2573  }
2574  // Price by quantity
2575  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2576  $sql .= ", (SELECT pp.rowid FROM ".$this->db->prefix()."product_price as pp WHERE pp.fk_product = p.rowid";
2577  if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2578  $sql .= " AND price_level = ".((int) $price_level);
2579  }
2580  $sql .= " ORDER BY date_price";
2581  $sql .= " DESC LIMIT 1) as price_rowid";
2582  $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
2583  if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2584  $sql .= " AND price_level = ".((int) $price_level);
2585  }
2586  $sql .= " ORDER BY date_price";
2587  $sql .= " DESC LIMIT 1) as price_by_qty";
2588  $selectFields .= ", price_rowid, price_by_qty";
2589  }
2590  $sql .= " FROM ".$this->db->prefix()."product as p";
2591  if (count($warehouseStatusArray)) {
2592  $sql .= " LEFT JOIN ".$this->db->prefix()."product_stock as ps on ps.fk_product = p.rowid";
2593  $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (".getEntity('stock').")";
2594  $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.
2595  }
2596 
2597  // include search in supplier ref
2598  if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2599  $sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
2600  }
2601 
2602  //Price by customer
2603  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2604  $sql .= " LEFT JOIN ".$this->db->prefix()."product_customer_price as pcp ON pcp.fk_soc=".((int) $socid)." AND pcp.fk_product=p.rowid";
2605  }
2606  // Units
2607  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2608  $sql .= " LEFT JOIN ".$this->db->prefix()."c_units u ON u.rowid = p.fk_unit";
2609  }
2610  // Multilang : we add translation
2611  if (!empty($conf->global->MAIN_MULTILANGS)) {
2612  $sql .= " LEFT JOIN ".$this->db->prefix()."product_lang as pl ON pl.fk_product = p.rowid ";
2613  if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && !empty($socid)) {
2614  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
2615  $soc = new Societe($this->db);
2616  $result = $soc->fetch($socid);
2617  if ($result > 0 && !empty($soc->default_lang)) {
2618  $sql .= " AND pl.lang = '".$this->db->escape($soc->default_lang)."'";
2619  } else {
2620  $sql .= " AND pl.lang = '".$this->db->escape($langs->getDefaultLang())."'";
2621  }
2622  } else {
2623  $sql .= " AND pl.lang = '".$this->db->escape($langs->getDefaultLang())."'";
2624  }
2625  }
2626 
2627  if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2628  $sql .= " LEFT JOIN ".$this->db->prefix()."product_attribute_combination pac ON pac.fk_product_child = p.rowid";
2629  }
2630 
2631  $sql .= ' WHERE p.entity IN ('.getEntity('product').')';
2632 
2633  if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2634  $sql .= " AND pac.rowid IS NULL";
2635  }
2636 
2637  if ($finished == 0) {
2638  $sql .= " AND p.finished = ".((int) $finished);
2639  } elseif ($finished == 1) {
2640  $sql .= " AND p.finished = ".((int) $finished);
2641  if ($status >= 0) {
2642  $sql .= " AND p.tosell = ".((int) $status);
2643  }
2644  } elseif ($status >= 0) {
2645  $sql .= " AND p.tosell = ".((int) $status);
2646  }
2647  if ($status_purchase >= 0) {
2648  $sql .= " AND p.tobuy = ".((int) $status_purchase);
2649  }
2650  // Filter by product type
2651  if (strval($filtertype) != '') {
2652  $sql .= " AND p.fk_product_type = ".((int) $filtertype);
2653  } elseif (empty($conf->product->enabled)) { // when product module is disabled, show services only
2654  $sql .= " AND p.fk_product_type = 1";
2655  } elseif (empty($conf->service->enabled)) { // when service module is disabled, show products only
2656  $sql .= " AND p.fk_product_type = 0";
2657  }
2658  // Add where from hooks
2659  $parameters = array();
2660  $reshook = $hookmanager->executeHooks('selectProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
2661  $sql .= $hookmanager->resPrint;
2662  // Add criteria on ref/label
2663  if ($filterkey != '') {
2664  $sql .= ' AND (';
2665  $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
2666  // For natural search
2667  $scrit = explode(' ', $filterkey);
2668  $i = 0;
2669  if (count($scrit) > 1) {
2670  $sql .= "(";
2671  }
2672  foreach ($scrit as $crit) {
2673  if ($i > 0) {
2674  $sql .= " AND ";
2675  }
2676  $sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2677  if (!empty($conf->global->MAIN_MULTILANGS)) {
2678  $sql .= " OR pl.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2679  }
2680  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && ! empty($socid)) {
2681  $sql .= " OR pcp.ref_customer LIKE '".$this->db->escape($prefix.$crit)."%'";
2682  }
2683  if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION)) {
2684  $sql .= " OR p.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2685  if (!empty($conf->global->MAIN_MULTILANGS)) {
2686  $sql .= " OR pl.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2687  }
2688  }
2689  if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2690  $sql .= " OR pfp.ref_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
2691  }
2692  $sql .= ")";
2693  $i++;
2694  }
2695  if (count($scrit) > 1) {
2696  $sql .= ")";
2697  }
2698  if (!empty($conf->barcode->enabled)) {
2699  $sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
2700  }
2701  $sql .= ')';
2702  }
2703  if (count($warehouseStatusArray)) {
2704  $sql .= " GROUP BY ".$selectFields;
2705  }
2706 
2707  //Sort by category
2708  if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2709  $sql .= " ORDER BY categorie_product_id ";
2710  //ASC OR DESC order
2711  ($conf->global->PRODUCT_SORT_BY_CATEGORY == 1) ? $sql .= "ASC" : $sql .= "DESC";
2712  } else {
2713  $sql .= $this->db->order("p.ref");
2714  }
2715 
2716  $sql .= $this->db->plimit($limit, 0);
2717 
2718  // Build output string
2719  dol_syslog(get_class($this)."::select_produits_list search products", LOG_DEBUG);
2720  $result = $this->db->query($sql);
2721  if ($result) {
2722  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2723  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
2724  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
2725 
2726  $num = $this->db->num_rows($result);
2727 
2728  $events = null;
2729 
2730  if (!$forcecombo) {
2731  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2732  $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
2733  }
2734 
2735  $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
2736 
2737  $textifempty = '';
2738  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2739  //if (! empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2740  if (!empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2741  if ($showempty && !is_numeric($showempty)) {
2742  $textifempty = $langs->trans($showempty);
2743  } else {
2744  $textifempty .= $langs->trans("All");
2745  }
2746  } else {
2747  if ($showempty && !is_numeric($showempty)) {
2748  $textifempty = $langs->trans($showempty);
2749  }
2750  }
2751  if ($showempty) {
2752  $out .= '<option value="-1" selected>'.($textifempty ? $textifempty : '&nbsp;').'</option>';
2753  }
2754 
2755  $i = 0;
2756  while ($num && $i < $num) {
2757  $opt = '';
2758  $optJson = array();
2759  $objp = $this->db->fetch_object($result);
2760 
2761  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
2762  $sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
2763  $sql .= " FROM ".$this->db->prefix()."product_price_by_qty";
2764  $sql .= " WHERE fk_product_price = ".((int) $objp->price_rowid);
2765  $sql .= " ORDER BY quantity ASC";
2766 
2767  dol_syslog(get_class($this)."::select_produits_list search prices by qty", LOG_DEBUG);
2768  $result2 = $this->db->query($sql);
2769  if ($result2) {
2770  $nb_prices = $this->db->num_rows($result2);
2771  $j = 0;
2772  while ($nb_prices && $j < $nb_prices) {
2773  $objp2 = $this->db->fetch_object($result2);
2774 
2775  $objp->price_by_qty_rowid = $objp2->rowid;
2776  $objp->price_by_qty_price_base_type = $objp2->price_base_type;
2777  $objp->price_by_qty_quantity = $objp2->quantity;
2778  $objp->price_by_qty_unitprice = $objp2->unitprice;
2779  $objp->price_by_qty_remise_percent = $objp2->remise_percent;
2780  // For backward compatibility
2781  $objp->quantity = $objp2->quantity;
2782  $objp->price = $objp2->price;
2783  $objp->unitprice = $objp2->unitprice;
2784  $objp->remise_percent = $objp2->remise_percent;
2785 
2786  //$objp->tva_tx is not overwritten by $objp2 value
2787  //$objp->default_vat_code is not overwritten by $objp2 value
2788 
2789  $this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
2790 
2791  $j++;
2792 
2793  // Add new entry
2794  // "key" value of json key array is used by jQuery automatically as selected value
2795  // "label" value of json key array is used by jQuery automatically as text for combo box
2796  $out .= $opt;
2797  array_push($outarray, $optJson);
2798  }
2799  }
2800  } else {
2801  if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_price_expression)) {
2802  $price_product = new Product($this->db);
2803  $price_product->fetch($objp->rowid, '', '', 1);
2804  $priceparser = new PriceParser($this->db);
2805  $price_result = $priceparser->parseProduct($price_product);
2806  if ($price_result >= 0) {
2807  $objp->price = $price_result;
2808  $objp->unitprice = $price_result;
2809  //Calculate the VAT
2810  $objp->price_ttc = price2num($objp->price) * (1 + ($objp->tva_tx / 100));
2811  $objp->price_ttc = price2num($objp->price_ttc, 'MU');
2812  }
2813  }
2814 
2815  $this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
2816  // Add new entry
2817  // "key" value of json key array is used by jQuery automatically as selected value
2818  // "label" value of json key array is used by jQuery automatically as text for combo box
2819  $out .= $opt;
2820  array_push($outarray, $optJson);
2821  }
2822 
2823  $i++;
2824  }
2825 
2826  $out .= '</select>';
2827 
2828  $this->db->free($result);
2829 
2830  if (empty($outputmode)) {
2831  return $out;
2832  }
2833  return $outarray;
2834  } else {
2835  dol_print_error($this->db);
2836  }
2837  }
2838 
2854  protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
2855  {
2856  global $langs, $conf, $user;
2857 
2858  $outkey = '';
2859  $outval = '';
2860  $outref = '';
2861  $outlabel = '';
2862  $outlabel_translated = '';
2863  $outdesc = '';
2864  $outdesc_translated = '';
2865  $outbarcode = '';
2866  $outorigin = '';
2867  $outtype = '';
2868  $outprice_ht = '';
2869  $outprice_ttc = '';
2870  $outpricebasetype = '';
2871  $outtva_tx = '';
2872  $outdefault_vat_code = '';
2873  $outqty = 1;
2874  $outdiscount = 0;
2875 
2876  $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
2877 
2878  $label = $objp->label;
2879  if (!empty($objp->label_translated)) {
2880  $label = $objp->label_translated;
2881  }
2882  if (!empty($filterkey) && $filterkey != '') {
2883  $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
2884  }
2885 
2886  $outkey = $objp->rowid;
2887  $outref = $objp->ref;
2888  $outrefcust = empty($objp->custref) ? '' : $objp->custref;
2889  $outlabel = $objp->label;
2890  $outdesc = $objp->description;
2891  if (!empty($conf->global->MAIN_MULTILANGS)) {
2892  $outlabel_translated = $objp->label_translated;
2893  $outdesc_translated = $objp->description_translated;
2894  }
2895  $outbarcode = $objp->barcode;
2896  $outorigin = $objp->fk_country;
2897  $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
2898 
2899  $outtype = $objp->fk_product_type;
2900  $outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
2901  $outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
2902 
2903  if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2904  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
2905  }
2906 
2907  // Units
2908  $outvalUnits = '';
2909  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
2910  if (!empty($objp->unit_short)) {
2911  $outvalUnits .= ' - '.$objp->unit_short;
2912  }
2913  }
2914  if (!empty($conf->global->PRODUCT_SHOW_DIMENSIONS_IN_COMBO)) {
2915  if (!empty($objp->weight) && $objp->weight_units !== null) {
2916  $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
2917  $outvalUnits .= ' - '.$unitToShow;
2918  }
2919  if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
2920  $unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
2921  $outvalUnits .= ' - '.$unitToShow;
2922  }
2923  if (!empty($objp->surface) && $objp->surface_units !== null) {
2924  $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
2925  $outvalUnits .= ' - '.$unitToShow;
2926  }
2927  if (!empty($objp->volume) && $objp->volume_units !== null) {
2928  $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
2929  $outvalUnits .= ' - '.$unitToShow;
2930  }
2931  }
2932  if ($outdurationvalue && $outdurationunit) {
2933  $da = array(
2934  'h' => $langs->trans('Hour'),
2935  'd' => $langs->trans('Day'),
2936  'w' => $langs->trans('Week'),
2937  'm' => $langs->trans('Month'),
2938  'y' => $langs->trans('Year')
2939  );
2940  if (isset($da[$outdurationunit])) {
2941  $outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
2942  }
2943  }
2944 
2945  $opt = '<option value="'.$objp->rowid.'"';
2946  $opt .= ($objp->rowid == $selected) ? ' selected' : '';
2947  if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
2948  $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.'"';
2949  }
2950  if (!empty($conf->stock->enabled) && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
2951  if (!empty($user->rights->stock->lire)) {
2952  if ($objp->stock > 0) {
2953  $opt .= ' class="product_line_stock_ok"';
2954  } elseif ($objp->stock <= 0) {
2955  $opt .= ' class="product_line_stock_too_low"';
2956  }
2957  }
2958  }
2959  if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) {
2960  $opt .= ' data-labeltrans="'.$outlabel_translated.'"';
2961  $opt .= ' data-desctrans="'.dol_escape_htmltag($outdesc_translated).'"';
2962  }
2963  $opt .= '>';
2964  $opt .= $objp->ref;
2965  if (! empty($objp->custref)) {
2966  $opt.= ' (' . $objp->custref . ')';
2967  }
2968  if ($outbarcode) {
2969  $opt .= ' ('.$outbarcode.')';
2970  }
2971  $opt .= ' - '.dol_trunc($label, $maxlengtharticle);
2972  if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2973  $opt .= ' ('.getCountry($outorigin, 1).')';
2974  }
2975 
2976  $objRef = $objp->ref;
2977  if (! empty($objp->custref)) {
2978  $objRef .= ' (' . $objp->custref . ')';
2979  }
2980  if (!empty($filterkey) && $filterkey != '') {
2981  $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
2982  }
2983  $outval .= $objRef;
2984  if ($outbarcode) {
2985  $outval .= ' ('.$outbarcode.')';
2986  }
2987  $outval .= ' - '.dol_trunc($label, $maxlengtharticle);
2988  if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2989  $outval .= ' ('.getCountry($outorigin, 1).')';
2990  }
2991 
2992  // Units
2993  $opt .= $outvalUnits;
2994  $outval .= $outvalUnits;
2995 
2996  $found = 0;
2997 
2998  // Multiprice
2999  // If we need a particular price level (from 1 to n)
3000  if (empty($hidepriceinlabel) && $price_level >= 1 && (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) {
3001  $sql = "SELECT price, price_ttc, price_base_type, tva_tx, default_vat_code";
3002  $sql .= " FROM ".$this->db->prefix()."product_price";
3003  $sql .= " WHERE fk_product = ".((int) $objp->rowid);
3004  $sql .= " AND entity IN (".getEntity('productprice').")";
3005  $sql .= " AND price_level = ".((int) $price_level);
3006  $sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
3007  $sql .= " LIMIT 1";
3008 
3009  dol_syslog(get_class($this).'::constructProductListOption search price for product '.$objp->rowid.' AND level '.$price_level.'', LOG_DEBUG);
3010  $result2 = $this->db->query($sql);
3011  if ($result2) {
3012  $objp2 = $this->db->fetch_object($result2);
3013  if ($objp2) {
3014  $found = 1;
3015  if ($objp2->price_base_type == 'HT') {
3016  $opt .= ' - '.price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
3017  $outval .= ' - '.price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
3018  } else {
3019  $opt .= ' - '.price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
3020  $outval .= ' - '.price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
3021  }
3022  $outprice_ht = price($objp2->price);
3023  $outprice_ttc = price($objp2->price_ttc);
3024  $outpricebasetype = $objp2->price_base_type;
3025  if (!empty($conf->global->PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL)) { // using this option is a bug. kept for backward compatibility
3026  $outtva_tx = $objp2->tva_tx; // We use the vat rate on line of multiprice
3027  $outdefault_vat_code = $objp2->default_vat_code; // We use the vat code on line of multiprice
3028  } else {
3029  $outtva_tx = $objp->tva_tx; // We use the vat rate of product, not the one on line of multiprice
3030  $outdefault_vat_code = $objp->default_vat_code; // We use the vat code or product, not the one on line of multiprice
3031  }
3032  }
3033  } else {
3034  dol_print_error($this->db);
3035  }
3036  }
3037 
3038  // Price by quantity
3039  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))) {
3040  $found = 1;
3041  $outqty = $objp->quantity;
3042  $outdiscount = $objp->remise_percent;
3043  if ($objp->quantity == 1) {
3044  $opt .= ' - '.price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/";
3045  $outval .= ' - '.price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/";
3046  $opt .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3047  $outval .= $langs->transnoentities("Unit");
3048  } else {
3049  $opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3050  $outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3051  $opt .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3052  $outval .= $langs->transnoentities("Units");
3053  }
3054 
3055  $outprice_ht = price($objp->unitprice);
3056  $outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
3057  $outpricebasetype = $objp->price_base_type;
3058  $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
3059  $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
3060  }
3061  if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
3062  $opt .= " (".price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
3063  $outval .= " (".price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
3064  }
3065  if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
3066  $opt .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
3067  $outval .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
3068  }
3069 
3070  // Price by customer
3071  if (empty($hidepriceinlabel) && !empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
3072  if (!empty($objp->idprodcustprice)) {
3073  $found = 1;
3074 
3075  if ($objp->custprice_base_type == 'HT') {
3076  $opt .= ' - '.price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
3077  $outval .= ' - '.price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
3078  } else {
3079  $opt .= ' - '.price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
3080  $outval .= ' - '.price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
3081  }
3082 
3083  $outprice_ht = price($objp->custprice);
3084  $outprice_ttc = price($objp->custprice_ttc);
3085  $outpricebasetype = $objp->custprice_base_type;
3086  $outtva_tx = $objp->custtva_tx;
3087  $outdefault_vat_code = $objp->custdefault_vat_code;
3088  }
3089  }
3090 
3091  // If level no defined or multiprice not found, we used the default price
3092  if (empty($hidepriceinlabel) && !$found) {
3093  if ($objp->price_base_type == 'HT') {
3094  $opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
3095  $outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
3096  } else {
3097  $opt .= ' - '.price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
3098  $outval .= ' - '.price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
3099  }
3100  $outprice_ht = price($objp->price);
3101  $outprice_ttc = price($objp->price_ttc);
3102  $outpricebasetype = $objp->price_base_type;
3103  $outtva_tx = $objp->tva_tx;
3104  $outdefault_vat_code = $objp->default_vat_code;
3105  }
3106 
3107  if (!empty($conf->stock->enabled) && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3108  if (!empty($user->rights->stock->lire)) {
3109  $opt .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
3110 
3111  if ($objp->stock > 0) {
3112  $outval .= ' - <span class="product_line_stock_ok">';
3113  } elseif ($objp->stock <= 0) {
3114  $outval .= ' - <span class="product_line_stock_too_low">';
3115  }
3116  $outval .= $langs->transnoentities("Stock").': '.price(price2num($objp->stock, 'MS'));
3117  $outval .= '</span>';
3118  if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) { // Warning, this option may slow down combo list generation
3119  $langs->load("stocks");
3120 
3121  $tmpproduct = new Product($this->db);
3122  $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3123  $tmpproduct->load_virtual_stock();
3124  $virtualstock = $tmpproduct->stock_theorique;
3125 
3126  $opt .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
3127 
3128  $outval .= ' - '.$langs->transnoentities("VirtualStock").':';
3129  if ($virtualstock > 0) {
3130  $outval .= '<span class="product_line_stock_ok">';
3131  } elseif ($virtualstock <= 0) {
3132  $outval .= '<span class="product_line_stock_too_low">';
3133  }
3134  $outval .= $virtualstock;
3135  $outval .= '</span>';
3136 
3137  unset($tmpproduct);
3138  }
3139  }
3140  }
3141 
3142  $opt .= "</option>\n";
3143  $optJson = array(
3144  'key'=>$outkey,
3145  'value'=>$outref,
3146  'label'=>$outval,
3147  'label2'=>$outlabel,
3148  'desc'=>$outdesc,
3149  'type'=>$outtype,
3150  'price_ht'=>price2num($outprice_ht),
3151  'price_ttc'=>price2num($outprice_ttc),
3152  'price_ht_locale'=>price(price2num($outprice_ht)),
3153  'price_ttc_locale'=>price(price2num($outprice_ttc)),
3154  'pricebasetype'=>$outpricebasetype,
3155  'tva_tx'=>$outtva_tx,
3156  'default_vat_code'=>$outdefault_vat_code,
3157  'qty'=>$outqty,
3158  'discount'=>$outdiscount,
3159  'duration_value'=>$outdurationvalue,
3160  'duration_unit'=>$outdurationunit,
3161  'pbq'=>$outpbq,
3162  'labeltrans'=>$outlabel_translated,
3163  'desctrans'=>$outdesc_translated,
3164  'ref_customer'=>$outrefcust
3165  );
3166  }
3167 
3168  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3184  public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '')
3185  {
3186  // phpcs:enable
3187  global $langs, $conf;
3188  global $price_level, $status, $finished;
3189 
3190  if (!isset($status)) {
3191  $status = 1;
3192  }
3193 
3194  $selected_input_value = '';
3195  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
3196  if ($selected > 0) {
3197  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3198  $producttmpselect = new Product($this->db);
3199  $producttmpselect->fetch($selected);
3200  $selected_input_value = $producttmpselect->ref;
3201  unset($producttmpselect);
3202  }
3203 
3204  // mode=2 means suppliers products
3205  $urloption = ($socid > 0 ? 'socid='.$socid.'&' : '').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=2&status='.$status.'&finished='.$finished.'&alsoproductwithnosupplierprice='.$alsoproductwithnosupplierprice;
3206  print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
3207  print ($hidelabel ? '' : $langs->trans("RefOrLabel").' : ').'<input type="text" class="minwidth300" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.($placeholder ? ' placeholder="'.$placeholder.'"' : '').'>';
3208  } else {
3209  print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, 0, $placeholder);
3210  }
3211  }
3212 
3213  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3232  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 = '')
3233  {
3234  // phpcs:enable
3235  global $langs, $conf, $user;
3236  global $hookmanager;
3237 
3238  $out = '';
3239  $outarray = array();
3240 
3241  $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3242 
3243  $langs->load('stocks');
3244  // Units
3245  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3246  $langs->load('other');
3247  }
3248 
3249  $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, p.fk_product_type, p.stock,";
3250  $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,";
3251  $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.default_vat_code, pfp.fk_soc, s.nom as name,";
3252  $sql .= " pfp.supplier_reputation";
3253  // if we use supplier description of the products
3254  if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3255  $sql .= ", pfp.desc_fourn as description";
3256  } else {
3257  $sql .= ", p.description";
3258  }
3259  // Units
3260  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3261  $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";
3262  }
3263  if (!empty($conf->barcode->enabled)) {
3264  $sql .= ", pfp.barcode";
3265  }
3266  $sql .= " FROM ".$this->db->prefix()."product as p";
3267  $sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (".getEntity('product').") )";
3268  if ($socid > 0) {
3269  $sql .= " AND pfp.fk_soc = ".((int) $socid);
3270  }
3271  $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON pfp.fk_soc = s.rowid";
3272  // Units
3273  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3274  $sql .= " LEFT JOIN ".$this->db->prefix()."c_units u ON u.rowid = p.fk_unit";
3275  }
3276  $sql .= " WHERE p.entity IN (".getEntity('product').")";
3277  if ($statut != -1) {
3278  $sql .= " AND p.tobuy = ".((int) $statut);
3279  }
3280  if (strval($filtertype) != '') {
3281  $sql .= " AND p.fk_product_type = ".((int) $filtertype);
3282  }
3283  if (!empty($filtre)) {
3284  $sql .= " ".$filtre;
3285  }
3286  // Add where from hooks
3287  $parameters = array();
3288  $reshook = $hookmanager->executeHooks('selectSuppliersProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3289  $sql .= $hookmanager->resPrint;
3290  // Add criteria on ref/label
3291  if ($filterkey != '') {
3292  $sql .= ' AND (';
3293  $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3294  // For natural search
3295  $scrit = explode(' ', $filterkey);
3296  $i = 0;
3297  if (count($scrit) > 1) {
3298  $sql .= "(";
3299  }
3300  foreach ($scrit as $crit) {
3301  if ($i > 0) {
3302  $sql .= " AND ";
3303  }
3304  $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)."%'";
3305  if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3306  $sql .= " OR pfp.desc_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
3307  }
3308  $sql .= ")";
3309  $i++;
3310  }
3311  if (count($scrit) > 1) {
3312  $sql .= ")";
3313  }
3314  if (!empty($conf->barcode->enabled)) {
3315  $sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3316  $sql .= " OR pfp.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3317  }
3318  $sql .= ')';
3319  }
3320  $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
3321  $sql .= $this->db->plimit($limit, 0);
3322 
3323  // Build output string
3324 
3325  dol_syslog(get_class($this)."::select_produits_fournisseurs_list", LOG_DEBUG);
3326  $result = $this->db->query($sql);
3327  if ($result) {
3328  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3329  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
3330 
3331  $num = $this->db->num_rows($result);
3332 
3333  //$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">'; // remove select to have id same with combo and ajax
3334  $out .= '<select class="flat '.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.'">';
3335  if (!$selected) {
3336  $out .= '<option value="-1" selected>'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3337  } else {
3338  $out .= '<option value="-1">'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3339  }
3340 
3341  $i = 0;
3342  while ($i < $num) {
3343  $objp = $this->db->fetch_object($result);
3344 
3345  $outkey = $objp->idprodfournprice; // id in table of price
3346  if (!$outkey && $alsoproductwithnosupplierprice) {
3347  $outkey = 'idprod_'.$objp->rowid; // id of product
3348  }
3349 
3350  $outref = $objp->ref;
3351  $outval = '';
3352  $outbarcode = $objp->barcode;
3353  $outqty = 1;
3354  $outdiscount = 0;
3355  $outtype = $objp->fk_product_type;
3356  $outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3357  $outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
3358 
3359  // Units
3360  $outvalUnits = '';
3361  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
3362  if (!empty($objp->unit_short)) {
3363  $outvalUnits .= ' - '.$objp->unit_short;
3364  }
3365  if (!empty($objp->weight) && $objp->weight_units !== null) {
3366  $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3367  $outvalUnits .= ' - '.$unitToShow;
3368  }
3369  if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3370  $unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
3371  $outvalUnits .= ' - '.$unitToShow;
3372  }
3373  if (!empty($objp->surface) && $objp->surface_units !== null) {
3374  $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3375  $outvalUnits .= ' - '.$unitToShow;
3376  }
3377  if (!empty($objp->volume) && $objp->volume_units !== null) {
3378  $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3379  $outvalUnits .= ' - '.$unitToShow;
3380  }
3381  if ($outdurationvalue && $outdurationunit) {
3382  $da = array(
3383  'h' => $langs->trans('Hour'),
3384  'd' => $langs->trans('Day'),
3385  'w' => $langs->trans('Week'),
3386  'm' => $langs->trans('Month'),
3387  'y' => $langs->trans('Year')
3388  );
3389  if (isset($da[$outdurationunit])) {
3390  $outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
3391  }
3392  }
3393  }
3394 
3395  $objRef = $objp->ref;
3396  if ($filterkey && $filterkey != '') {
3397  $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
3398  }
3399  $objRefFourn = $objp->ref_fourn;
3400  if ($filterkey && $filterkey != '') {
3401  $objRefFourn = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRefFourn, 1);
3402  }
3403  $label = $objp->label;
3404  if ($filterkey && $filterkey != '') {
3405  $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
3406  }
3407 
3408  $optlabel = $objp->ref;
3409  if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3410  $optlabel .= ' <span class="opacitymedium">('.$objp->ref_fourn.')</span>';
3411  }
3412  if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) {
3413  $optlabel .= ' ('.$outbarcode.')';
3414  }
3415  $optlabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3416 
3417  $outvallabel = $objRef;
3418  if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3419  $outvallabel .= ' ('.$objRefFourn.')';
3420  }
3421  if (!empty($conf->barcode->enabled) && !empty($objp->barcode)) {
3422  $outvallabel .= ' ('.$outbarcode.')';
3423  }
3424  $outvallabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3425 
3426  // Units
3427  $optlabel .= $outvalUnits;
3428  $outvallabel .= $outvalUnits;
3429 
3430  if (!empty($objp->idprodfournprice)) {
3431  $outqty = $objp->quantity;
3432  $outdiscount = $objp->remise_percent;
3433  if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
3434  $prod_supplier = new ProductFournisseur($this->db);
3435  $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3436  $prod_supplier->id = $objp->fk_product;
3437  $prod_supplier->fourn_qty = $objp->quantity;
3438  $prod_supplier->fourn_tva_tx = $objp->tva_tx;
3439  $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3440  $priceparser = new PriceParser($this->db);
3441  $price_result = $priceparser->parseProductSupplier($prod_supplier);
3442  if ($price_result >= 0) {
3443  $objp->fprice = $price_result;
3444  if ($objp->quantity >= 1) {
3445  $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
3446  }
3447  }
3448  }
3449  if ($objp->quantity == 1) {
3450  $optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3451  $outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/";
3452  $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3453  $outvallabel .= $langs->transnoentities("Unit");
3454  } else {
3455  $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;
3456  $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;
3457  $optlabel .= ' '.$langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3458  $outvallabel .= ' '.$langs->transnoentities("Units");
3459  }
3460 
3461  if ($objp->quantity > 1) {
3462  $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
3463  $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
3464  }
3465  if ($objp->remise_percent >= 1) {
3466  $optlabel .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
3467  $outvallabel .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
3468  }
3469  if ($objp->duration) {
3470  $optlabel .= " - ".$objp->duration;
3471  $outvallabel .= " - ".$objp->duration;
3472  }
3473  if (!$socid) {
3474  $optlabel .= " - ".dol_trunc($objp->name, 8);
3475  $outvallabel .= " - ".dol_trunc($objp->name, 8);
3476  }
3477  if ($objp->supplier_reputation) {
3478  //TODO dictionary
3479  $reputations = array(''=>$langs->trans('Standard'), 'FAVORITE'=>$langs->trans('Favorite'), 'NOTTHGOOD'=>$langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER'=>$langs->trans('DoNotOrderThisProductToThisSupplier'));
3480 
3481  $optlabel .= " - ".$reputations[$objp->supplier_reputation];
3482  $outvallabel .= " - ".$reputations[$objp->supplier_reputation];
3483  }
3484  } else {
3485  if (empty($alsoproductwithnosupplierprice)) { // No supplier price defined for couple product/supplier
3486  $optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3487  $outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3488  } else // No supplier price defined for product, even on other suppliers
3489  {
3490  $optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3491  $outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3492  }
3493  }
3494 
3495  if (!empty($conf->stock->enabled) && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3496  $novirtualstock = ($showstockinlist == 2);
3497 
3498  if (!empty($user->rights->stock->lire)) {
3499  $outvallabel .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
3500 
3501  if ($objp->stock > 0) {
3502  $optlabel .= ' - <span class="product_line_stock_ok">';
3503  } elseif ($objp->stock <= 0) {
3504  $optlabel .= ' - <span class="product_line_stock_too_low">';
3505  }
3506  $optlabel .= $langs->transnoentities("Stock").':'.price(price2num($objp->stock, 'MS'));
3507  $optlabel .= '</span>';
3508  if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) { // Warning, this option may slow down combo list generation
3509  $langs->load("stocks");
3510 
3511  $tmpproduct = new Product($this->db);
3512  $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3513  $tmpproduct->load_virtual_stock();
3514  $virtualstock = $tmpproduct->stock_theorique;
3515 
3516  $outvallabel .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
3517 
3518  $optlabel .= ' - '.$langs->transnoentities("VirtualStock").':';
3519  if ($virtualstock > 0) {
3520  $optlabel .= '<span class="product_line_stock_ok">';
3521  } elseif ($virtualstock <= 0) {
3522  $optlabel .= '<span class="product_line_stock_too_low">';
3523  }
3524  $optlabel .= $virtualstock;
3525  $optlabel .= '</span>';
3526 
3527  unset($tmpproduct);
3528  }
3529  }
3530  }
3531 
3532  $opt = '<option value="'.$outkey.'"';
3533  if ($selected && $selected == $objp->idprodfournprice) {
3534  $opt .= ' selected';
3535  }
3536  if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
3537  $opt .= ' disabled';
3538  }
3539  if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
3540  $opt .= ' data-product-id="'.dol_escape_htmltag($objp->rowid).'"';
3541  $opt .= ' data-price-id="'.dol_escape_htmltag($objp->idprodfournprice).'"';
3542  $opt .= ' data-qty="'.dol_escape_htmltag($objp->quantity).'"';
3543  $opt .= ' data-up="'.dol_escape_htmltag($objp->unitprice).'"';
3544  $opt .= ' data-up-locale="'.dol_escape_htmltag(price($objp->unitprice)).'"';
3545  $opt .= ' data-discount="'.dol_escape_htmltag($outdiscount).'"';
3546  $opt .= ' data-tvatx="'.dol_escape_htmltag($objp->tva_tx).'"';
3547  }
3548  $opt .= ' data-description="'.dol_escape_htmltag($objp->description, 0, 1).'"';
3549  $opt .= ' data-html="'.dol_escape_htmltag($optlabel).'"';
3550  $opt .= '>';
3551 
3552  $opt .= $optlabel;
3553  $outval .= $outvallabel;
3554 
3555  $opt .= "</option>\n";
3556 
3557  // Add new entry
3558  // "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
3559  // "label" value of json key array is used by jQuery automatically as text for combo box
3560  $out .= $opt;
3561  array_push(
3562  $outarray,
3563  array('key'=>$outkey,
3564  'value'=>$outref,
3565  'label'=>$outval,
3566  'qty'=>$outqty,
3567  'price_qty_ht'=>price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
3568  'price_unit_ht'=>price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
3569  'price_ht'=>price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
3570  'price_qty_ht_locale'=>price($objp->fprice),
3571  'price_unit_ht_locale'=>price($objp->unitprice),
3572  'tva_tx'=>$objp->tva_tx,
3573  'default_vat_code'=>$objp->default_vat_code,
3574  'discount'=>$outdiscount,
3575  'type'=>$outtype,
3576  'duration_value'=>$outdurationvalue,
3577  'duration_unit'=>$outdurationunit,
3578  'disabled'=>(empty($objp->idprodfournprice) ? true : false),
3579  'description'=>$objp->description
3580  )
3581  );
3582  // Exemple of var_dump $outarray
3583  // array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
3584  // ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)"
3585  // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
3586  //}
3587  //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3588  //$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)');
3589  //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3590 
3591  $i++;
3592  }
3593  $out .= '</select>';
3594 
3595  $this->db->free($result);
3596 
3597  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
3598  $out .= ajax_combobox($htmlname);
3599 
3600  if (empty($outputmode)) {
3601  return $out;
3602  }
3603  return $outarray;
3604  } else {
3605  dol_print_error($this->db);
3606  }
3607  }
3608 
3609  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3618  public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = '')
3619  {
3620  // phpcs:enable
3621  global $langs, $conf;
3622 
3623  $langs->load('stocks');
3624 
3625  $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
3626  $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
3627  $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
3628  $sql .= " FROM ".$this->db->prefix()."product as p";
3629  $sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
3630  $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON pfp.fk_soc = s.rowid";
3631  $sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")";
3632  $sql .= " AND p.tobuy = 1";
3633  $sql .= " AND s.fournisseur = 1";
3634  $sql .= " AND p.rowid = ".((int) $productid);
3635  $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
3636 
3637  dol_syslog(get_class($this)."::select_product_fourn_price", LOG_DEBUG);
3638  $result = $this->db->query($sql);
3639 
3640  if ($result) {
3641  $num = $this->db->num_rows($result);
3642 
3643  $form = '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3644 
3645  if (!$num) {
3646  $form .= '<option value="0">-- '.$langs->trans("NoSupplierPriceDefinedForThisProduct").' --</option>';
3647  } else {
3648  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3649  $form .= '<option value="0">&nbsp;</option>';
3650 
3651  $i = 0;
3652  while ($i < $num) {
3653  $objp = $this->db->fetch_object($result);
3654 
3655  $opt = '<option value="'.$objp->idprodfournprice.'"';
3656  //if there is only one supplier, preselect it
3657  if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier)) {
3658  $opt .= ' selected';
3659  }
3660  $opt .= '>'.$objp->name.' - '.$objp->ref_fourn.' - ';
3661 
3662  if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
3663  $prod_supplier = new ProductFournisseur($this->db);
3664  $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3665  $prod_supplier->id = $productid;
3666  $prod_supplier->fourn_qty = $objp->quantity;
3667  $prod_supplier->fourn_tva_tx = $objp->tva_tx;
3668  $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3669  $priceparser = new PriceParser($this->db);
3670  $price_result = $priceparser->parseProductSupplier($prod_supplier);
3671  if ($price_result >= 0) {
3672  $objp->fprice = $price_result;
3673  if ($objp->quantity >= 1) {
3674  $objp->unitprice = $objp->fprice / $objp->quantity;
3675  }
3676  }
3677  }
3678  if ($objp->quantity == 1) {
3679  $opt .= price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3680  }
3681 
3682  $opt .= $objp->quantity.' ';
3683 
3684  if ($objp->quantity == 1) {
3685  $opt .= $langs->trans("Unit");
3686  } else {
3687  $opt .= $langs->trans("Units");
3688  }
3689  if ($objp->quantity > 1) {
3690  $opt .= " - ";
3691  $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");
3692  }
3693  if ($objp->duration) {
3694  $opt .= " - ".$objp->duration;
3695  }
3696  $opt .= "</option>\n";
3697 
3698  $form .= $opt;
3699  $i++;
3700  }
3701  }
3702 
3703  $form .= '</select>';
3704  $this->db->free($result);
3705  return $form;
3706  } else {
3707  dol_print_error($this->db);
3708  }
3709  }
3710 
3711  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3721  public function select_address($selected, $socid, $htmlname = 'address_id', $showempty = 0)
3722  {
3723  // phpcs:enable
3724  // looking for users
3725  $sql = "SELECT a.rowid, a.label";
3726  $sql .= " FROM ".$this->db->prefix()."societe_address as a";
3727  $sql .= " WHERE a.fk_soc = ".((int) $socid);
3728  $sql .= " ORDER BY a.label ASC";
3729 
3730  dol_syslog(get_class($this)."::select_address", LOG_DEBUG);
3731  $resql = $this->db->query($sql);
3732  if ($resql) {
3733  print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3734  if ($showempty) {
3735  print '<option value="0">&nbsp;</option>';
3736  }
3737  $num = $this->db->num_rows($resql);
3738  $i = 0;
3739  if ($num) {
3740  while ($i < $num) {
3741  $obj = $this->db->fetch_object($resql);
3742 
3743  if ($selected && $selected == $obj->rowid) {
3744  print '<option value="'.$obj->rowid.'" selected>'.$obj->label.'</option>';
3745  } else {
3746  print '<option value="'.$obj->rowid.'">'.$obj->label.'</option>';
3747  }
3748  $i++;
3749  }
3750  }
3751  print '</select>';
3752  return $num;
3753  } else {
3754  dol_print_error($this->db);
3755  }
3756  }
3757 
3758  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3765  {
3766  // phpcs:enable
3767  global $langs;
3768 
3769  $num = count($this->cache_conditions_paiements);
3770  if ($num > 0) {
3771  return 0; // Cache already loaded
3772  }
3773 
3774  dol_syslog(__METHOD__, LOG_DEBUG);
3775 
3776  $sql = "SELECT rowid, code, libelle as label, deposit_percent";
3777  $sql .= " FROM ".$this->db->prefix().'c_payment_term';
3778  $sql .= " WHERE entity IN (".getEntity('c_payment_term').")";
3779  $sql .= " AND active > 0";
3780  $sql .= " ORDER BY sortorder";
3781 
3782  $resql = $this->db->query($sql);
3783  if ($resql) {
3784  $num = $this->db->num_rows($resql);
3785  $i = 0;
3786  while ($i < $num) {
3787  $obj = $this->db->fetch_object($resql);
3788 
3789  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3790  $label = ($langs->trans("PaymentConditionShort".$obj->code) != ("PaymentConditionShort".$obj->code) ? $langs->trans("PaymentConditionShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3791  $this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code;
3792  $this->cache_conditions_paiements[$obj->rowid]['label'] = $label;
3793  $this->cache_conditions_paiements[$obj->rowid]['deposit_percent'] = $obj->deposit_percent;
3794  $i++;
3795  }
3796 
3797  //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table
3798 
3799  return $num;
3800  } else {
3801  dol_print_error($this->db);
3802  return -1;
3803  }
3804  }
3805 
3806  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3812  public function load_cache_availability()
3813  {
3814  // phpcs:enable
3815  global $langs;
3816 
3817  $num = count($this->cache_availability); // TODO Use $conf->cache['availability'] instead of $this->cache_availability
3818  if ($num > 0) {
3819  return 0; // Cache already loaded
3820  }
3821 
3822  dol_syslog(__METHOD__, LOG_DEBUG);
3823 
3824  $langs->load('propal');
3825 
3826  $sql = "SELECT rowid, code, label, position";
3827  $sql .= " FROM ".$this->db->prefix().'c_availability';
3828  $sql .= " WHERE active > 0";
3829 
3830  $resql = $this->db->query($sql);
3831  if ($resql) {
3832  $num = $this->db->num_rows($resql);
3833  $i = 0;
3834  while ($i < $num) {
3835  $obj = $this->db->fetch_object($resql);
3836 
3837  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3838  $label = ($langs->trans("AvailabilityType".$obj->code) != ("AvailabilityType".$obj->code) ? $langs->trans("AvailabilityType".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3839  $this->cache_availability[$obj->rowid]['code'] = $obj->code;
3840  $this->cache_availability[$obj->rowid]['label'] = $label;
3841  $this->cache_availability[$obj->rowid]['position'] = $obj->position;
3842  $i++;
3843  }
3844 
3845  $this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
3846 
3847  return $num;
3848  } else {
3849  dol_print_error($this->db);
3850  return -1;
3851  }
3852  }
3853 
3864  public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '')
3865  {
3866  global $langs, $user;
3867 
3868  $this->load_cache_availability();
3869 
3870  dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
3871 
3872  print '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3873  if ($addempty) {
3874  print '<option value="0">&nbsp;</option>';
3875  }
3876  foreach ($this->cache_availability as $id => $arrayavailability) {
3877  if ($selected == $id) {
3878  print '<option value="'.$id.'" selected>';
3879  } else {
3880  print '<option value="'.$id.'">';
3881  }
3882  print dol_escape_htmltag($arrayavailability['label']);
3883  print '</option>';
3884  }
3885  print '</select>';
3886  if ($user->admin) {
3887  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3888  }
3889  print ajax_combobox($htmlname);
3890  }
3891 
3897  public function loadCacheInputReason()
3898  {
3899  global $langs;
3900 
3901  $num = count($this->cache_demand_reason); // TODO Use $conf->cache['input_reason'] instead of $this->cache_demand_reason
3902  if ($num > 0) {
3903  return 0; // Cache already loaded
3904  }
3905 
3906  $sql = "SELECT rowid, code, label";
3907  $sql .= " FROM ".$this->db->prefix().'c_input_reason';
3908  $sql .= " WHERE active > 0";
3909 
3910  $resql = $this->db->query($sql);
3911  if ($resql) {
3912  $num = $this->db->num_rows($resql);
3913  $i = 0;
3914  $tmparray = array();
3915  while ($i < $num) {
3916  $obj = $this->db->fetch_object($resql);
3917 
3918  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3919  $label = ($obj->label != '-' ? $obj->label : '');
3920  if ($langs->trans("DemandReasonType".$obj->code) != ("DemandReasonType".$obj->code)) {
3921  $label = $langs->trans("DemandReasonType".$obj->code); // So translation key DemandReasonTypeSRC_XXX will work
3922  }
3923  if ($langs->trans($obj->code) != $obj->code) {
3924  $label = $langs->trans($obj->code); // So translation key SRC_XXX will work
3925  }
3926 
3927  $tmparray[$obj->rowid]['id'] = $obj->rowid;
3928  $tmparray[$obj->rowid]['code'] = $obj->code;
3929  $tmparray[$obj->rowid]['label'] = $label;
3930  $i++;
3931  }
3932 
3933  $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
3934 
3935  unset($tmparray);
3936  return $num;
3937  } else {
3938  dol_print_error($this->db);
3939  return -1;
3940  }
3941  }
3942 
3955  public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
3956  {
3957  global $langs, $user;
3958 
3959  $this->loadCacheInputReason();
3960 
3961  print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3962  if ($addempty) {
3963  print '<option value="0"'.(empty($selected) ? ' selected' : '').'>&nbsp;</option>';
3964  }
3965  foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
3966  if ($arraydemandreason['code'] == $exclude) {
3967  continue;
3968  }
3969 
3970  if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
3971  print '<option value="'.$arraydemandreason['id'].'" selected>';
3972  } else {
3973  print '<option value="'.$arraydemandreason['id'].'">';
3974  }
3975  $label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
3976  print $langs->trans($label);
3977  print '</option>';
3978  }
3979  print '</select>';
3980  if ($user->admin && empty($notooltip)) {
3981  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3982  }
3983  print ajax_combobox('select_'.$htmlname);
3984  }
3985 
3986  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3992  public function load_cache_types_paiements()
3993  {
3994  // phpcs:enable
3995  global $langs;
3996 
3997  $num = count($this->cache_types_paiements); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_types_paiements
3998  if ($num > 0) {
3999  return $num; // Cache already loaded
4000  }
4001 
4002  dol_syslog(__METHOD__, LOG_DEBUG);
4003 
4004  $this->cache_types_paiements = array();
4005 
4006  $sql = "SELECT id, code, libelle as label, type, active";
4007  $sql .= " FROM ".$this->db->prefix()."c_paiement";
4008  $sql .= " WHERE entity IN (".getEntity('c_paiement').")";
4009 
4010  $resql = $this->db->query($sql);
4011  if ($resql) {
4012  $num = $this->db->num_rows($resql);
4013  $i = 0;
4014  while ($i < $num) {
4015  $obj = $this->db->fetch_object($resql);
4016 
4017  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
4018  $label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
4019  $this->cache_types_paiements[$obj->id]['id'] = $obj->id;
4020  $this->cache_types_paiements[$obj->id]['code'] = $obj->code;
4021  $this->cache_types_paiements[$obj->id]['label'] = $label;
4022  $this->cache_types_paiements[$obj->id]['type'] = $obj->type;
4023  $this->cache_types_paiements[$obj->id]['active'] = $obj->active;
4024  $i++;
4025  }
4026 
4027  $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
4028 
4029  return $num;
4030  } else {
4031  dol_print_error($this->db);
4032  return -1;
4033  }
4034  }
4035 
4036 
4037  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4054  public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
4055  {
4056  // phpcs:enable
4057  print $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, $noinfoadmin, $morecss, $deposit_percent);
4058  }
4059 
4060 
4077  public function getSelectConditionsPaiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
4078  {
4079  global $langs, $user, $conf;
4080 
4081  $out = '';
4082  dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
4083 
4085 
4086  // Set default value if not already set by caller
4087  if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) {
4088  $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID;
4089  }
4090 
4091  $out.= '<select id="'.$htmlname.'" class="flat selectpaymentterms'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4092  if ($addempty) {
4093  $out.= '<option value="0">&nbsp;</option>';
4094  }
4095 
4096  $selectedDepositPercent = null;
4097 
4098  foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
4099  if ($filtertype <= 0 && ! empty($arrayconditions['deposit_percent'])) {
4100  continue;
4101  }
4102 
4103  if ($selected == $id) {
4104  $selectedDepositPercent = $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'];
4105  $out .= '<option value="'.$id.'" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '" selected>';
4106  } else {
4107  $out .= '<option value="'.$id.'" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '">';
4108  }
4109  $label = $arrayconditions['label'];
4110 
4111  if (! empty($arrayconditions['deposit_percent'])) {
4112  $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'], $label);
4113  }
4114 
4115  $out.= $label;
4116  $out.= '</option>';
4117  }
4118  $out.= '</select>';
4119  if ($user->admin && empty($noinfoadmin)) {
4120  $out.= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4121  }
4122  $out.= ajax_combobox($htmlname);
4123 
4124  if ($deposit_percent >= 0) {
4125  $out .= ' <span id="'.$htmlname.'_deposit_percent_container"' . (empty($selectedDepositPercent) ? ' style="display: none"' : '') . '>';
4126  $out .= $langs->trans('DepositPercent') . ' : ';
4127  $out .= '<input id="'.$htmlname.'_deposit_percent" name="'.$htmlname.'_deposit_percent" class="maxwidth50" value="' . strval($deposit_percent) . '" />';
4128  $out .= '</span>';
4129  $out .= '
4130  <script>
4131  $(document).ready(function () {
4132  $("#' . $htmlname . '").change(function () {
4133  let $selected = $(this).find("option:selected");
4134  let depositPercent = $selected.attr("data-deposit_percent");
4135 
4136  if (depositPercent.length > 0) {
4137  $("#'.$htmlname.'_deposit_percent_container").show().find("#'.$htmlname.'_deposit_percent").val(depositPercent);
4138  } else {
4139  $("#'.$htmlname.'_deposit_percent_container").hide();
4140  }
4141 
4142  return true;
4143  });
4144  });
4145  </script>';
4146  }
4147 
4148  return $out;
4149  }
4150 
4151 
4152  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4169  public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
4170  {
4171  // phpcs:enable
4172  global $langs, $user, $conf;
4173 
4174  $out = '';
4175 
4176  dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
4177 
4178  $filterarray = array();
4179  if ($filtertype == 'CRDT') {
4180  $filterarray = array(0, 2, 3);
4181  } elseif ($filtertype == 'DBIT') {
4182  $filterarray = array(1, 2, 3);
4183  } elseif ($filtertype != '' && $filtertype != '-1') {
4184  $filterarray = explode(',', $filtertype);
4185  }
4186 
4187  $this->load_cache_types_paiements();
4188 
4189  // Set default value if not already set by caller
4190  if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID)) {
4191  $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID;
4192  }
4193 
4194  $out .= '<select id="select'.$htmlname.'" class="flat selectpaymenttypes'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4195  if ($empty) {
4196  $out .= '<option value="">&nbsp;</option>';
4197  }
4198  foreach ($this->cache_types_paiements as $id => $arraytypes) {
4199  // If not good status
4200  if ($active >= 0 && $arraytypes['active'] != $active) {
4201  continue;
4202  }
4203 
4204  // On passe si on a demande de filtrer sur des modes de paiments particuliers
4205  if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
4206  continue;
4207  }
4208 
4209  // We discard empty line if showempty is on because an empty line has already been output.
4210  if ($empty && empty($arraytypes['code'])) {
4211  continue;
4212  }
4213 
4214  if ($format == 0) {
4215  $out .= '<option value="'.$id.'"';
4216  } elseif ($format == 1) {
4217  $out .= '<option value="'.$arraytypes['code'].'"';
4218  } elseif ($format == 2) {
4219  $out .= '<option value="'.$arraytypes['code'].'"';
4220  } elseif ($format == 3) {
4221  $out .= '<option value="'.$id.'"';
4222  }
4223  // Print attribute selected or not
4224  if ($format == 1 || $format == 2) {
4225  if ($selected == $arraytypes['code']) {
4226  $out .= ' selected';
4227  }
4228  } else {
4229  if ($selected == $id) {
4230  $out .= ' selected';
4231  }
4232  }
4233  $out .= '>';
4234  $value = '';
4235  if ($format == 0) {
4236  $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4237  } elseif ($format == 1) {
4238  $value = $arraytypes['code'];
4239  } elseif ($format == 2) {
4240  $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4241  } elseif ($format == 3) {
4242  $value = $arraytypes['code'];
4243  }
4244  $out .= $value ? $value : '&nbsp;';
4245  $out .= '</option>';
4246  }
4247  $out .= '</select>';
4248  if ($user->admin && !$noadmininfo) {
4249  $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4250  }
4251  $out .= ajax_combobox('select'.$htmlname);
4252 
4253  if (empty($nooutput)) {
4254  print $out;
4255  } else {
4256  return $out;
4257  }
4258  }
4259 
4260 
4269  public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
4270  {
4271  global $langs;
4272 
4273  $return = '<select class="flat maxwidth100" id="select_'.$htmlname.'" name="'.$htmlname.'">';
4274  $options = array(
4275  'HT'=>$langs->trans("HT"),
4276  'TTC'=>$langs->trans("TTC")
4277  );
4278  foreach ($options as $id => $value) {
4279  if ($selected == $id) {
4280  $return .= '<option value="'.$id.'" selected>'.$value;
4281  } else {
4282  $return .= '<option value="'.$id.'">'.$value;
4283  }
4284  $return .= '</option>';
4285  }
4286  $return .= '</select>';
4287  if ($addjscombo) {
4288  $return .= ajax_combobox('select_'.$htmlname);
4289  }
4290 
4291  return $return;
4292  }
4293 
4294  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4300  public function load_cache_transport_mode()
4301  {
4302  // phpcs:enable
4303  global $langs;
4304 
4305  $num = count($this->cache_transport_mode); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_transport_mode
4306  if ($num > 0) {
4307  return $num; // Cache already loaded
4308  }
4309 
4310  dol_syslog(__METHOD__, LOG_DEBUG);
4311 
4312  $this->cache_transport_mode = array();
4313 
4314  $sql = "SELECT rowid, code, label, active";
4315  $sql .= " FROM ".$this->db->prefix()."c_transport_mode";
4316  $sql .= " WHERE entity IN (".getEntity('c_transport_mode').")";
4317 
4318  $resql = $this->db->query($sql);
4319  if ($resql) {
4320  $num = $this->db->num_rows($resql);
4321  $i = 0;
4322  while ($i < $num) {
4323  $obj = $this->db->fetch_object($resql);
4324 
4325  // If traduction exist, we use it else we take the default label
4326  $label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
4327  $this->cache_transport_mode[$obj->rowid]['rowid'] = $obj->rowid;
4328  $this->cache_transport_mode[$obj->rowid]['code'] = $obj->code;
4329  $this->cache_transport_mode[$obj->rowid]['label'] = $label;
4330  $this->cache_transport_mode[$obj->rowid]['active'] = $obj->active;
4331  $i++;
4332  }
4333 
4334  $this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
4335 
4336  return $num;
4337  } else {
4338  dol_print_error($this->db);
4339  return -1;
4340  }
4341  }
4342 
4356  public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
4357  {
4358  global $langs, $user;
4359 
4360  dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$format, LOG_DEBUG);
4361 
4362  $this->load_cache_transport_mode();
4363 
4364  print '<select id="select'.$htmlname.'" class="flat selectmodetransport'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4365  if ($empty) {
4366  print '<option value="">&nbsp;</option>';
4367  }
4368  foreach ($this->cache_transport_mode as $id => $arraytypes) {
4369  // If not good status
4370  if ($active >= 0 && $arraytypes['active'] != $active) {
4371  continue;
4372  }
4373 
4374  // We discard empty line if showempty is on because an empty line has already been output.
4375  if ($empty && empty($arraytypes['code'])) {
4376  continue;
4377  }
4378 
4379  if ($format == 0) {
4380  print '<option value="'.$id.'"';
4381  } elseif ($format == 1) {
4382  print '<option value="'.$arraytypes['code'].'"';
4383  } elseif ($format == 2) {
4384  print '<option value="'.$arraytypes['code'].'"';
4385  } elseif ($format == 3) {
4386  print '<option value="'.$id.'"';
4387  }
4388  // If text is selected, we compare with code, else with id
4389  if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
4390  print ' selected';
4391  } elseif ($selected == $id) {
4392  print ' selected';
4393  }
4394  print '>';
4395  $value = '';
4396  if ($format == 0) {
4397  $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4398  } elseif ($format == 1) {
4399  $value = $arraytypes['code'];
4400  } elseif ($format == 2) {
4401  $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4402  } elseif ($format == 3) {
4403  $value = $arraytypes['code'];
4404  }
4405  print $value ? $value : '&nbsp;';
4406  print '</option>';
4407  }
4408  print '</select>';
4409  if ($user->admin && !$noadmininfo) {
4410  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4411  }
4412  }
4413 
4426  public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
4427  {
4428  global $langs, $conf, $user;
4429 
4430  $langs->load("admin");
4431  $langs->load("deliveries");
4432 
4433  $sql = "SELECT rowid, code, libelle as label";
4434  $sql .= " FROM ".$this->db->prefix()."c_shipment_mode";
4435  $sql .= " WHERE active > 0";
4436  if ($filtre) {
4437  $sql .= " AND ".$filtre;
4438  }
4439  $sql .= " ORDER BY libelle ASC";
4440 
4441  dol_syslog(get_class($this)."::selectShippingMode", LOG_DEBUG);
4442  $result = $this->db->query($sql);
4443  if ($result) {
4444  $num = $this->db->num_rows($result);
4445  $i = 0;
4446  if ($num) {
4447  print '<select id="select'.$htmlname.'" class="flat selectshippingmethod'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4448  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4449  print '<option value="-1">&nbsp;</option>';
4450  }
4451  while ($i < $num) {
4452  $obj = $this->db->fetch_object($result);
4453  if ($selected == $obj->rowid) {
4454  print '<option value="'.$obj->rowid.'" selected>';
4455  } else {
4456  print '<option value="'.$obj->rowid.'">';
4457  }
4458  print ($langs->trans("SendingMethod".strtoupper($obj->code)) != "SendingMethod".strtoupper($obj->code)) ? $langs->trans("SendingMethod".strtoupper($obj->code)) : $obj->label;
4459  print '</option>';
4460  $i++;
4461  }
4462  print "</select>";
4463  if ($user->admin && empty($noinfoadmin)) {
4464  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4465  }
4466 
4467  print ajax_combobox('select'.$htmlname);
4468  } else {
4469  print $langs->trans("NoShippingMethodDefined");
4470  }
4471  } else {
4472  dol_print_error($this->db);
4473  }
4474  }
4475 
4485  public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
4486  {
4487  global $langs;
4488 
4489  $langs->load("deliveries");
4490 
4491  if ($htmlname != "none") {
4492  print '<form method="POST" action="'.$page.'">';
4493  print '<input type="hidden" name="action" value="setshippingmethod">';
4494  print '<input type="hidden" name="token" value="'.newToken().'">';
4495  $this->selectShippingMethod($selected, $htmlname, '', $addempty);
4496  print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
4497  print '</form>';
4498  } else {
4499  if ($selected) {
4500  $code = $langs->getLabelFromKey($this->db, $selected, 'c_shipment_mode', 'rowid', 'code');
4501  print $langs->trans("SendingMethod".strtoupper($code));
4502  } else {
4503  print "&nbsp;";
4504  }
4505  }
4506  }
4507 
4516  public function selectSituationInvoices($selected = '', $socid = 0)
4517  {
4518  global $langs;
4519 
4520  $langs->load('bills');
4521 
4522  $opt = '<option value="" selected></option>';
4523  $sql = "SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc";
4524  $sql .= ' FROM '.$this->db->prefix().'facture';
4525  $sql .= ' WHERE entity IN ('.getEntity('invoice').')';
4526  $sql .= ' AND situation_counter >= 1';
4527  $sql .= ' AND fk_soc = '.(int) $socid;
4528  $sql .= ' AND type <> 2';
4529  $sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
4530  $resql = $this->db->query($sql);
4531 
4532  if ($resql && $this->db->num_rows($resql) > 0) {
4533  // Last seen cycle
4534  $ref = 0;
4535  while ($obj = $this->db->fetch_object($resql)) {
4536  //Same cycle ?
4537  if ($obj->situation_cycle_ref != $ref) {
4538  // Just seen this cycle
4539  $ref = $obj->situation_cycle_ref;
4540  //not final ?
4541  if ($obj->situation_final != 1) {
4542  //Not prov?
4543  if (substr($obj->ref, 1, 4) != 'PROV') {
4544  if ($selected == $obj->rowid) {
4545  $opt .= '<option value="'.$obj->rowid.'" selected>'.$obj->ref.'</option>';
4546  } else {
4547  $opt .= '<option value="'.$obj->rowid.'">'.$obj->ref.'</option>';
4548  }
4549  }
4550  }
4551  }
4552  }
4553  } else {
4554  dol_syslog("Error sql=".$sql.", error=".$this->error, LOG_ERR);
4555  }
4556  if ($opt == '<option value ="" selected></option>') {
4557  $opt = '<option value ="0" selected>'.$langs->trans('NoSituations').'</option>';
4558  }
4559  return $opt;
4560  }
4561 
4571  public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
4572  {
4573  global $langs;
4574 
4575  $langs->load('products');
4576 
4577  $return = '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'">';
4578 
4579  $sql = "SELECT rowid, label, code FROM ".$this->db->prefix()."c_units";
4580  $sql .= ' WHERE active > 0';
4581  if (!empty($unit_type)) {
4582  $sql .= " AND unit_type = '".$this->db->escape($unit_type)."'";
4583  }
4584  $sql .= " ORDER BY sortorder";
4585 
4586  $resql = $this->db->query($sql);
4587  if ($resql && $this->db->num_rows($resql) > 0) {
4588  if ($showempty) {
4589  $return .= '<option value="none"></option>';
4590  }
4591 
4592  while ($res = $this->db->fetch_object($resql)) {
4593  $unitLabel = $res->label;
4594  if (!empty($langs->tab_translate['unit'.$res->code])) { // check if Translation is available before
4595  $unitLabel = $langs->trans('unit'.$res->code) != $res->label ? $langs->trans('unit'.$res->code) : $res->label;
4596  }
4597 
4598  if ($selected == $res->rowid) {
4599  $return .= '<option value="'.$res->rowid.'" selected>'.$unitLabel.'</option>';
4600  } else {
4601  $return .= '<option value="'.$res->rowid.'">'.$unitLabel.'</option>';
4602  }
4603  }
4604  $return .= '</select>';
4605  }
4606  return $return;
4607  }
4608 
4609  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4624  public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0)
4625  {
4626  // phpcs:enable
4627  global $langs, $conf;
4628 
4629  $out = '';
4630 
4631  $langs->load("admin");
4632  $num = 0;
4633 
4634  $sql = "SELECT rowid, label, bank, clos as status, currency_code";
4635  $sql .= " FROM ".$this->db->prefix()."bank_account";
4636  $sql .= " WHERE entity IN (".getEntity('bank_account').")";
4637  if ($status != 2) {
4638  $sql .= " AND clos = ".(int) $status;
4639  }
4640  if ($filtre) {
4641  $sql .= " AND ".$filtre;
4642  }
4643  $sql .= " ORDER BY label";
4644 
4645  dol_syslog(get_class($this)."::select_comptes", LOG_DEBUG);
4646  $result = $this->db->query($sql);
4647  if ($result) {
4648  $num = $this->db->num_rows($result);
4649  $i = 0;
4650  if ($num) {
4651  $out .= '<select id="select'.$htmlname.'" class="flat selectbankaccount'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4652  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4653  $out .= '<option value="-1">&nbsp;</option>';
4654  }
4655 
4656  while ($i < $num) {
4657  $obj = $this->db->fetch_object($result);
4658  if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
4659  $out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'" selected>';
4660  } else {
4661  $out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'">';
4662  }
4663  $out .= trim($obj->label);
4664  if ($showcurrency) {
4665  $out .= ' ('.$obj->currency_code.')';
4666  }
4667  if ($status == 2 && $obj->status == 1) {
4668  $out .= ' ('.$langs->trans("Closed").')';
4669  }
4670  $out .= '</option>';
4671  $i++;
4672  }
4673  $out .= "</select>";
4674  $out .= ajax_combobox('select'.$htmlname);
4675  } else {
4676  if ($status == 0) {
4677  $out .= '<span class="opacitymedium">'.$langs->trans("NoActiveBankAccountDefined").'</span>';
4678  } else {
4679  $out .= '<span class="opacitymedium">'.$langs->trans("NoBankAccountFound").'</span>';
4680  }
4681  }
4682  } else {
4683  dol_print_error($this->db);
4684  }
4685 
4686  // Output or return
4687  if (empty($nooutput)) {
4688  print $out;
4689  } else {
4690  return $out;
4691  }
4692 
4693  return $num;
4694  }
4695 
4707  public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
4708  {
4709  global $langs, $conf;
4710 
4711  $langs->load("admin");
4712  $num = 0;
4713 
4714  $sql = "SELECT rowid, name, fk_country, status, entity";
4715  $sql .= " FROM ".$this->db->prefix()."establishment";
4716  $sql .= " WHERE 1=1";
4717  if ($status != 2) {
4718  $sql .= " AND status = ".(int) $status;
4719  }
4720  if ($filtre) {
4721  $sql .= " AND ".$filtre;
4722  }
4723  $sql .= " ORDER BY name";
4724 
4725  dol_syslog(get_class($this)."::select_establishment", LOG_DEBUG);
4726  $result = $this->db->query($sql);
4727  if ($result) {
4728  $num = $this->db->num_rows($result);
4729  $i = 0;
4730  if ($num) {
4731  print '<select id="select'.$htmlname.'" class="flat selectestablishment" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4732  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4733  print '<option value="-1">&nbsp;</option>';
4734  }
4735 
4736  while ($i < $num) {
4737  $obj = $this->db->fetch_object($result);
4738  if ($selected == $obj->rowid) {
4739  print '<option value="'.$obj->rowid.'" selected>';
4740  } else {
4741  print '<option value="'.$obj->rowid.'">';
4742  }
4743  print trim($obj->name);
4744  if ($status == 2 && $obj->status == 1) {
4745  print ' ('.$langs->trans("Closed").')';
4746  }
4747  print '</option>';
4748  $i++;
4749  }
4750  print "</select>";
4751  } else {
4752  if ($status == 0) {
4753  print '<span class="opacitymedium">'.$langs->trans("NoActiveEstablishmentDefined").'</span>';
4754  } else {
4755  print '<span class="opacitymedium">'.$langs->trans("NoEstablishmentFound").'</span>';
4756  }
4757  }
4758  } else {
4759  dol_print_error($this->db);
4760  }
4761  }
4762 
4772  public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
4773  {
4774  global $langs;
4775  if ($htmlname != "none") {
4776  print '<form method="POST" action="'.$page.'">';
4777  print '<input type="hidden" name="action" value="setbankaccount">';
4778  print '<input type="hidden" name="token" value="'.newToken().'">';
4779  print img_picto('', 'bank_account', 'class="pictofixedwidth"');
4780  $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
4781  if ($nbaccountfound > 0) {
4782  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
4783  }
4784  print '</form>';
4785  } else {
4786  $langs->load('banks');
4787 
4788  if ($selected) {
4789  require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
4790  $bankstatic = new Account($this->db);
4791  $result = $bankstatic->fetch($selected);
4792  if ($result) {
4793  print $bankstatic->getNomUrl(1);
4794  }
4795  } else {
4796  print "&nbsp;";
4797  }
4798  }
4799  }
4800 
4801  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4820  public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $markafterid = 0, $outputmode = 0, $include = 0, $morecss = '')
4821  {
4822  // phpcs:enable
4823  global $conf, $langs;
4824  $langs->load("categories");
4825 
4826  include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
4827 
4828  // For backward compatibility
4829  if (is_numeric($type)) {
4830  dol_syslog(__METHOD__.': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING);
4831  }
4832 
4833  if ($type === Categorie::TYPE_BANK_LINE) {
4834  // TODO Move this into common category feature
4835  $cate_arbo = array();
4836  $sql = "SELECT c.label, c.rowid";
4837  $sql .= " FROM ".$this->db->prefix()."bank_categ as c";
4838  $sql .= " WHERE entity = ".$conf->entity;
4839  $sql .= " ORDER BY c.label";
4840  $result = $this->db->query($sql);
4841  if ($result) {
4842  $num = $this->db->num_rows($result);
4843  $i = 0;
4844  while ($i < $num) {
4845  $objp = $this->db->fetch_object($result);
4846  if ($objp) {
4847  $cate_arbo[$objp->rowid] = array('id'=>$objp->rowid, 'fulllabel'=>$objp->label);
4848  }
4849  $i++;
4850  }
4851  $this->db->free($result);
4852  } else {
4853  dol_print_error($this->db);
4854  }
4855  } else {
4856  $cat = new Categorie($this->db);
4857  $cate_arbo = $cat->get_full_arbo($type, $markafterid, $include);
4858  }
4859 
4860  $output = '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
4861  $outarray = array();
4862  if (is_array($cate_arbo)) {
4863  if (!count($cate_arbo)) {
4864  $output .= '<option value="-1" disabled>'.$langs->trans("NoCategoriesDefined").'</option>';
4865  } else {
4866  $output .= '<option value="-1">&nbsp;</option>';
4867  foreach ($cate_arbo as $key => $value) {
4868  if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
4869  $add = 'selected ';
4870  } else {
4871  $add = '';
4872  }
4873  $output .= '<option '.$add.'value="'.$cate_arbo[$key]['id'].'">'.dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle').'</option>';
4874 
4875  $outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
4876  }
4877  }
4878  }
4879  $output .= '</select>';
4880  $output .= "\n";
4881 
4882  if ($outputmode) {
4883  return $outarray;
4884  }
4885  return $output;
4886  }
4887 
4888  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4905  public function form_confirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
4906  {
4907  // phpcs:enable
4908  dol_syslog(__METHOD__.': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
4909  print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
4910  }
4911 
4938  public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0, $labelbuttonyes = 'Yes', $labelbuttonno = 'No')
4939  {
4940  global $langs, $conf;
4941 
4942  $more = '<!-- formconfirm before calling page='.dol_escape_htmltag($page).' -->';
4943  $formconfirm = '';
4944  $inputok = array();
4945  $inputko = array();
4946 
4947  // Clean parameters
4948  $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
4949  if ($conf->browser->layout == 'phone') {
4950  $width = '95%';
4951  }
4952 
4953  // Set height automatically if not defined
4954  if (empty($height)) {
4955  $height = 220;
4956  if (is_array($formquestion) && count($formquestion) > 2) {
4957  $height += ((count($formquestion) - 2) * 24);
4958  }
4959  }
4960 
4961  if (is_array($formquestion) && !empty($formquestion)) {
4962  // First add hidden fields and value
4963  foreach ($formquestion as $key => $input) {
4964  if (is_array($input) && !empty($input)) {
4965  if ($input['type'] == 'hidden') {
4966  $moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : '');
4967  $morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : '');
4968 
4969  $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";
4970  }
4971  }
4972  }
4973 
4974  // Now add questions
4975  $moreonecolumn = '';
4976  $more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">'."\n";
4977  foreach ($formquestion as $key => $input) {
4978  if (is_array($input) && !empty($input)) {
4979  $size = (!empty($input['size']) ? ' size="'.$input['size'].'"' : ''); // deprecated. Use morecss instead.
4980  $moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : '');
4981  $morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : '');
4982 
4983  if ($input['type'] == 'text') {
4984  $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="'.$input['value'].'"'.$moreattr.' /></div></div>'."\n";
4985  } elseif ($input['type'] == 'password') {
4986  $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="'.$input['value'].'"'.$moreattr.' /></div></div>'."\n";
4987  } elseif ($input['type'] == 'textarea') {
4988  /*$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd">';
4989  $more .= '<textarea name="'.$input['name'].'" class="'.$morecss.'"'.$moreattr.'>';
4990  $more .= $input['value'];
4991  $more .= '</textarea>';
4992  $more .= '</div></div>'."\n";*/
4993  $moreonecolumn .= '<div class="margintoponly">';
4994  $moreonecolumn .= $input['label'].'<br>';
4995  $moreonecolumn .= '<textarea name="'.dol_escape_htmltag($input['name']).'" id="'.dol_escape_htmltag($input['name']).'" class="'.$morecss.'"'.$moreattr.'>';
4996  $moreonecolumn .= $input['value'];
4997  $moreonecolumn .= '</textarea>';
4998  $moreonecolumn .= '</div>';
4999  } elseif ($input['type'] == 'select') {
5000  if (empty($morecss)) {
5001  $morecss = 'minwidth100';
5002  }
5003 
5004  $show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
5005  $key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
5006  $value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
5007  $translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
5008  $maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
5009  $disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
5010  $sort = isset($input['select_sort']) ? $input['select_sort'] : '';
5011 
5012  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
5013  if (!empty($input['label'])) {
5014  $more .= $input['label'].'</div><div class="tagtd left">';
5015  }
5016  $more .= $this->selectarray($input['name'], $input['values'], isset($input['default'])?$input['default']:'', $show_empty, $key_in_label, $value_as_key, $moreattr, $translate, $maxlen, $disabled, $sort, $morecss);
5017  $more .= '</div></div>'."\n";
5018  } elseif ($input['type'] == 'checkbox') {
5019  $more .= '<div class="tagtr">';
5020  $more .= '<div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].' </div><div class="tagtd">';
5021  $more .= '<input type="checkbox" class="flat'.$morecss.'" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'"'.$moreattr;
5022  if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0' && $input['value'] != '') {
5023  $more .= ' checked';
5024  }
5025  if (is_bool($input['value']) && $input['value']) {
5026  $more .= ' checked';
5027  }
5028  if (isset($input['disabled'])) {
5029  $more .= ' disabled';
5030  }
5031  $more .= ' /></div>';
5032  $more .= '</div>'."\n";
5033  } elseif ($input['type'] == 'radio') {
5034  $i = 0;
5035  foreach ($input['values'] as $selkey => $selval) {
5036  $more .= '<div class="tagtr">';
5037  if ($i == 0) {
5038  $more .= '<div class="tagtd'.(empty($input['tdclass']) ? ' tdtop' : (' tdtop '.$input['tdclass'])).'">'.$input['label'].'</div>';
5039  } else {
5040  $more .= '<div clas="tagtd'.(empty($input['tdclass']) ? '' : (' "'.$input['tdclass'])).'">&nbsp;</div>';
5041  }
5042  $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;
5043  if (!empty($input['disabled'])) {
5044  $more .= ' disabled';
5045  }
5046  if (isset($input['default']) && $input['default'] === $selkey) {
5047  $more .= ' checked="checked"';
5048  }
5049  $more .= ' /> ';
5050  $more .= '<label for="'.dol_escape_htmltag($input['name'].$selkey).'" class="valignmiddle">'.$selval.'</label>';
5051  $more .= '</div></div>'."\n";
5052  $i++;
5053  }
5054  } elseif ($input['type'] == 'date') {
5055  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div>';
5056  $more .= '<div class="tagtd">';
5057  $addnowlink = (empty($input['datenow']) ? 0 : 1);
5058  $more .= $this->selectDate($input['value'], $input['name'], 0, 0, 0, '', 1, $addnowlink);
5059  $more .= '</div></div>'."\n";
5060  $formquestion[] = array('name'=>$input['name'].'day');
5061  $formquestion[] = array('name'=>$input['name'].'month');
5062  $formquestion[] = array('name'=>$input['name'].'year');
5063  $formquestion[] = array('name'=>$input['name'].'hour');
5064  $formquestion[] = array('name'=>$input['name'].'min');
5065  } elseif ($input['type'] == 'other') {
5066  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
5067  if (!empty($input['label'])) {
5068  $more .= $input['label'].'</div><div class="tagtd">';
5069  }
5070  $more .= $input['value'];
5071  $more .= '</div></div>'."\n";
5072  } elseif ($input['type'] == 'onecolumn') {
5073  $moreonecolumn .= '<div class="margintoponly">';
5074  $moreonecolumn .= $input['value'];
5075  $moreonecolumn .= '</div>'."\n";
5076  } elseif ($input['type'] == 'hidden') {
5077  // Do nothing more, already added by a previous loop
5078  } elseif ($input['type'] == 'separator') {
5079  $more .= '<br>';
5080  } else {
5081  $more .= 'Error type '.$input['type'].' for the confirm box is not a supported type';
5082  }
5083  }
5084  }
5085  $more .= '</div>'."\n";
5086  $more .= $moreonecolumn;
5087  }
5088 
5089  // JQUERY method dialog is broken with smartphone, we use standard HTML.
5090  // 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
5091  // See page product/card.php for example
5092  if (!empty($conf->dol_use_jmobile)) {
5093  $useajax = 0;
5094  }
5095  if (empty($conf->use_javascript_ajax)) {
5096  $useajax = 0;
5097  }
5098 
5099  if ($useajax) {
5100  $autoOpen = true;
5101  $dialogconfirm = 'dialog-confirm';
5102  $button = '';
5103  if (!is_numeric($useajax)) {
5104  $button = $useajax;
5105  $useajax = 1;
5106  $autoOpen = false;
5107  $dialogconfirm .= '-'.$button;
5108  }
5109  $pageyes = $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.urlencode($action).'&confirm=yes';
5110  $pageno = ($useajax == 2 ? $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.urlencode($action).'&confirm=no' : '');
5111 
5112  // Add input fields into list of fields to read during submit (inputok and inputko)
5113  if (is_array($formquestion)) {
5114  foreach ($formquestion as $key => $input) {
5115  //print "xx ".$key." rr ".is_array($input)."<br>\n";
5116  // Add name of fields to propagate with the GET when submitting the form with button OK.
5117  if (is_array($input) && isset($input['name'])) {
5118  if (strpos($input['name'], ',') > 0) {
5119  $inputok = array_merge($inputok, explode(',', $input['name']));
5120  } else {
5121  array_push($inputok, $input['name']);
5122  }
5123  }
5124  // Add name of fields to propagate with the GET when submitting the form with button KO.
5125  if (isset($input['inputko']) && $input['inputko'] == 1) {
5126  array_push($inputko, $input['name']);
5127  }
5128  }
5129  }
5130 
5131  // Show JQuery confirm box.
5132  $formconfirm .= '<div id="'.$dialogconfirm.'" title="'.dol_escape_htmltag($title).'" style="display: none;">';
5133  if (is_array($formquestion) && !empty($formquestion['text'])) {
5134  $formconfirm .= '<div class="confirmtext">'.$formquestion['text'].'</div>'."\n";
5135  }
5136  if (!empty($more)) {
5137  $formconfirm .= '<div class="confirmquestions">'.$more.'</div>'."\n";
5138  }
5139  $formconfirm .= ($question ? '<div class="confirmmessage">'.img_help('', '').' '.$question.'</div>' : '');
5140  $formconfirm .= '</div>'."\n";
5141 
5142  $formconfirm .= "\n<!-- begin code of popup for formconfirm page=".$page." -->\n";
5143  $formconfirm .= '<script type="text/javascript">'."\n";
5144  $formconfirm .= "/* Code for the jQuery('#dialogforpopup').dialog() */\n";
5145  $formconfirm .= 'jQuery(document).ready(function() {
5146  $(function() {
5147  $( "#'.$dialogconfirm.'" ).dialog(
5148  {
5149  autoOpen: '.($autoOpen ? "true" : "false").',';
5150  if ($newselectedchoice == 'no') {
5151  $formconfirm .= '
5152  open: function() {
5153  $(this).parent().find("button.ui-button:eq(2)").focus();
5154  },';
5155  }
5156 
5157  $jsforcursor = '';
5158  if (empty($useajax)) {
5159  $jsforcursor = '// The call to urljump can be slow, so we set the wait cursor'."\n";
5160  $jsforcursor .= 'jQuery("html,body,#id-container").addClass("cursorwait");'."\n";
5161  }
5162 
5163  $formconfirm .= '
5164  resizable: false,
5165  height: "'.$height.'",
5166  width: "'.$width.'",
5167  modal: true,
5168  closeOnEscape: false,
5169  buttons: {
5170  "'.dol_escape_js($langs->transnoentities($labelbuttonyes)).'": function() {
5171  var options = "&token='.urlencode(newToken()).'";
5172  var inputok = '.json_encode($inputok).'; /* List of fields into form */
5173  var pageyes = "'.dol_escape_js(!empty($pageyes) ? $pageyes : '').'";
5174 
5175  if (inputok.length > 0) {
5176  $.each(inputok, function(i, inputname) {
5177  var more = "";
5178  var inputvalue;
5179  if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
5180  inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
5181  } else {
5182  if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5183  inputvalue = $("#" + inputname + more).val();
5184  }
5185  if (typeof inputvalue == "undefined") { inputvalue=""; }
5186  console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
5187  options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5188  });
5189  }
5190  var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "") + options;
5191  if (pageyes.length > 0) {
5192  '.$jsforcursor.'
5193  location.href = urljump;
5194  console.log("after location.href");
5195  }
5196  $(this).dialog("close");
5197  },
5198  "'.dol_escape_js($langs->transnoentities($labelbuttonno)).'": function() {
5199  var options = "&token='.urlencode(newToken()).'";
5200  var inputko = '.json_encode($inputko).'; /* List of fields into form */
5201  var pageno="'.dol_escape_js(!empty($pageno) ? $pageno : '').'";
5202  if (inputko.length > 0) {
5203  $.each(inputko, function(i, inputname) {
5204  var more = "";
5205  if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5206  var inputvalue = $("#" + inputname + more).val();
5207  if (typeof inputvalue == "undefined") { inputvalue=""; }
5208  options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5209  });
5210  }
5211  var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "") + options;
5212  //alert(urljump);
5213  if (pageno.length > 0) {
5214  '.$jsforcursor.'
5215  location.href = urljump;
5216  console.log("after location.href");
5217  }
5218  $(this).dialog("close");
5219  }
5220  }
5221  }
5222  );
5223 
5224  var button = "'.$button.'";
5225  if (button.length > 0) {
5226  $( "#" + button ).click(function() {
5227  $("#'.$dialogconfirm.'").dialog("open");
5228  });
5229  }
5230  });
5231  });
5232  </script>';
5233  $formconfirm .= "<!-- end ajax formconfirm -->\n";
5234  } else {
5235  $formconfirm .= "\n<!-- begin formconfirm page=".dol_escape_htmltag($page)." -->\n";
5236 
5237  if (empty($disableformtag)) {
5238  $formconfirm .= '<form method="POST" action="'.$page.'" class="notoptoleftroright">'."\n";
5239  }
5240 
5241  $formconfirm .= '<input type="hidden" name="action" value="'.$action.'">'."\n";
5242  $formconfirm .= '<input type="hidden" name="token" value="'.newToken().'">'."\n";
5243 
5244  $formconfirm .= '<table class="valid centpercent">'."\n";
5245 
5246  // Line title
5247  $formconfirm .= '<tr class="validtitre"><td class="validtitre" colspan="2">';
5248  $formconfirm .= img_picto('', 'recent').' '.$title;
5249  $formconfirm .= '</td></tr>'."\n";
5250 
5251  // Line text
5252  if (is_array($formquestion) && !empty($formquestion['text'])) {
5253  $formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'.$formquestion['text'].'</td></tr>'."\n";
5254  }
5255 
5256  // Line form fields
5257  if ($more) {
5258  $formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'."\n";
5259  $formconfirm .= $more;
5260  $formconfirm .= '</td></tr>'."\n";
5261  }
5262 
5263  // Line with question
5264  $formconfirm .= '<tr class="valid">';
5265  $formconfirm .= '<td class="valid">'.$question.'</td>';
5266  $formconfirm .= '<td class="valid center">';
5267  $formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly', $labelbuttonyes, $labelbuttonno);
5268  $formconfirm .= '<input class="button valignmiddle confirmvalidatebutton small" type="submit" value="'.$langs->trans("Validate").'">';
5269  $formconfirm .= '</td>';
5270  $formconfirm .= '</tr>'."\n";
5271 
5272  $formconfirm .= '</table>'."\n";
5273 
5274  if (empty($disableformtag)) {
5275  $formconfirm .= "</form>\n";
5276  }
5277  $formconfirm .= '<br>';
5278 
5279  if (!empty($conf->use_javascript_ajax)) {
5280  $formconfirm .= '<!-- code to disable button to avoid double clic -->';
5281  $formconfirm .= '<script type="text/javascript">'."\n";
5282  $formconfirm .= '
5283  $(document).ready(function () {
5284  $(".confirmvalidatebutton").on("click", function() {
5285  console.log("We click on button");
5286  $(this).attr("disabled", "disabled");
5287  setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
5288  //console.log($(this).closest("form"));
5289  $(this).closest("form").submit();
5290  });
5291  });
5292  ';
5293  $formconfirm .= '</script>'."\n";
5294  }
5295 
5296  $formconfirm .= "<!-- end formconfirm -->\n";
5297  }
5298 
5299  return $formconfirm;
5300  }
5301 
5302 
5303  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5317  public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0)
5318  {
5319  // phpcs:enable
5320  global $langs;
5321 
5322  require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
5323  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
5324 
5325  $out = '';
5326 
5327  $formproject = new FormProjets($this->db);
5328 
5329  $langs->load("project");
5330  if ($htmlname != "none") {
5331  $out .= "\n";
5332  $out .= '<form method="post" action="'.$page.'">';
5333  $out .= '<input type="hidden" name="action" value="classin">';
5334  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
5335  $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1);
5336  $out .= '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5337  $out .= '</form>';
5338  } else {
5339  $out .= '<span class="project_head_block">';
5340  if ($selected) {
5341  $projet = new Project($this->db);
5342  $projet->fetch($selected);
5343  $out .= $projet->getNomUrl(1, '', 1);
5344  } else {
5345  $out .= "&nbsp;";
5346  }
5347  $out .= '</span>';
5348  }
5349 
5350  if (empty($nooutput)) {
5351  print $out;
5352  return '';
5353  }
5354  return $out;
5355  }
5356 
5357  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5372  public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0, $type = '', $filtertype = -1, $deposit_percent = -1)
5373  {
5374  // phpcs:enable
5375  global $langs;
5376  if ($htmlname != "none") {
5377  print '<form method="POST" action="'.$page.'">';
5378  print '<input type="hidden" name="action" value="setconditions">';
5379  print '<input type="hidden" name="token" value="'.newToken().'">';
5380  if ($type) {
5381  print '<input type="hidden" name="type" value="'.dol_escape_htmltag($type).'">';
5382  }
5383  $this->select_conditions_paiements($selected, $htmlname, $filtertype, $addempty, 0, '', $deposit_percent);
5384  print '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
5385  print '</form>';
5386  } else {
5387  if ($selected) {
5389  if (isset($this->cache_conditions_paiements[$selected])) {
5390  $label = $this->cache_conditions_paiements[$selected]['label'];
5391 
5392  if (! empty($this->cache_conditions_paiements[$selected]['deposit_percent'])) {
5393  $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $this->cache_conditions_paiements[$selected]['deposit_percent'], $label);
5394  }
5395 
5396  print $label;
5397  } else {
5398  $langs->load('errors');
5399  print $langs->trans('ErrorNotInDictionaryPaymentConditions');
5400  }
5401  } else {
5402  print "&nbsp;";
5403  }
5404  }
5405  }
5406 
5407  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5417  public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
5418  {
5419  // phpcs:enable
5420  global $langs;
5421  if ($htmlname != "none") {
5422  print '<form method="post" action="'.$page.'">';
5423  print '<input type="hidden" name="action" value="setavailability">';
5424  print '<input type="hidden" name="token" value="'.newToken().'">';
5425  $this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty);
5426  print '<input type="submit" name="modify" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5427  print '<input type="submit" name="cancel" class="button smallpaddingimp" value="'.$langs->trans("Cancel").'">';
5428  print '</form>';
5429  } else {
5430  if ($selected) {
5431  $this->load_cache_availability();
5432  print $this->cache_availability[$selected]['label'];
5433  } else {
5434  print "&nbsp;";
5435  }
5436  }
5437  }
5438 
5449  public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0)
5450  {
5451  global $langs;
5452  if ($htmlname != "none") {
5453  print '<form method="post" action="'.$page.'">';
5454  print '<input type="hidden" name="action" value="setdemandreason">';
5455  print '<input type="hidden" name="token" value="'.newToken().'">';
5456  $this->selectInputReason($selected, $htmlname, -1, $addempty);
5457  print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5458  print '</form>';
5459  } else {
5460  if ($selected) {
5461  $this->loadCacheInputReason();
5462  foreach ($this->cache_demand_reason as $key => $val) {
5463  if ($val['id'] == $selected) {
5464  print $val['label'];
5465  break;
5466  }
5467  }
5468  } else {
5469  print "&nbsp;";
5470  }
5471  }
5472  }
5473 
5474  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5488  public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0, $type = '')
5489  {
5490  // phpcs:enable
5491  global $langs;
5492 
5493  $ret = '';
5494 
5495  if ($htmlname != "none") {
5496  $ret .= '<form method="POST" action="'.$page.'" name="form'.$htmlname.'">';
5497  $ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
5498  $ret .= '<input type="hidden" name="token" value="'.newToken().'">';
5499  if ($type) {
5500  $ret .= '<input type="hidden" name="type" value="'.dol_escape_htmltag($type).'">';
5501  }
5502  $ret .= '<table class="nobordernopadding">';
5503  $ret .= '<tr><td>';
5504  $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form'.$htmlname, 1, 0);
5505  $ret .= '</td>';
5506  $ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5507  $ret .= '</tr></table></form>';
5508  } else {
5509  if ($displayhour) {
5510  $ret .= dol_print_date($selected, 'dayhour');
5511  } else {
5512  $ret .= dol_print_date($selected, 'day');
5513  }
5514  }
5515 
5516  if (empty($nooutput)) {
5517  print $ret;
5518  }
5519  return $ret;
5520  }
5521 
5522 
5523  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5534  public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = '', $include = '')
5535  {
5536  // phpcs:enable
5537  global $langs;
5538 
5539  if ($htmlname != "none") {
5540  print '<form method="POST" action="'.$page.'" name="form'.$htmlname.'">';
5541  print '<input type="hidden" name="action" value="set'.$htmlname.'">';
5542  print '<input type="hidden" name="token" value="'.newToken().'">';
5543  print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
5544  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5545  print '</form>';
5546  } else {
5547  if ($selected) {
5548  require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
5549  $theuser = new User($this->db);
5550  $theuser->fetch($selected);
5551  print $theuser->getNomUrl(1);
5552  } else {
5553  print "&nbsp;";
5554  }
5555  }
5556  }
5557 
5558 
5559  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5572  public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0, $type = '')
5573  {
5574  // phpcs:enable
5575  global $langs;
5576  if ($htmlname != "none") {
5577  print '<form method="POST" action="'.$page.'">';
5578  print '<input type="hidden" name="action" value="setmode">';
5579  print '<input type="hidden" name="token" value="'.newToken().'">';
5580  if ($type) {
5581  print '<input type="hidden" name="type" value="'.dol_escape_htmltag($type).'">';
5582  }
5583  print $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active, '', 1);
5584  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5585  print '</form>';
5586  } else {
5587  if ($selected) {
5588  $this->load_cache_types_paiements();
5589  print $this->cache_types_paiements[$selected]['label'];
5590  } else {
5591  print "&nbsp;";
5592  }
5593  }
5594  }
5595 
5606  public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
5607  {
5608  global $langs;
5609  if ($htmlname != "none") {
5610  print '<form method="POST" action="'.$page.'">';
5611  print '<input type="hidden" name="action" value="settransportmode">';
5612  print '<input type="hidden" name="token" value="'.newToken().'">';
5613  $this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
5614  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5615  print '</form>';
5616  } else {
5617  if ($selected) {
5618  $this->load_cache_transport_mode();
5619  print $this->cache_transport_mode[$selected]['label'];
5620  } else {
5621  print "&nbsp;";
5622  }
5623  }
5624  }
5625 
5626  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5635  public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
5636  {
5637  // phpcs:enable
5638  global $langs;
5639  if ($htmlname != "none") {
5640  print '<form method="POST" action="'.$page.'">';
5641  print '<input type="hidden" name="action" value="setmulticurrencycode">';
5642  print '<input type="hidden" name="token" value="'.newToken().'">';
5643  print $this->selectMultiCurrency($selected, $htmlname, 0);
5644  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5645  print '</form>';
5646  } else {
5647  dol_include_once('/core/lib/company.lib.php');
5648  print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
5649  }
5650  }
5651 
5652  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5662  public function form_multicurrency_rate($page, $rate = '', $htmlname = 'multicurrency_tx', $currency = '')
5663  {
5664  // phpcs:enable
5665  global $langs, $mysoc, $conf;
5666 
5667  if ($htmlname != "none") {
5668  print '<form method="POST" action="'.$page.'">';
5669  print '<input type="hidden" name="action" value="setmulticurrencyrate">';
5670  print '<input type="hidden" name="token" value="'.newToken().'">';
5671  print '<input type="text" class="maxwidth100" name="'.$htmlname.'" value="'.(!empty($rate) ? price(price2num($rate, 'CU')) : 1).'" /> ';
5672  print '<select name="calculation_mode">';
5673  print '<option value="1">Change '.$langs->trans("PriceUHT").' of lines</option>';
5674  print '<option value="2">Change '.$langs->trans("PriceUHTCurrency").' of lines</option>';
5675  print '</select> ';
5676  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5677  print '</form>';
5678  } else {
5679  if (!empty($rate)) {
5680  print price($rate, 1, $langs, 1, 0);
5681  if ($currency && $rate != 1) {
5682  print ' &nbsp; ('.price($rate, 1, $langs, 1, 0).' '.$currency.' = 1 '.$conf->currency.')';
5683  }
5684  } else {
5685  print 1;
5686  }
5687  }
5688  }
5689 
5690 
5691  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5707  public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0)
5708  {
5709  // phpcs:enable
5710  global $conf, $langs;
5711  if ($htmlname != "none") {
5712  print '<form method="post" action="'.$page.'">';
5713  print '<input type="hidden" name="action" value="setabsolutediscount">';
5714  print '<input type="hidden" name="token" value="'.newToken().'">';
5715  print '<div class="inline-block">';
5716  if (!empty($discount_type)) {
5717  if (!empty($conf->global->FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS)) {
5718  if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
5719  $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be substracted to payments only and not to total of final invoice
5720  } else {
5721  $translationKey = 'HasCreditNoteFromSupplier';
5722  }
5723  } else {
5724  if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5725  $translationKey = 'HasAbsoluteDiscountFromSupplier';
5726  } else {
5727  $translationKey = 'HasCreditNoteFromSupplier';
5728  }
5729  }
5730  } else {
5731  if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
5732  if (!$filter || $filter == "fk_facture_source IS NULL") {
5733  $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be substracted to payments only and not to total of final invoice
5734  } else {
5735  $translationKey = 'CompanyHasCreditNote';
5736  }
5737  } else {
5738  if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5739  $translationKey = 'CompanyHasAbsoluteDiscount';
5740  } else {
5741  $translationKey = 'CompanyHasCreditNote';
5742  }
5743  }
5744  }
5745  print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
5746  if (empty($hidelist)) {
5747  print ' ';
5748  }
5749  print '</div>';
5750  if (empty($hidelist)) {
5751  print '<div class="inline-block" style="padding-right: 10px">';
5752  $newfilter = 'discount_type='.intval($discount_type);
5753  if (!empty($discount_type)) {
5754  $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
5755  } else {
5756  $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
5757  }
5758  if ($filter) {
5759  $newfilter .= ' AND ('.$filter.')';
5760  }
5761  // output the combo of discounts
5762  $nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue);
5763  if ($nbqualifiedlines > 0) {
5764  print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="'.dol_escape_htmltag($langs->trans("UseLine")).'"';
5765  if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5766  print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5767  }
5768  if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5769  print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5770  }
5771 
5772  print '>';
5773  }
5774  print '</div>';
5775  }
5776  if ($more) {
5777  print '<div class="inline-block">';
5778  print $more;
5779  print '</div>';
5780  }
5781  print '</form>';
5782  } else {
5783  if ($selected) {
5784  print $selected;
5785  } else {
5786  print "0";
5787  }
5788  }
5789  }
5790 
5791 
5792  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5802  public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
5803  {
5804  // phpcs:enable
5805  global $langs, $conf;
5806 
5807  if ($htmlname != "none") {
5808  print '<form method="post" action="'.$page.'">';
5809  print '<input type="hidden" name="action" value="set_contact">';
5810  print '<input type="hidden" name="token" value="'.newToken().'">';
5811  print '<table class="nobordernopadding">';
5812  print '<tr><td>';
5813  print $this->selectcontacts($societe->id, $selected, $htmlname);
5814  $num = $this->num;
5815  if ($num == 0) {
5816  $addcontact = (!empty($conf->global->SOCIETE_ADDRESSES_MANAGEMENT) ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
5817  print '<a href="'.DOL_URL_ROOT.'/contact/card.php?socid='.$societe->id.'&amp;action=create&amp;backtoreferer=1">'.$addcontact.'</a>';
5818  }
5819  print '</td>';
5820  print '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5821  print '</tr></table></form>';
5822  } else {
5823  if ($selected) {
5824  require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
5825  $contact = new Contact($this->db);
5826  $contact->fetch($selected);
5827  print $contact->getFullName($langs);
5828  } else {
5829  print "&nbsp;";
5830  }
5831  }
5832  }
5833 
5834  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5850  public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array())
5851  {
5852  // phpcs:enable
5853  global $langs;
5854 
5855  $out = '';
5856  if ($htmlname != "none") {
5857  $out .= '<form method="post" action="'.$page.'">';
5858  $out .= '<input type="hidden" name="action" value="set_thirdparty">';
5859  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
5860  $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, 0, 'minwidth100', '', '', 1, array(), false, $excludeids);
5861  $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5862  $out .= '</form>';
5863  } else {
5864  if ($selected) {
5865  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
5866  $soc = new Societe($this->db);
5867  $soc->fetch($selected);
5868  $out .= $soc->getNomUrl($langs);
5869  } else {
5870  $out .= "&nbsp;";
5871  }
5872  }
5873 
5874  if ($nooutput) {
5875  return $out;
5876  } else {
5877  print $out;
5878  }
5879  }
5880 
5881  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5890  public function select_currency($selected = '', $htmlname = 'currency_id')
5891  {
5892  // phpcs:enable
5893  print $this->selectCurrency($selected, $htmlname);
5894  }
5895 
5905  public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0, $useempty = '')
5906  {
5907  global $conf, $langs, $user;
5908 
5909  $langs->loadCacheCurrencies('');
5910 
5911  $out = '';
5912 
5913  if ($selected == 'euro' || $selected == 'euros') {
5914  $selected = 'EUR'; // Pour compatibilite
5915  }
5916 
5917  $out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="'.$htmlname.'" id="'.$htmlname.'">';
5918  if ($useempty) {
5919  $out .= '<option value="-1" selected></option>';
5920  }
5921  foreach ($langs->cache_currencies as $code_iso => $currency) {
5922  $labeltoshow = $currency['label'];
5923  if ($mode == 1) {
5924  $labeltoshow .= ' <span class="opacitymedium">('.$code_iso.')</span>';
5925  } else {
5926  $labeltoshow .= ' <span class="opacitymedium">('.$langs->getCurrencySymbol($code_iso).')</span>';
5927  }
5928 
5929  if ($selected && $selected == $code_iso) {
5930  $out .= '<option value="'.$code_iso.'" selected data-html="'.dol_escape_htmltag($labeltoshow).'">';
5931  } else {
5932  $out .= '<option value="'.$code_iso.'" data-html="'.dol_escape_htmltag($labeltoshow).'">';
5933  }
5934  $out .= $labeltoshow;
5935  $out .= '</option>';
5936  }
5937  $out .= '</select>';
5938  if ($user->admin) {
5939  $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5940  }
5941 
5942  // Make select dynamic
5943  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
5944  $out .= ajax_combobox($htmlname);
5945 
5946  return $out;
5947  }
5948 
5961  public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false, $morecss = '')
5962  {
5963  global $conf, $langs;
5964 
5965  $langs->loadCacheCurrencies(''); // Load ->cache_currencies
5966 
5967  $TCurrency = array();
5968 
5969  $sql = "SELECT code FROM ".$this->db->prefix()."multicurrency";
5970  $sql .= " WHERE entity IN ('".getEntity('mutlicurrency')."')";
5971  if ($filter) {
5972  $sql .= " AND ".$filter;
5973  }
5974  $resql = $this->db->query($sql);
5975  if ($resql) {
5976  while ($obj = $this->db->fetch_object($resql)) {
5977  $TCurrency[$obj->code] = $obj->code;
5978  }
5979  }
5980 
5981  $out = '';
5982  $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
5983  if ($useempty) {
5984  $out .= '<option value="">&nbsp;</option>';
5985  }
5986  // If company current currency not in table, we add it into list. Should always be available.
5987  if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
5988  $TCurrency[$conf->currency] = $conf->currency;
5989  }
5990  if (count($TCurrency) > 0) {
5991  foreach ($langs->cache_currencies as $code_iso => $currency) {
5992  if (isset($TCurrency[$code_iso])) {
5993  if (!empty($selected) && $selected == $code_iso) {
5994  $out .= '<option value="'.$code_iso.'" selected="selected">';
5995  } else {
5996  $out .= '<option value="'.$code_iso.'">';
5997  }
5998 
5999  $out .= $currency['label'];
6000  $out .= ' ('.$langs->getCurrencySymbol($code_iso).')';
6001  $out .= '</option>';
6002  }
6003  }
6004  }
6005 
6006  $out .= '</select>';
6007 
6008  // Make select dynamic
6009  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6010  $out .= ajax_combobox($htmlname);
6011 
6012  return $out;
6013  }
6014 
6015  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6022  public function load_cache_vatrates($country_code)
6023  {
6024  // phpcs:enable
6025  global $langs;
6026 
6027  $num = count($this->cache_vatrates);
6028  if ($num > 0) {
6029  return $num; // Cache already loaded
6030  }
6031 
6032  dol_syslog(__METHOD__, LOG_DEBUG);
6033 
6034  $sql = "SELECT DISTINCT t.rowid, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly";
6035  $sql .= " FROM ".$this->db->prefix()."c_tva as t, ".$this->db->prefix()."c_country as c";
6036  $sql .= " WHERE t.fk_pays = c.rowid";
6037  $sql .= " AND t.active > 0";
6038  $sql .= " AND c.code IN (".$this->db->sanitize($country_code, 1).")";
6039  $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
6040 
6041  $resql = $this->db->query($sql);
6042  if ($resql) {
6043  $num = $this->db->num_rows($resql);
6044  if ($num) {
6045  for ($i = 0; $i < $num; $i++) {
6046  $obj = $this->db->fetch_object($resql);
6047  $this->cache_vatrates[$i]['rowid'] = $obj->rowid;
6048  $this->cache_vatrates[$i]['code'] = $obj->code;
6049  $this->cache_vatrates[$i]['txtva'] = $obj->taux;
6050  $this->cache_vatrates[$i]['nprtva'] = $obj->recuperableonly;
6051  $this->cache_vatrates[$i]['localtax1'] = $obj->localtax1;
6052  $this->cache_vatrates[$i]['localtax1_type'] = $obj->localtax1_type;
6053  $this->cache_vatrates[$i]['localtax2'] = $obj->localtax2;
6054  $this->cache_vatrates[$i]['localtax2_type'] = $obj->localtax1_type;
6055 
6056  $this->cache_vatrates[$i]['label'] = $obj->taux.'%'.($obj->code ? ' ('.$obj->code.')' : ''); // Label must contains only 0-9 , . % or *
6057  $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
6058  $positiverates = '';
6059  if ($obj->taux) {
6060  $positiverates .= ($positiverates ? '/' : '').$obj->taux;
6061  }
6062  if ($obj->localtax1) {
6063  $positiverates .= ($positiverates ? '/' : '').$obj->localtax1;
6064  }
6065  if ($obj->localtax2) {
6066  $positiverates .= ($positiverates ? '/' : '').$obj->localtax2;
6067  }
6068  if (empty($positiverates)) {
6069  $positiverates = '0';
6070  }
6071  $this->cache_vatrates[$i]['labelpositiverates'] = $positiverates.($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label
6072  }
6073 
6074  return $num;
6075  } else {
6076  $this->error = '<span class="error">'.$langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code).'</span>';
6077  return -1;
6078  }
6079  } else {
6080  $this->error = '<span class="error">'.$this->db->error().'</span>';
6081  return -2;
6082  }
6083  }
6084 
6085  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6107  public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = '', $societe_acheteuse = '', $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0)
6108  {
6109  // phpcs:enable
6110  global $langs, $conf, $mysoc;
6111 
6112  $langs->load('errors');
6113 
6114  $return = '';
6115 
6116  // Define defaultnpr, defaultttx and defaultcode
6117  $defaultnpr = ($info_bits & 0x01);
6118  $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
6119  $defaulttx = str_replace('*', '', $selectedrate);
6120  $defaultcode = '';
6121  $reg = array();
6122  if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
6123  $defaultcode = $reg[1];
6124  $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
6125  }
6126  //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
6127 
6128  // Check parameters
6129  if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
6130  if ($societe_vendeuse->id == $mysoc->id) {
6131  $return .= '<span class="error">'.$langs->trans("ErrorYourCountryIsNotDefined").'</span>';
6132  } else {
6133  $return .= '<span class="error">'.$langs->trans("ErrorSupplierCountryIsNotDefined").'</span>';
6134  }
6135  return $return;
6136  }
6137 
6138  //var_dump($societe_acheteuse);
6139  //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";
6140  //exit;
6141 
6142  // Define list of countries to use to search VAT rates to show
6143  // First we defined code_country to use to find list
6144  if (is_object($societe_vendeuse)) {
6145  $code_country = "'".$societe_vendeuse->country_code."'";
6146  } else {
6147  $code_country = "'".$mysoc->country_code."'"; // Pour compatibilite ascendente
6148  }
6149  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) { // If option to have vat for end customer for services is on
6150  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6151  if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) {
6152  // We also add the buyer
6153  if (is_numeric($type)) {
6154  if ($type == 1) { // We know product is a service
6155  $code_country .= ",'".$societe_acheteuse->country_code."'";
6156  }
6157  } elseif (!$idprod) { // We don't know type of product
6158  $code_country .= ",'".$societe_acheteuse->country_code."'";
6159  } else {
6160  $prodstatic = new Product($this->db);
6161  $prodstatic->fetch($idprod);
6162  if ($prodstatic->type == Product::TYPE_SERVICE) { // We know product is a service
6163  $code_country .= ",'".$societe_acheteuse->country_code."'";
6164  }
6165  }
6166  }
6167  }
6168 
6169  // Now we get list
6170  $num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
6171 
6172  if ($num > 0) {
6173  // Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '')
6174  if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
6175  $tmpthirdparty = new Societe($this->db);
6176  $defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
6177  $defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
6178  if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
6179  $defaultcode = $reg[1];
6180  $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
6181  }
6182  if (empty($defaulttx)) {
6183  $defaultnpr = 0;
6184  }
6185  }
6186 
6187  // Si taux par defaut n'a pu etre determine, on prend dernier de la liste.
6188  // Comme ils sont tries par ordre croissant, dernier = plus eleve = taux courant
6189  if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
6190  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6191  $defaulttx = $this->cache_vatrates[$num - 1]['txtva'];
6192  } else {
6193  $defaulttx = ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS == 'none' ? '' : $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS);
6194  }
6195  }
6196 
6197  // Disabled if seller is not subject to VAT
6198  $disabled = false;
6199  $title = '';
6200  if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0") {
6201  // Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead
6202  // of using supplier invoices (this is a very bad idea !)
6203  if (empty($conf->global->EXPENSEREPORT_OVERRIDE_VAT)) {
6204  $title = ' title="'.$langs->trans('VATIsNotUsed').'"';
6205  $disabled = true;
6206  }
6207  }
6208 
6209  if (!$options_only) {
6210  $return .= '<select class="flat minwidth75imp" id="'.$htmlname.'" name="'.$htmlname.'"'.($disabled ? ' disabled' : '').$title.'>';
6211  }
6212 
6213  $selectedfound = false;
6214  foreach ($this->cache_vatrates as $rate) {
6215  // Keep only 0 if seller is not subject to VAT
6216  if ($disabled && $rate['txtva'] != 0) {
6217  continue;
6218  }
6219 
6220  // Define key to use into select list
6221  $key = $rate['txtva'];
6222  $key .= $rate['nprtva'] ? '*' : '';
6223  if ($mode > 0 && $rate['code']) {
6224  $key .= ' ('.$rate['code'].')';
6225  }
6226  if ($mode < 0) {
6227  $key = $rate['rowid'];
6228  }
6229 
6230  $return .= '<option value="'.$key.'"';
6231  if (!$selectedfound) {
6232  if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
6233  if ($defaultcode == $rate['code']) {
6234  $return .= ' selected';
6235  $selectedfound = true;
6236  }
6237  } elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
6238  $return .= ' selected';
6239  $selectedfound = true;
6240  }
6241  }
6242  $return .= '>';
6243  //if (! empty($conf->global->MAIN_VAT_SHOW_POSITIVE_RATES))
6244  if ($mysoc->country_code == 'IN' || !empty($conf->global->MAIN_VAT_LABEL_IS_POSITIVE_RATES)) {
6245  $return .= $rate['labelpositiverates'];
6246  } else {
6247  $return .= vatrate($rate['label']);
6248  }
6249  //$return.=($rate['code']?' '.$rate['code']:'');
6250  $return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the * (old behaviour only if new vat code is not used)
6251 
6252  $return .= '</option>';
6253  }
6254 
6255  if (!$options_only) {
6256  $return .= '</select>';
6257  }
6258  } else {
6259  $return .= $this->error;
6260  }
6261 
6262  $this->num = $num;
6263  return $return;
6264  }
6265 
6266 
6267  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6292  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 = '')
6293  {
6294  // phpcs:enable
6295  $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
6296  if (!empty($nooutput)) {
6297  return $retstring;
6298  }
6299  print $retstring;
6300  return;
6301  }
6302 
6318  public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0, $forcenewline = 0)
6319  {
6320  global $langs;
6321 
6322  $ret = $this->selectDate($set_time, $prefix.'_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
6323  if ($forcenewline) {
6324  $ret .= '<br>';
6325  }
6326  $ret .= $this->selectDate($set_time_end, $prefix.'_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
6327  return $ret;
6328  }
6329 
6357  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')
6358  {
6359  global $conf, $langs;
6360 
6361  if ($gm === 'auto') {
6362  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
6363  }
6364 
6365  $retstring = '';
6366 
6367  if ($prefix == '') {
6368  $prefix = 're';
6369  }
6370  if ($h == '') {
6371  $h = 0;
6372  }
6373  if ($m == '') {
6374  $m = 0;
6375  }
6376  $emptydate = 0;
6377  $emptyhours = 0;
6378  if ($stepminutes <= 0 || $stepminutes > 30) {
6379  $stepminutes = 1;
6380  }
6381  if ($empty == 1) {
6382  $emptydate = 1;
6383  $emptyhours = 1;
6384  }
6385  if ($empty == 2) {
6386  $emptydate = 0;
6387  $emptyhours = 1;
6388  }
6389  $orig_set_time = $set_time;
6390 
6391  if ($set_time === '' && $emptydate == 0) {
6392  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6393  if ($gm == 'tzuser' || $gm == 'tzuserrel') {
6394  $set_time = dol_now($gm);
6395  } else {
6396  $set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
6397  }
6398  }
6399 
6400  // Analysis of the pre-selection date
6401  $reg = array();
6402  $shour = '';
6403  $smin = '';
6404  $ssec = '';
6405  if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', $set_time, $reg)) { // deprecated usage
6406  // Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
6407  $syear = (!empty($reg[1]) ? $reg[1] : '');
6408  $smonth = (!empty($reg[2]) ? $reg[2] : '');
6409  $sday = (!empty($reg[3]) ? $reg[3] : '');
6410  $shour = (!empty($reg[4]) ? $reg[4] : '');
6411  $smin = (!empty($reg[5]) ? $reg[5] : '');
6412  } elseif (strval($set_time) != '' && $set_time != -1) {
6413  // set_time est un timestamps (0 possible)
6414  $syear = dol_print_date($set_time, "%Y", $gm);
6415  $smonth = dol_print_date($set_time, "%m", $gm);
6416  $sday = dol_print_date($set_time, "%d", $gm);
6417  if ($orig_set_time != '') {
6418  $shour = dol_print_date($set_time, "%H", $gm);
6419  $smin = dol_print_date($set_time, "%M", $gm);
6420  $ssec = dol_print_date($set_time, "%S", $gm);
6421  }
6422  } else {
6423  // Date est '' ou vaut -1
6424  $syear = '';
6425  $smonth = '';
6426  $sday = '';
6427  $shour = !isset($conf->global->MAIN_DEFAULT_DATE_HOUR) ? ($h == -1 ? '23' : '') : $conf->global->MAIN_DEFAULT_DATE_HOUR;
6428  $smin = !isset($conf->global->MAIN_DEFAULT_DATE_MIN) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_MIN;
6429  $ssec = !isset($conf->global->MAIN_DEFAULT_DATE_SEC) ? ($h == -1 ? '59' : '') : $conf->global->MAIN_DEFAULT_DATE_SEC;
6430  }
6431  if ($h == 3) {
6432  $shour = '';
6433  }
6434  if ($m == 3) {
6435  $smin = '';
6436  }
6437 
6438  $nowgmt = dol_now('gmt');
6439  //var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
6440 
6441  // You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
6442  $usecalendar = 'combo';
6443  if (!empty($conf->use_javascript_ajax) && (empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR != "none")) {
6444  $usecalendar = ((empty($conf->global->MAIN_POPUP_CALENDAR) || $conf->global->MAIN_POPUP_CALENDAR == 'eldy') ? 'jquery' : $conf->global->MAIN_POPUP_CALENDAR);
6445  }
6446 
6447  if ($d) {
6448  // Show date with popup
6449  if ($usecalendar != 'combo') {
6450  $formated_date = '';
6451  //print "e".$set_time." t ".$conf->format_date_short;
6452  if (strval($set_time) != '' && $set_time != -1) {
6453  //$formated_date=dol_print_date($set_time,$conf->format_date_short);
6454  $formated_date = dol_print_date($set_time, $langs->trans("FormatDateShortInput"), $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6455  }
6456 
6457  // Calendrier popup version eldy
6458  if ($usecalendar == "eldy") {
6459  // Input area to enter date manually
6460  $retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
6461  $retstring .= ($disabled ? ' disabled' : '');
6462  $retstring .= ' onChange="dpChangeDay(\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6463  $retstring .= '>';
6464 
6465  // Icon calendar
6466  $retstringbuttom = '';
6467  if (!$disabled) {
6468  $retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons"';
6469  $base = DOL_URL_ROOT.'/core/';
6470  $retstringbuttom .= ' onClick="showDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');"';
6471  $retstringbuttom .= '>'.img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink"').'</button>';
6472  } else {
6473  $retstringbuttom = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"').'</button>';
6474  }
6475  $retstring = $retstringbuttom.$retstring;
6476 
6477  $retstring .= '<input type="hidden" id="'.$prefix.'day" name="'.$prefix.'day" value="'.$sday.'">'."\n";
6478  $retstring .= '<input type="hidden" id="'.$prefix.'month" name="'.$prefix.'month" value="'.$smonth.'">'."\n";
6479  $retstring .= '<input type="hidden" id="'.$prefix.'year" name="'.$prefix.'year" value="'.$syear.'">'."\n";
6480  } elseif ($usecalendar == 'jquery') {
6481  if (!$disabled) {
6482  // Output javascript for datepicker
6483  $minYear = getDolGlobalString('MIN_YEAR_SELECT_DATE', date('Y') - 100);
6484  $maxYear = getDolGlobalString('MAX_YEAR_SELECT_DATE', date('Y') + 100);
6485 
6486  $retstring .= "<script type='text/javascript'>";
6487  $retstring .= "$(function(){ $('#".$prefix."').datepicker({
6488  dateFormat: '".$langs->trans("FormatDateShortJQueryInput")."',
6489  autoclose: true,
6490  todayHighlight: true,
6491  yearRange: '".$minYear.":".$maxYear."',";
6492  if (!empty($conf->dol_use_jmobile)) {
6493  $retstring .= "
6494  beforeShow: function (input, datePicker) {
6495  input.disabled = true;
6496  },
6497  onClose: function (dateText, datePicker) {
6498  this.disabled = false;
6499  },
6500  ";
6501  }
6502  // Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
6503  if (empty($conf->global->MAIN_POPUP_CALENDAR_ON_FOCUS)) {
6504  $retstring .= "
6505  showOn: 'button', /* both has problem with autocompletion */
6506  buttonImage: '".DOL_URL_ROOT."/theme/".dol_escape_js($conf->theme)."/img/object_calendarday.png',
6507  buttonImageOnly: true";
6508  }
6509  $retstring .= "
6510  }) });";
6511  $retstring .= "</script>";
6512  }
6513 
6514  // Zone de saisie manuelle de la date
6515  $retstring .= '<div class="nowrap inline-block divfordateinput">';
6516  $retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="text" class="maxwidthdate" maxlength="11" value="'.$formated_date.'"';
6517  $retstring .= ($disabled ? ' disabled' : '');
6518  $retstring .= ($placeholder ? ' placeholder="'.dol_escape_htmltag($placeholder).'"' : '');
6519  $retstring .= ' onChange="dpChangeDay(\''.dol_escape_js($prefix).'\',\''.dol_escape_js($langs->trans("FormatDateShortJavaInput")).'\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
6520  $retstring .= '>';
6521 
6522  // Icone calendrier
6523  if (!$disabled) {
6524  /* Not required. Managed by option buttonImage of jquery
6525  $retstring.=img_object($langs->trans("SelectDate"),'calendarday','id="'.$prefix.'id" class="datecallink"');
6526  $retstring.="<script type='text/javascript'>";
6527  $retstring.="jQuery(document).ready(function() {";
6528  $retstring.=' jQuery("#'.$prefix.'id").click(function() {';
6529  $retstring.=" jQuery('#".$prefix."').focus();";
6530  $retstring.=' });';
6531  $retstring.='});';
6532  $retstring.="</script>";*/
6533  } else {
6534  $retstringbutton = '<button id="'.$prefix.'Button" type="button" class="dpInvisibleButtons">'.img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink"').'</button>';
6535  $retsring = $retstringbutton.$retstring;
6536  }
6537 
6538  $retstring .= '</div>';
6539  $retstring .= '<input type="hidden" id="'.$prefix.'day" name="'.$prefix.'day" value="'.$sday.'">'."\n";
6540  $retstring .= '<input type="hidden" id="'.$prefix.'month" name="'.$prefix.'month" value="'.$smonth.'">'."\n";
6541  $retstring .= '<input type="hidden" id="'.$prefix.'year" name="'.$prefix.'year" value="'.$syear.'">'."\n";
6542  } else {
6543  $retstring .= "Bad value of MAIN_POPUP_CALENDAR";
6544  }
6545  } else {
6546  // Show date with combo selects
6547  // Day
6548  $retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50imp" id="'.$prefix.'day" name="'.$prefix.'day">';
6549 
6550  if ($emptydate || $set_time == -1) {
6551  $retstring .= '<option value="0" selected>&nbsp;</option>';
6552  }
6553 
6554  for ($day = 1; $day <= 31; $day++) {
6555  $retstring .= '<option value="'.$day.'"'.($day == $sday ? ' selected' : '').'>'.$day.'</option>';
6556  }
6557 
6558  $retstring .= "</select>";
6559 
6560  $retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'month" name="'.$prefix.'month">';
6561  if ($emptydate || $set_time == -1) {
6562  $retstring .= '<option value="0" selected>&nbsp;</option>';
6563  }
6564 
6565  // Month
6566  for ($month = 1; $month <= 12; $month++) {
6567  $retstring .= '<option value="'.$month.'"'.($month == $smonth ? ' selected' : '').'>';
6568  $retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
6569  $retstring .= "</option>";
6570  }
6571  $retstring .= "</select>";
6572 
6573  // Year
6574  if ($emptydate || $set_time == -1) {
6575  $retstring .= '<input'.($disabled ? ' disabled' : '').' placeholder="'.dol_escape_htmltag($langs->trans("Year")).'" class="flat maxwidth50imp valignmiddle" type="number" min="0" max="3000" maxlength="4" id="'.$prefix.'year" name="'.$prefix.'year" value="'.$syear.'">';
6576  } else {
6577  $retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth75imp" id="'.$prefix.'year" name="'.$prefix.'year">';
6578 
6579  for ($year = $syear - 10; $year < $syear + 10; $year++) {
6580  $retstring .= '<option value="'.$year.'"'.($year == $syear ? ' selected' : '').'>'.$year.'</option>';
6581  }
6582  $retstring .= "</select>\n";
6583  }
6584  }
6585  }
6586 
6587  if ($d && $h) {
6588  $retstring .= ($h == 2 ? '<br>' : ' ');
6589  $retstring .= '<span class="nowraponall">';
6590  }
6591 
6592  if ($h) {
6593  $hourstart = 0;
6594  $hourend = 24;
6595  if ($openinghours != '') {
6596  $openinghours = explode(',', $openinghours);
6597  $hourstart = $openinghours[0];
6598  $hourend = $openinghours[1];
6599  if ($hourend < $hourstart) {
6600  $hourend = $hourstart;
6601  }
6602  }
6603  // Show hour
6604  $retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50 '.($fullday ? $fullday.'hour' : '').'" id="'.$prefix.'hour" name="'.$prefix.'hour">';
6605  if ($emptyhours) {
6606  $retstring .= '<option value="-1">&nbsp;</option>';
6607  }
6608  for ($hour = $hourstart; $hour < $hourend; $hour++) {
6609  if (strlen($hour) < 2) {
6610  $hour = "0".$hour;
6611  }
6612  $retstring .= '<option value="'.$hour.'"'.(($hour == $shour) ? ' selected' : '').'>'.$hour;
6613  //$retstring .= (empty($conf->dol_optimize_smallscreen) ? '' : 'H');
6614  $retstring .= '</option>';
6615  }
6616  $retstring .= '</select>';
6617  //if ($m && empty($conf->dol_optimize_smallscreen)) $retstring .= ":";
6618  if ($m) {
6619  $retstring .= ":";
6620  }
6621  }
6622 
6623  if ($m) {
6624  // Show minutes
6625  $retstring .= '<select'.($disabled ? ' disabled' : '').' class="flat valignmiddle maxwidth50 '.($fullday ? $fullday.'min' : '').'<