dolibarr  18.0.0-alpha
html.form.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 2002-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004-2012 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
5  * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6  * Copyright (C) 2004 Eric Seigne <eric.seigne@ryxeo.com>
7  * Copyright (C) 2005-2017 Regis Houssin <regis.houssin@inodbox.com>
8  * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
9  * Copyright (C) 2006 Marc Barilley/Ocebo <marc@ocebo.com>
10  * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerker@telenet.be>
11  * Copyright (C) 2007 Patrick Raguin <patrick.raguin@gmail.com>
12  * Copyright (C) 2010 Juanjo Menent <jmenent@2byte.es>
13  * Copyright (C) 2010-2021 Philippe Grand <philippe.grand@atoo-net.com>
14  * Copyright (C) 2011 Herve Prot <herve.prot@symeos.com>
15  * Copyright (C) 2012-2016 Marcos García <marcosgdf@gmail.com>
16  * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
17  * Copyright (C) 2012-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
18  * Copyright (C) 2014-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 reposition" 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  if ($text) {
172  $ret .= ' : ';
173  } else {
174  $ret .= ' ';
175  }
176  }
177  if (!empty($notabletag) && $notabletag == 3) {
178  $ret .= ' ';
179  }
180  if (empty($notabletag) && $perm) {
181  $ret .= '</td>';
182  }
183  if (empty($notabletag) && $perm) {
184  $ret .= '</tr></table>';
185  }
186  }
187 
188  return $ret;
189  }
190 
212  public function editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata = 'string', $editvalue = '', $extObject = null, $custommsg = null, $moreparam = '', $notabletag = 1, $formatfunc = '', $paramid = 'id', $gm = 'auto', $moreoptions = array(), $editaction = '')
213  {
214  global $conf, $langs;
215 
216  $ret = '';
217 
218  // Check parameters
219  if (empty($typeofdata)) {
220  return 'ErrorBadParameter typeofdata is empty';
221  }
222  // Clean paramater $typeofdata
223  if ($typeofdata == 'datetime') {
224  $typeofdata = 'dayhour';
225  }
226  $reg = array();
227  if (preg_match('/^(\w+)\((\d+)\)$/', $typeofdata, $reg)) {
228  if ($reg[1] == 'varchar') {
229  $typeofdata = 'string';
230  } elseif ($reg[1] == 'int') {
231  $typeofdata = 'numeric';
232  } else {
233  return 'ErrorBadParameter '.$typeofdata;
234  }
235  }
236 
237  // When option to edit inline is activated
238  if (!empty($conf->global->MAIN_USE_JQUERY_JEDITABLE) && !preg_match('/^select;|day|datepicker|dayhour|datehourpicker/', $typeofdata)) { // TODO add jquery timepicker and support select
239  $ret .= $this->editInPlace($object, $value, $htmlname, $perm, $typeofdata, $editvalue, $extObject, $custommsg);
240  } else {
241  if ($editaction == '') {
242  $editaction = GETPOST('action', 'aZ09');
243  }
244  $editmode = ($editaction == 'edit'.$htmlname);
245  if ($editmode) {
246  $ret .= "\n";
247  $ret .= '<form method="post" action="'.$_SERVER["PHP_SELF"].($moreparam ? '?'.$moreparam : '').'">';
248  $ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
249  $ret .= '<input type="hidden" name="token" value="'.newToken().'">';
250  $ret .= '<input type="hidden" name="'.$paramid.'" value="'.$object->id.'">';
251  if (empty($notabletag)) {
252  $ret .= '<table class="nobordernopadding centpercent">';
253  }
254  if (empty($notabletag)) {
255  $ret .= '<tr><td>';
256  }
257  if (preg_match('/^(string|safehtmlstring|email)/', $typeofdata)) {
258  $tmp = explode(':', $typeofdata);
259  $ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.($editvalue ? $editvalue : $value).'"'.(empty($tmp[1]) ? '' : ' size="'.$tmp[1].'"').' autofocus>';
260  } elseif (preg_match('/^(integer)/', $typeofdata)) {
261  $tmp = explode(':', $typeofdata);
262  $valuetoshow = price2num($editvalue ? $editvalue : $value, 0);
263  $ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.$valuetoshow.'"'.(empty($tmp[1]) ? '' : ' size="'.$tmp[1].'"').' autofocus>';
264  } elseif (preg_match('/^(numeric|amount)/', $typeofdata)) {
265  $tmp = explode(':', $typeofdata);
266  $valuetoshow = price2num($editvalue ? $editvalue : $value);
267  $ret .= '<input type="text" id="'.$htmlname.'" name="'.$htmlname.'" value="'.($valuetoshow != '' ? price($valuetoshow) : '').'"'.(empty($tmp[1]) ? '' : ' size="'.$tmp[1].'"').' autofocus>';
268  } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
269  $tmp = explode(':', $typeofdata);
270  $ret .= '<input type="checkbox" id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . (empty($tmp[1]) ? '' : $tmp[1]) . '/>';
271  } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) { // if wysiwyg is enabled $typeofdata = 'ckeditor'
272  $tmp = explode(':', $typeofdata);
273  $cols = (empty($tmp[2]) ? '' : $tmp[2]);
274  $morealt = '';
275  if (preg_match('/%/', $cols)) {
276  $morealt = ' style="width: '.$cols.'"';
277  $cols = '';
278  }
279 
280  $valuetoshow = ($editvalue ? $editvalue : $value);
281  $ret .= '<textarea id="'.$htmlname.'" name="'.$htmlname.'" wrap="soft" rows="'.(empty($tmp[1]) ? '20' : $tmp[1]).'"'.($cols ? ' cols="'.$cols.'"' : 'class="quatrevingtpercent"').$morealt.'" autofocus>';
282  // textarea convert automatically entities chars into simple chars.
283  // 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.
284  $valuetoshow = str_replace('&', '&amp;', $valuetoshow);
285  $ret .= dol_string_neverthesehtmltags($valuetoshow, array('textarea'));
286  $ret .= '</textarea>';
287  } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
288  $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
289  $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
290  $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
291  $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form'.$htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
292  } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
293  $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
294  $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
295  $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
296  $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form'.$htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
297  } elseif (preg_match('/^select;/', $typeofdata)) {
298  $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
299  $arraylist = array();
300  foreach ($arraydata as $val) {
301  $tmp = explode(':', $val);
302  $tmpkey = str_replace('|', ':', $tmp[0]);
303  $arraylist[$tmpkey] = $tmp[1];
304  }
305  $ret .= $this->selectarray($htmlname, $arraylist, $value);
306  } elseif (preg_match('/^link/', $typeofdata)) {
307  // TODO Not yet implemented. See code for extrafields
308  } elseif (preg_match('/^ckeditor/', $typeofdata)) {
309  $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser
310  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
311  $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]));
312  $ret .= $doleditor->Create(1);
313  }
314  if (empty($notabletag)) {
315  $ret .= '</td>';
316  }
317 
318  // Button save-cancel
319  if (empty($notabletag)) {
320  $ret .= '<td>';
321  }
322  //else $ret.='<div class="clearboth"></div>';
323  $ret .= '<input type="submit" class="smallpaddingimp button'.(empty($notabletag) ? '' : ' ').'" name="modify" value="'.$langs->trans("Modify").'">';
324  if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) {
325  $ret .= '<br>'."\n";
326  }
327  $ret .= '<input type="submit" class="smallpaddingimp button button-cancel'.(empty($notabletag) ? '' : ' ').'" name="cancel" value="'.$langs->trans("Cancel").'">';
328  if (empty($notabletag)) {
329  $ret .= '</td>';
330  }
331 
332  if (empty($notabletag)) {
333  $ret .= '</tr></table>'."\n";
334  }
335  $ret .= '</form>'."\n";
336  } else {
337  if (preg_match('/^(email)/', $typeofdata)) {
338  $ret .= dol_print_email($value, 0, 0, 0, 0, 1);
339  } elseif (preg_match('/^(amount|numeric)/', $typeofdata)) {
340  $ret .= ($value != '' ? price($value, '', $langs, 0, -1, -1, $conf->currency) : '');
341  } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
342  $tmp = explode(':', $typeofdata);
343  $ret .= '<input type="checkbox" disabled id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($tmp[1] ? $tmp[1] : '') . '/>';
344  } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {
345  $ret .= dol_htmlentitiesbr($value);
346  } elseif (preg_match('/^safehtmlstring/', $typeofdata)) {
347  $ret .= dol_string_onlythesehtmltags($value);
348  } elseif (preg_match('/^restricthtml/', $typeofdata)) {
349  $ret .= dol_string_onlythesehtmltags($value);
350  } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
351  $ret .= '<span class="valuedate">'.dol_print_date($value, 'day', $gm).'</span>';
352  } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
353  $ret .= '<span class="valuedate">'.dol_print_date($value, 'dayhour', $gm).'</span>';
354  } elseif (preg_match('/^select;/', $typeofdata)) {
355  $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
356  $arraylist = array();
357  foreach ($arraydata as $val) {
358  $tmp = explode(':', $val);
359  $arraylist[$tmp[0]] = $tmp[1];
360  }
361  $ret .= $arraylist[$value];
362  if ($htmlname == 'fk_product_type') {
363  if ($value == 0) {
364  $ret = img_picto($langs->trans("Product"), 'product', 'class="paddingleftonly paddingrightonly colorgrey"').$ret;
365  } else {
366  $ret = img_picto($langs->trans("Service"), 'service', 'class="paddingleftonly paddingrightonly colorgrey"').$ret;
367  }
368  }
369  } elseif (preg_match('/^ckeditor/', $typeofdata)) {
370  $tmpcontent = dol_htmlentitiesbr($value);
371  if (!empty($conf->global->MAIN_DISABLE_NOTES_TAB)) {
372  $firstline = preg_replace('/<br>.*/', '', $tmpcontent);
373  $firstline = preg_replace('/[\n\r].*/', '', $firstline);
374  $tmpcontent = $firstline.((strlen($firstline) != strlen($tmpcontent)) ? '...' : '');
375  }
376  // We dont use dol_escape_htmltag to get the html formating active, but this need we must also
377  // clean data from some dangerous html
378  $ret .= dol_string_onlythesehtmltags(dol_htmlentitiesbr($tmpcontent));
379  } else {
380  if (empty($moreoptions['valuealreadyhtmlescaped'])) {
381  $ret .= dol_escape_htmltag($value);
382  } else {
383  $ret .= $value; // $value must be already html escaped.
384  }
385  }
386 
387  if ($formatfunc && method_exists($object, $formatfunc)) {
388  $ret = $object->$formatfunc($ret);
389  }
390  }
391  }
392  return $ret;
393  }
394 
406  public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '')
407  {
408  global $conf, $langs, $extralanguages;
409 
410  $result = '';
411 
412  // List of extra languages
413  $arrayoflangcode = array();
414  if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) {
415  $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
416  }
417 
418  if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
419  if (!is_object($extralanguages)) {
420  include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php';
421  $extralanguages = new ExtraLanguages($this->db);
422  }
423  $extralanguages->fetch_name_extralanguages('societe');
424 
425  if (!is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) {
426  return ''; // No extralang field to show
427  }
428 
429  $result .= '<!-- Widget for translation -->'."\n";
430  $result .= '<div class="inline-block paddingleft image-'.$object->element.'-'.$fieldname.'">';
431  $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', false, 0, 0, '', 'fa-15 editfieldlang');
432  $result .= $s;
433  $result .= '</div>';
434 
435  $result .= '<div class="inline-block hidden field-'.$object->element.'-'.$fieldname.'">';
436 
437  $resultforextrlang = '';
438  foreach ($arrayoflangcode as $langcode) {
439  $valuetoshow = GETPOSTISSET('field-'.$object->element."-".$fieldname."-".$langcode) ? GETPOST('field-'.$object->element.'-'.$fieldname."-".$langcode, $check) : '';
440  if (empty($valuetoshow)) {
441  $object->fetchValuesForExtraLanguages();
442  //var_dump($object->array_languages);
443  $valuetoshow = $object->array_languages[$fieldname][$langcode];
444  }
445 
446  $s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"');
447  $resultforextrlang .= $s;
448 
449  // TODO Use the showInputField() method of ExtraLanguages object
450  if ($typeofdata == 'textarea') {
451  $resultforextrlang .= '<textarea name="field-'.$object->element."-".$fieldname."-".$langcode.'" id="'.$fieldname."-".$langcode.'" class="'.$morecss.'" rows="'.ROWS_2.'" wrap="soft">';
452  $resultforextrlang .= $valuetoshow;
453  $resultforextrlang .= '</textarea>';
454  } else {
455  $resultforextrlang .= '<input type="text" class="inputfieldforlang '.($morecss ? ' '.$morecss : '').'" name="field-'.$object->element.'-'.$fieldname.'-'.$langcode.'" value="'.$valuetoshow.'">';
456  }
457  }
458  $result .= $resultforextrlang;
459 
460  $result .= '</div>';
461  $result .= '<script nonce="'.getNonce().'">$(".image-'.$object->element.'-'.$fieldname.'").click(function() { console.log("Toggle lang widget"); jQuery(".field-'.$object->element.'-'.$fieldname.'").toggle(); });</script>';
462  }
463 
464  return $result;
465  }
466 
480  protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null)
481  {
482  global $conf;
483 
484  $out = '';
485 
486  // Check parameters
487  if (preg_match('/^text/', $inputType)) {
488  $value = dol_nl2br($value);
489  } elseif (preg_match('/^numeric/', $inputType)) {
490  $value = price($value);
491  } elseif ($inputType == 'day' || $inputType == 'datepicker') {
492  $value = dol_print_date($value, 'day');
493  }
494 
495  if ($condition) {
496  $element = false;
497  $table_element = false;
498  $fk_element = false;
499  $loadmethod = false;
500  $savemethod = false;
501  $ext_element = false;
502  $button_only = false;
503  $inputOption = '';
504  $rows = '';
505  $cols = '';
506 
507  if (is_object($object)) {
508  $element = $object->element;
509  $table_element = $object->table_element;
510  $fk_element = $object->id;
511  }
512 
513  if (is_object($extObject)) {
514  $ext_element = $extObject->element;
515  }
516 
517  if (preg_match('/^(string|email|numeric)/', $inputType)) {
518  $tmp = explode(':', $inputType);
519  $inputType = $tmp[0];
520  if (!empty($tmp[1])) {
521  $inputOption = $tmp[1];
522  }
523  if (!empty($tmp[2])) {
524  $savemethod = $tmp[2];
525  }
526  $out .= '<input id="width_'.$htmlname.'" value="'.$inputOption.'" type="hidden"/>'."\n";
527  } elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) {
528  $tmp = explode(':', $inputType);
529  $inputType = $tmp[0];
530  if (!empty($tmp[1])) {
531  $inputOption = $tmp[1];
532  }
533  if (!empty($tmp[2])) {
534  $savemethod = $tmp[2];
535  }
536 
537  $out .= '<input id="timestamp" type="hidden"/>'."\n"; // Use for timestamp format
538  } elseif (preg_match('/^(select|autocomplete)/', $inputType)) {
539  $tmp = explode(':', $inputType);
540  $inputType = $tmp[0];
541  $loadmethod = $tmp[1];
542  if (!empty($tmp[2])) {
543  $savemethod = $tmp[2];
544  }
545  if (!empty($tmp[3])) {
546  $button_only = true;
547  }
548  } elseif (preg_match('/^textarea/', $inputType)) {
549  $tmp = explode(':', $inputType);
550  $inputType = $tmp[0];
551  $rows = (empty($tmp[1]) ? '8' : $tmp[1]);
552  $cols = (empty($tmp[2]) ? '80' : $tmp[2]);
553  } elseif (preg_match('/^ckeditor/', $inputType)) {
554  $tmp = explode(':', $inputType);
555  $inputType = $tmp[0];
556  $toolbar = $tmp[1];
557  if (!empty($tmp[2])) {
558  $width = $tmp[2];
559  }
560  if (!empty($tmp[3])) {
561  $heigth = $tmp[3];
562  }
563  if (!empty($tmp[4])) {
564  $savemethod = $tmp[4];
565  }
566 
567  if (isModEnabled('fckeditor')) {
568  $out .= '<input id="ckeditor_toolbar" value="'.$toolbar.'" type="hidden"/>'."\n";
569  } else {
570  $inputType = 'textarea';
571  }
572  }
573 
574  $out .= '<input id="element_'.$htmlname.'" value="'.$element.'" type="hidden"/>'."\n";
575  $out .= '<input id="table_element_'.$htmlname.'" value="'.$table_element.'" type="hidden"/>'."\n";
576  $out .= '<input id="fk_element_'.$htmlname.'" value="'.$fk_element.'" type="hidden"/>'."\n";
577  $out .= '<input id="loadmethod_'.$htmlname.'" value="'.$loadmethod.'" type="hidden"/>'."\n";
578  if (!empty($savemethod)) {
579  $out .= '<input id="savemethod_'.$htmlname.'" value="'.$savemethod.'" type="hidden"/>'."\n";
580  }
581  if (!empty($ext_element)) {
582  $out .= '<input id="ext_element_'.$htmlname.'" value="'.$ext_element.'" type="hidden"/>'."\n";
583  }
584  if (!empty($custommsg)) {
585  if (is_array($custommsg)) {
586  if (!empty($custommsg['success'])) {
587  $out .= '<input id="successmsg_'.$htmlname.'" value="'.$custommsg['success'].'" type="hidden"/>'."\n";
588  }
589  if (!empty($custommsg['error'])) {
590  $out .= '<input id="errormsg_'.$htmlname.'" value="'.$custommsg['error'].'" type="hidden"/>'."\n";
591  }
592  } else {
593  $out .= '<input id="successmsg_'.$htmlname.'" value="'.$custommsg.'" type="hidden"/>'."\n";
594  }
595  }
596  if ($inputType == 'textarea') {
597  $out .= '<input id="textarea_'.$htmlname.'_rows" value="'.$rows.'" type="hidden"/>'."\n";
598  $out .= '<input id="textarea_'.$htmlname.'_cols" value="'.$cols.'" type="hidden"/>'."\n";
599  }
600  $out .= '<span id="viewval_'.$htmlname.'" class="viewval_'.$inputType.($button_only ? ' inactive' : ' active').'">'.$value.'</span>'."\n";
601  $out .= '<span id="editval_'.$htmlname.'" class="editval_'.$inputType.($button_only ? ' inactive' : ' active').' hideobject">'.(!empty($editvalue) ? $editvalue : $value).'</span>'."\n";
602  } else {
603  $out = $value;
604  }
605 
606  return $out;
607  }
608 
627  public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
628  {
629  if ($incbefore) {
630  $text = $incbefore.$text;
631  }
632  if (!$htmltext) {
633  return $text;
634  }
635  $direction = (int) $direction; // For backward compatibility when $direction was set to '' instead of 0
636 
637  $tag = 'td';
638  if ($notabs == 2) {
639  $tag = 'div';
640  }
641  if ($notabs == 3) {
642  $tag = 'span';
643  }
644  // Sanitize tooltip
645  $htmltext = str_replace(array("\r", "\n"), '', $htmltext);
646 
647  $extrastyle = '';
648  if ($direction < 0) {
649  $extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : '');
650  $extrastyle = 'padding: 0px; padding-left: 3px;';
651  }
652  if ($direction > 0) {
653  $extracss = ($extracss ? $extracss.' ' : '').($notabs != 3 ? 'inline-block' : '');
654  $extrastyle = 'padding: 0px; padding-right: 3px;';
655  }
656 
657  $classfortooltip = 'classfortooltip';
658 
659  $s = '';
660  $textfordialog = '';
661 
662  if ($tooltiptrigger == '') {
663  $htmltext = str_replace('"', '&quot;', $htmltext);
664  } else {
665  $classfortooltip = 'classfortooltiponclick';
666  $textfordialog .= '<div style="display: none;" id="idfortooltiponclick_'.$tooltiptrigger.'" class="classfortooltiponclicktext">'.$htmltext.'</div>';
667  }
668  if ($tooltipon == 2 || $tooltipon == 3) {
669  $paramfortooltipimg = ' class="'.$classfortooltip.($notabs != 3 ? ' inline-block' : '').($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'"';
670  if ($tooltiptrigger == '') {
671  $paramfortooltipimg .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on img tag to store tooltip
672  } else {
673  $paramfortooltipimg .= ' dolid="'.$tooltiptrigger.'"';
674  }
675  } else {
676  $paramfortooltipimg = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag
677  }
678  if ($tooltipon == 1 || $tooltipon == 3) {
679  $paramfortooltiptd = ' class="'.($tooltipon == 3 ? 'cursorpointer ' : '').$classfortooltip.' inline-block'.($extracss ? ' '.$extracss : '').'" style="padding: 0px;'.($extrastyle ? ' '.$extrastyle : '').'" ';
680  if ($tooltiptrigger == '') {
681  $paramfortooltiptd .= ' title="'.($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1)).'"'; // Attribut to put on td tag to store tooltip
682  } else {
683  $paramfortooltiptd .= ' dolid="'.$tooltiptrigger.'"';
684  }
685  } else {
686  $paramfortooltiptd = ($extracss ? ' class="'.$extracss.'"' : '').($extrastyle ? ' style="'.$extrastyle.'"' : ''); // Attribut to put on td text tag
687  }
688  if (empty($notabs)) {
689  $s .= '<table class="nobordernopadding"><tr style="height: auto;">';
690  } elseif ($notabs == 2) {
691  $s .= '<div class="inline-block'.($forcenowrap ? ' nowrap' : '').'">';
692  }
693  // Define value if value is before
694  if ($direction < 0) {
695  $s .= '<'.$tag.$paramfortooltipimg;
696  if ($tag == 'td') {
697  $s .= ' class="valigntop" width="14"';
698  }
699  $s .= '>'.$textfordialog.$img.'</'.$tag.'>';
700  }
701  // Use another method to help avoid having a space in value in order to use this value with jquery
702  // Define label
703  if ((string) $text != '') {
704  $s .= '<'.$tag.$paramfortooltiptd.'>'.$text.'</'.$tag.'>';
705  }
706  // Define value if value is after
707  if ($direction > 0) {
708  $s .= '<'.$tag.$paramfortooltipimg;
709  if ($tag == 'td') {
710  $s .= ' class="valignmiddle" width="14"';
711  }
712  $s .= '>'.$textfordialog.$img.'</'.$tag.'>';
713  }
714  if (empty($notabs)) {
715  $s .= '</tr></table>';
716  } elseif ($notabs == 2) {
717  $s .= '</div>';
718  }
719 
720  return $s;
721  }
722 
737  public function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
738  {
739  global $conf, $langs;
740 
741  //For backwards compatibility
742  if ($type == '0') {
743  $type = 'info';
744  } elseif ($type == '1') {
745  $type = 'help';
746  }
747 
748  if (preg_match('/onsmartphone$/', $tooltiptrigger) && empty($conf->dol_no_mouse_hover)) {
749  $tooltiptrigger = preg_replace('/^.*onsmartphone$/', '', $tooltiptrigger);
750  }
751 
752  $alt = '';
753  if ($tooltiptrigger) {
754  $alt = $langs->transnoentitiesnoconv("ClickToShowHelp");
755  }
756 
757  // If info or help with no javascript, show only text
758  if (empty($conf->use_javascript_ajax)) {
759  if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
760  return $text;
761  } else {
762  $alt = $htmltext;
763  $htmltext = '';
764  }
765  }
766 
767  // If info or help with smartphone, show only text (tooltip hover can't works)
768  if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) {
769  if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
770  return $text;
771  }
772  }
773  // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone)
774  //if (!empty($conf->dol_no_mouse_hover) && !empty($tooltiptrigger))
775  //{
776  //if ($type == 'info' || $type == 'help') return '<a href="'..'">'.$text.'</a>';
777  //}
778 
779  $img = '';
780  if ($type == 'info') {
781  $img = img_help(0, $alt);
782  } elseif ($type == 'help') {
783  $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
784  } elseif ($type == 'helpclickable') {
785  $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
786  } elseif ($type == 'superadmin') {
787  $img = img_picto($alt, 'redstar');
788  } elseif ($type == 'admin') {
789  $img = img_picto($alt, 'star');
790  } elseif ($type == 'warning') {
791  $img = img_warning($alt);
792  } elseif ($type != 'none') {
793  $img = img_picto($alt, $type); // $type can be an image path
794  }
795 
796  return $this->textwithtooltip($text, $htmltext, ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2), $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
797  }
798 
809  public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect')
810  {
811  global $conf, $langs, $hookmanager;
812 
813 
814  $disabled = 0;
815  $ret = '<div class="centpercent center">';
816  $ret .= '<select class="flat'.(empty($conf->use_javascript_ajax) ? '' : ' hideobject').' '.$name.' '.$name.'select valignmiddle alignstart" id="'.$name.'" name="'.$name.'"'.($disabled ? ' disabled="disabled"' : '').'>';
817 
818  // 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.
819  $parameters = array();
820  $reshook = $hookmanager->executeHooks('addMoreMassActions', $parameters); // Note that $action and $object may have been modified by hook
821  // check if there is a mass action
822  if (count($arrayofaction) == 0 && empty($hookmanager->resPrint)) {
823  return;
824  }
825  if (empty($reshook)) {
826  $ret .= '<option value="0"'.($disabled ? ' disabled="disabled"' : '').'>-- '.$langs->trans("SelectAction").' --</option>';
827  foreach ($arrayofaction as $code => $label) {
828  $ret .= '<option value="'.$code.'"'.($disabled ? ' disabled="disabled"' : '').' data-html="'.dol_escape_htmltag($label).'">'.$label.'</option>';
829  }
830  }
831  $ret .= $hookmanager->resPrint;
832 
833  $ret .= '</select>';
834 
835  if (empty($conf->dol_optimize_smallscreen)) {
836  $ret .= ajax_combobox('.'.$name.'select');
837  }
838 
839  // 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
840  $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.
841  $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")).'">';
842  $ret .= '</div>';
843 
844  if (!empty($conf->use_javascript_ajax)) {
845  $ret .= '<!-- JS CODE TO ENABLE mass action select -->
846  <script nonce="'.getNonce().'">
847  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 */
848  {
849  atleastoneselected=0;
850  jQuery("."+cssclass).each(function( index ) {
851  /* console.log( index + ": " + $( this ).text() ); */
852  if ($(this).is(\':checked\')) atleastoneselected++;
853  });
854 
855  console.log("initCheckForSelect mode="+mode+" name="+name+" cssclass="+cssclass+" atleastoneselected="+atleastoneselected);
856 
857  if (atleastoneselected || '.$alwaysvisible.')
858  {
859  jQuery("."+name).show();
860  '.($selected ? 'if (atleastoneselected) { jQuery("."+name+"select").val("'.$selected.'").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', false); }' : '').'
861  '.($selected ? 'if (! atleastoneselected) { jQuery("."+name+"select").val("0").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', true); } ' : '').'
862  }
863  else
864  {
865  jQuery("."+name).hide();
866  jQuery("."+name+"other").hide();
867  }
868  }
869 
870  jQuery(document).ready(function () {
871  initCheckForSelect(0, "' . $name.'", "'.$cssclass.'");
872  jQuery(".' . $cssclass.'").click(function() {
873  initCheckForSelect(1, "'.$name.'", "'.$cssclass.'");
874  });
875  jQuery(".' . $name.'select").change(function() {
876  var massaction = $( this ).val();
877  var urlform = $( this ).closest("form").attr("action").replace("#show_files","");
878  if (massaction == "builddoc")
879  {
880  urlform = urlform + "#show_files";
881  }
882  $( this ).closest("form").attr("action", urlform);
883  console.log("we select a mass action name='.$name.' massaction="+massaction+" - "+urlform);
884  /* Warning: if you set submit button to disabled, post using Enter will no more work if there is no other button */
885  if ($(this).val() != \'0\')
886  {
887  jQuery(".' . $name.'confirmed").prop(\'disabled\', false);
888  jQuery(".' . $name.'other").hide(); /* To disable if another div was open */
889  jQuery(".' . $name.'"+massaction).show();
890  }
891  else
892  {
893  jQuery(".' . $name.'confirmed").prop(\'disabled\', true);
894  jQuery(".' . $name.'other").hide(); /* To disable any div open */
895  }
896  });
897  });
898  </script>
899  ';
900  }
901 
902  return $ret;
903  }
904 
905  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
922  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)
923  {
924  // phpcs:enable
925  global $conf, $langs, $mysoc;
926 
927  $langs->load("dict");
928 
929  $out = '';
930  $countryArray = array();
931  $favorite = array();
932  $label = array();
933  $atleastonefavorite = 0;
934 
935  $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite, eec";
936  $sql .= " FROM ".$this->db->prefix()."c_country";
937  $sql .= " WHERE active > 0";
938  //$sql.= " ORDER BY code ASC";
939 
940  dol_syslog(get_class($this)."::select_country", LOG_DEBUG);
941  $resql = $this->db->query($sql);
942  if ($resql) {
943  $out .= '<select id="select'.$htmlname.'" class="flat maxwidth200onsmartphone selectcountry'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" '.$htmloption.'>';
944  $num = $this->db->num_rows($resql);
945  $i = 0;
946  if ($num) {
947  while ($i < $num) {
948  $obj = $this->db->fetch_object($resql);
949 
950  $countryArray[$i]['rowid'] = $obj->rowid;
951  $countryArray[$i]['code_iso'] = $obj->code_iso;
952  $countryArray[$i]['code_iso3'] = $obj->code_iso3;
953  $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 : ''));
954  $countryArray[$i]['favorite'] = $obj->favorite;
955  $countryArray[$i]['eec'] = $obj->eec;
956  $favorite[$i] = $obj->favorite;
957  $label[$i] = dol_string_unaccent($countryArray[$i]['label']);
958  $i++;
959  }
960 
961  if (empty($disablefavorites)) {
962  $array1_sort_order = SORT_DESC;
963  $array2_sort_order = SORT_ASC;
964  array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $countryArray);
965  } else {
966  $countryArray = dol_sort_array($countryArray, 'label');
967  }
968 
969  if ($showempty) {
970  if (is_numeric($showempty)) {
971  $out .= '<option value="">&nbsp;</option>'."\n";
972  } else {
973  $out .= '<option value="">'.$langs->trans($showempty).'</option>'."\n";
974  }
975  }
976 
977  if ($addspecialentries) { // Add dedicated entries for groups of countries
978  //if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
979  $out .= '<option value="special_allnotme"'.($selected == 'special_allnotme' ? ' selected' : '').'>'.$langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
980  $out .= '<option value="special_eec"'.($selected == 'special_eec' ? ' selected' : '').'>'.$langs->trans("CountriesInEEC").'</option>';
981  if ($mysoc->isInEEC()) {
982  $out .= '<option value="special_eecnotme"'.($selected == 'special_eecnotme' ? ' selected' : '').'>'.$langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country".$mysoc->country_code)).'</option>';
983  }
984  $out .= '<option value="special_noteec"'.($selected == 'special_noteec' ? ' selected' : '').'>'.$langs->trans("CountriesNotInEEC").'</option>';
985  $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
986  }
987 
988  foreach ($countryArray as $row) {
989  //if (empty($showempty) && empty($row['rowid'])) continue;
990  if (empty($row['rowid'])) {
991  continue;
992  }
993  if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
994  continue; // exclude some countries
995  }
996 
997  if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
998  $atleastonefavorite++;
999  }
1000  if (empty($row['favorite']) && $atleastonefavorite) {
1001  $atleastonefavorite = 0;
1002  $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1003  }
1004 
1005  $labeltoshow = '';
1006  if ($row['label']) {
1007  $labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
1008  } else {
1009  $labeltoshow .= '&nbsp;';
1010  }
1011  if ($row['code_iso']) {
1012  $labeltoshow .= ' <span class="opacitymedium">('.$row['code_iso'].')</span>';
1013  if (empty($hideflags)) {
1014  $tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
1015  $labeltoshow = $tmpflag.' '.$labeltoshow;
1016  }
1017  }
1018 
1019  if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
1020  $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']).'">';
1021  } else {
1022  $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']).'">';
1023  }
1024  $out .= $labeltoshow;
1025  $out .= '</option>'."\n";
1026  }
1027  }
1028  $out .= '</select>';
1029  } else {
1030  dol_print_error($this->db);
1031  }
1032 
1033  // Make select dynamic
1034  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1035  $out .= ajax_combobox('select'.$htmlname, array(), 0, 0, 'resolve');
1036 
1037  return $out;
1038  }
1039 
1040  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1054  public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array(), $disableautocomplete = 0)
1055  {
1056  // phpcs:enable
1057  global $conf, $langs;
1058 
1059  $langs->load("dict");
1060 
1061  $out = '';
1062  $moreattrib = '';
1063  $incotermArray = array();
1064 
1065  $sql = "SELECT rowid, code";
1066  $sql .= " FROM ".$this->db->prefix()."c_incoterms";
1067  $sql .= " WHERE active > 0";
1068  $sql .= " ORDER BY code ASC";
1069 
1070  dol_syslog(get_class($this)."::select_incoterm", LOG_DEBUG);
1071  $resql = $this->db->query($sql);
1072  if ($resql) {
1073  if ($conf->use_javascript_ajax && !$forcecombo) {
1074  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1075  $out .= ajax_combobox($htmlname, $events);
1076  }
1077 
1078  if (!empty($page)) {
1079  $out .= '<form method="post" action="'.$page.'">';
1080  $out .= '<input type="hidden" name="action" value="set_incoterms">';
1081  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
1082  }
1083 
1084  $out .= '<select id="'.$htmlname.'" class="flat selectincoterm width75" name="'.$htmlname.'" '.$htmloption.'>';
1085  $out .= '<option value="0">&nbsp;</option>';
1086  $num = $this->db->num_rows($resql);
1087  $i = 0;
1088  if ($num) {
1089  while ($i < $num) {
1090  $obj = $this->db->fetch_object($resql);
1091  $incotermArray[$i]['rowid'] = $obj->rowid;
1092  $incotermArray[$i]['code'] = $obj->code;
1093  $i++;
1094  }
1095 
1096  foreach ($incotermArray as $row) {
1097  if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1098  $out .= '<option value="'.$row['rowid'].'" selected>';
1099  } else {
1100  $out .= '<option value="'.$row['rowid'].'">';
1101  }
1102 
1103  if ($row['code']) {
1104  $out .= $row['code'];
1105  }
1106 
1107  $out .= '</option>';
1108  }
1109  }
1110  $out .= '</select>';
1111 
1112  if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
1113  $out .= ajax_multiautocompleter('location_incoterms', '', DOL_URL_ROOT.'/core/ajax/locationincoterms.php')."\n";
1114  $moreattrib .= ' autocomplete="off"';
1115  }
1116  $out .= '<input id="location_incoterms" class="maxwidthonsmartphone type="text" name="location_incoterms" value="'.$location_incoterms.'">'."\n";
1117 
1118  if (!empty($page)) {
1119  $out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="'.$langs->trans("Modify").'"></form>';
1120  }
1121  } else {
1122  dol_print_error($this->db);
1123  }
1124 
1125  return $out;
1126  }
1127 
1128  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1140  public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0)
1141  {
1142  // phpcs:enable
1143  global $langs, $conf;
1144 
1145  // If product & services are enabled or both disabled.
1146  if ($forceall == 1 || (empty($forceall) && isModEnabled("product") && isModEnabled("service"))
1147  || (empty($forceall) && !isModEnabled('product') && !isModEnabled('service'))) {
1148  if (empty($hidetext)) {
1149  print $langs->trans("Type").': ';
1150  }
1151  print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
1152  if ($showempty) {
1153  print '<option value="-1"';
1154  if ($selected == -1) {
1155  print ' selected';
1156  }
1157  print '>&nbsp;</option>';
1158  }
1159 
1160  print '<option value="0"';
1161  if (0 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'product')) {
1162  print ' selected';
1163  }
1164  print '>'.$langs->trans("Product");
1165 
1166  print '<option value="1"';
1167  if (1 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'service')) {
1168  print ' selected';
1169  }
1170  print '>'.$langs->trans("Service");
1171 
1172  print '</select>';
1173  print ajax_combobox('select_'.$htmlname);
1174  //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1175  }
1176  if ((empty($forceall) && !isModEnabled('product') && isModEnabled("service")) || $forceall == 3) {
1177  print $langs->trans("Service");
1178  print '<input type="hidden" name="'.$htmlname.'" value="1">';
1179  }
1180  if ((empty($forceall) && isModEnabled("product") && !isModEnabled('service')) || $forceall == 2) {
1181  print $langs->trans("Product");
1182  print '<input type="hidden" name="'.$htmlname.'" value="0">';
1183  }
1184  if ($forceall < 0) { // This should happened only for contracts when both predefined product and service are disabled.
1185  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
1186  }
1187  }
1188 
1189  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1195  public function load_cache_types_fees()
1196  {
1197  // phpcs:enable
1198  global $langs;
1199 
1200  $num = count($this->cache_types_fees);
1201  if ($num > 0) {
1202  return 0; // Cache already loaded
1203  }
1204 
1205  dol_syslog(__METHOD__, LOG_DEBUG);
1206 
1207  $langs->load("trips");
1208 
1209  $sql = "SELECT c.code, c.label";
1210  $sql .= " FROM ".$this->db->prefix()."c_type_fees as c";
1211  $sql .= " WHERE active > 0";
1212 
1213  $resql = $this->db->query($sql);
1214  if ($resql) {
1215  $num = $this->db->num_rows($resql);
1216  $i = 0;
1217 
1218  while ($i < $num) {
1219  $obj = $this->db->fetch_object($resql);
1220 
1221  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1222  $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1223  $this->cache_types_fees[$obj->code] = $label;
1224  $i++;
1225  }
1226 
1227  asort($this->cache_types_fees);
1228 
1229  return $num;
1230  } else {
1231  dol_print_error($this->db);
1232  return -1;
1233  }
1234  }
1235 
1236  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1245  public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1246  {
1247  // phpcs:enable
1248  global $user, $langs;
1249 
1250  dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
1251 
1252  $this->load_cache_types_fees();
1253 
1254  print '<select id="select_'.$htmlname.'" class="flat" name="'.$htmlname.'">';
1255  if ($showempty) {
1256  print '<option value="-1"';
1257  if ($selected == -1) {
1258  print ' selected';
1259  }
1260  print '>&nbsp;</option>';
1261  }
1262 
1263  foreach ($this->cache_types_fees as $key => $value) {
1264  print '<option value="'.$key.'"';
1265  if ($key == $selected) {
1266  print ' selected';
1267  }
1268  print '>';
1269  print $value;
1270  print '</option>';
1271  }
1272 
1273  print '</select>';
1274  if ($user->admin) {
1275  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1276  }
1277  }
1278 
1279 
1280  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1302  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)
1303  {
1304  // phpcs:enable
1305  global $conf, $user, $langs;
1306 
1307  $out = '';
1308 
1309  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT) && !$forcecombo) {
1310  if (is_null($ajaxoptions)) {
1311  $ajaxoptions = array();
1312  }
1313 
1314  require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1315 
1316  // No immediate load of all database
1317  $placeholder = '';
1318  if ($selected && empty($selected_input_value)) {
1319  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1320  $societetmp = new Societe($this->db);
1321  $societetmp->fetch($selected);
1322  $selected_input_value = $societetmp->name;
1323  unset($societetmp);
1324  }
1325 
1326  // mode 1
1327  $urloption = 'htmlname='.urlencode(str_replace('.', '_', $htmlname)).'&outjson=1&filter='.urlencode($filter).(empty($excludeids) ? '' : '&excludeids='.join(',', $excludeids)).($showtype ? '&showtype='.urlencode($showtype) : '').($showcode ? '&showcode='.urlencode($showcode) : '');
1328 
1329  $out .= '<style type="text/css">.ui-autocomplete { z-index: 1003; }</style>';
1330  if (empty($hidelabel)) {
1331  print $langs->trans("RefOrLabel").' : ';
1332  } elseif ($hidelabel > 1) {
1333  $placeholder = $langs->trans("RefOrLabel");
1334  if ($hidelabel == 2) {
1335  $out .= img_picto($langs->trans("Search"), 'search');
1336  }
1337  }
1338  $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' : '').' />';
1339  if ($hidelabel == 3) {
1340  $out .= img_picto($langs->trans("Search"), 'search');
1341  }
1342 
1343  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, $conf->global->COMPANY_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
1344  } else {
1345  // Immediate load of all database
1346  $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids, $showcode);
1347  }
1348 
1349  return $out;
1350  }
1351 
1352  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1374  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)
1375  {
1376  // phpcs:enable
1377  global $conf, $user, $langs;
1378  global $hookmanager;
1379 
1380  $out = '';
1381  $num = 0;
1382  $outarray = array();
1383 
1384  if ($selected === '') {
1385  $selected = array();
1386  } elseif (!is_array($selected)) {
1387  $selected = array($selected);
1388  }
1389 
1390  // Clean $filter that may contains sql conditions so sql code
1391  if (function_exists('testSqlAndScriptInject')) {
1392  if (testSqlAndScriptInject($filter, 3) > 0) {
1393  $filter = '';
1394  }
1395  }
1396 
1397  // We search companies
1398  $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
1399  if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1400  $sql .= ", s.address, s.zip, s.town";
1401  $sql .= ", dictp.code as country_code";
1402  }
1403  $sql .= " FROM ".$this->db->prefix()."societe as s";
1404  if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1405  $sql .= " LEFT JOIN ".$this->db->prefix()."c_country as dictp ON dictp.rowid = s.fk_pays";
1406  }
1407  if (empty($user->rights->societe->client->voir) && !$user->socid) {
1408  $sql .= ", ".$this->db->prefix()."societe_commerciaux as sc";
1409  }
1410  $sql .= " WHERE s.entity IN (".getEntity('societe').")";
1411  if (!empty($user->socid)) {
1412  $sql .= " AND s.rowid = ".((int) $user->socid);
1413  }
1414  if ($filter) {
1415  $sql .= " AND (".$filter.")";
1416  }
1417  if (empty($user->rights->societe->client->voir) && !$user->socid) {
1418  $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
1419  }
1420  if (!empty($conf->global->COMPANY_HIDE_INACTIVE_IN_COMBOBOX)) {
1421  $sql .= " AND s.status <> 0";
1422  }
1423  if (!empty($excludeids)) {
1424  $sql .= " AND s.rowid NOT IN (".$this->db->sanitize(join(',', $excludeids)).")";
1425  }
1426  // Add where from hooks
1427  $parameters = array();
1428  $reshook = $hookmanager->executeHooks('selectThirdpartyListWhere', $parameters); // Note that $action and $object may have been modified by hook
1429  $sql .= $hookmanager->resPrint;
1430  // Add criteria
1431  if ($filterkey && $filterkey != '') {
1432  $sql .= " AND (";
1433  $prefix = empty($conf->global->COMPANY_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
1434  // For natural search
1435  $scrit = explode(' ', $filterkey);
1436  $i = 0;
1437  if (count($scrit) > 1) {
1438  $sql .= "(";
1439  }
1440  foreach ($scrit as $crit) {
1441  if ($i > 0) {
1442  $sql .= " AND ";
1443  }
1444  $sql .= "(s.nom LIKE '".$this->db->escape($prefix.$crit)."%')";
1445  $i++;
1446  }
1447  if (count($scrit) > 1) {
1448  $sql .= ")";
1449  }
1450  if (isModEnabled('barcode')) {
1451  $sql .= " OR s.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1452  }
1453  $sql .= " OR s.code_client LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.code_fournisseur LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1454  $sql .= " OR s.name_alias LIKE '".$this->db->escape($prefix.$filterkey)."%' OR s.tva_intra LIKE '".$this->db->escape($prefix.$filterkey)."%'";
1455  $sql .= ")";
1456  }
1457  $sql .= $this->db->order("nom", "ASC");
1458  $sql .= $this->db->plimit($limit, 0);
1459 
1460  // Build output string
1461  dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
1462  $resql = $this->db->query($sql);
1463  if ($resql) {
1464  if (!$forcecombo) {
1465  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1466  $out .= ajax_combobox($htmlname, $events, getDolGlobalString("COMPANY_USE_SEARCH_TO_SELECT"));
1467  }
1468 
1469  // Construct $out and $outarray
1470  $out .= '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').'>'."\n";
1471 
1472  $textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
1473  if (!empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT)) {
1474  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
1475  //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
1476  if ($showempty && !is_numeric($showempty)) {
1477  $textifempty = $langs->trans($showempty);
1478  } else {
1479  $textifempty .= $langs->trans("All");
1480  }
1481  }
1482  if ($showempty) {
1483  $out .= '<option value="-1" data-html="'.dol_escape_htmltag('<span class="opacitymedium">'.($textifempty ? $textifempty : '&nbsp;').'</span>').'">'.$textifempty.'</option>'."\n";
1484  }
1485 
1486  $companytemp = new Societe($this->db);
1487 
1488  $num = $this->db->num_rows($resql);
1489  $i = 0;
1490  if ($num) {
1491  while ($i < $num) {
1492  $obj = $this->db->fetch_object($resql);
1493  $label = '';
1494  if ($showcode || !empty($conf->global->SOCIETE_ADD_REF_IN_LIST)) {
1495  if (($obj->client) && (!empty($obj->code_client))) {
1496  $label = $obj->code_client.' - ';
1497  }
1498  if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
1499  $label .= $obj->code_fournisseur.' - ';
1500  }
1501  $label .= ' '.$obj->name;
1502  } else {
1503  $label = $obj->name;
1504  }
1505 
1506  if (!empty($obj->name_alias)) {
1507  $label .= ' ('.$obj->name_alias.')';
1508  }
1509 
1510  if (!empty($conf->global->SOCIETE_SHOW_VAT_IN_LIST) && !empty($obj->tva_intra)) {
1511  $label .= ' - '.$obj->tva_intra;
1512  }
1513 
1514  $labelhtml = $label;
1515 
1516  if ($showtype) {
1517  $companytemp->id = $obj->rowid;
1518  $companytemp->client = $obj->client;
1519  $companytemp->fournisseur = $obj->fournisseur;
1520  $tmptype = $companytemp->getTypeUrl(1, '', 0, 'span');
1521  if ($tmptype) {
1522  $labelhtml .= ' '.$tmptype;
1523  }
1524 
1525  if ($obj->client || $obj->fournisseur) {
1526  $label .= ' (';
1527  }
1528  if ($obj->client == 1 || $obj->client == 3) {
1529  $label .= $langs->trans("Customer");
1530  }
1531  if ($obj->client == 2 || $obj->client == 3) {
1532  $label .= ($obj->client == 3 ? ', ' : '').$langs->trans("Prospect");
1533  }
1534  if ($obj->fournisseur) {
1535  $label .= ($obj->client ? ', ' : '').$langs->trans("Supplier");
1536  }
1537  if ($obj->client || $obj->fournisseur) {
1538  $label .= ')';
1539  }
1540  }
1541 
1542  if (!empty($conf->global->COMPANY_SHOW_ADDRESS_SELECTLIST)) {
1543  $s = ($obj->address ? ' - '.$obj->address : '').($obj->zip ? ' - '.$obj->zip : '').($obj->town ? ' '.$obj->town : '');
1544  if (!empty($obj->country_code)) {
1545  $s .= ', '.$langs->trans('Country'.$obj->country_code);
1546  }
1547  $label .= $s;
1548  $labelhtml .= $s;
1549  }
1550 
1551  if (empty($outputmode)) {
1552  if (in_array($obj->rowid, $selected)) {
1553  $out .= '<option value="'.$obj->rowid.'" selected data-html="'.dol_escape_htmltag($labelhtml).'">'.$label.'</option>';
1554  } else {
1555  $out .= '<option value="'.$obj->rowid.'" data-html="'.dol_escape_htmltag($labelhtml).'">'.$label.'</option>';
1556  }
1557  } else {
1558  array_push($outarray, array('key'=>$obj->rowid, 'value'=>$label, 'label'=>$label, 'labelhtml'=>$labelhtml));
1559  }
1560 
1561  $i++;
1562  if (($i % 10) == 0) {
1563  $out .= "\n";
1564  }
1565  }
1566  }
1567  $out .= '</select>'."\n";
1568  } else {
1569  dol_print_error($this->db);
1570  }
1571 
1572  $this->result = array('nbofthirdparties'=>$num);
1573 
1574  if ($outputmode) {
1575  return $outarray;
1576  }
1577  return $out;
1578  }
1579 
1580 
1581  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1592  public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
1593  {
1594  // phpcs:enable
1595  global $langs, $conf;
1596 
1597  // On recherche les remises
1598  $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
1599  $sql .= " re.description, re.fk_facture_source";
1600  $sql .= " FROM ".$this->db->prefix()."societe_remise_except as re";
1601  $sql .= " WHERE re.fk_soc = ".(int) $socid;
1602  $sql .= " AND re.entity = ".$conf->entity;
1603  if ($filter) {
1604  $sql .= " AND ".$filter;
1605  }
1606  $sql .= " ORDER BY re.description ASC";
1607 
1608  dol_syslog(get_class($this)."::select_remises", LOG_DEBUG);
1609  $resql = $this->db->query($sql);
1610  if ($resql) {
1611  print '<select id="select_'.$htmlname.'" class="flat maxwidthonsmartphone" name="'.$htmlname.'">';
1612  $num = $this->db->num_rows($resql);
1613 
1614  $qualifiedlines = $num;
1615 
1616  $i = 0;
1617  if ($num) {
1618  print '<option value="0">&nbsp;</option>';
1619  while ($i < $num) {
1620  $obj = $this->db->fetch_object($resql);
1621  $desc = dol_trunc($obj->description, 40);
1622  if (preg_match('/\(CREDIT_NOTE\)/', $desc)) {
1623  $desc = preg_replace('/\(CREDIT_NOTE\)/', $langs->trans("CreditNote"), $desc);
1624  }
1625  if (preg_match('/\(DEPOSIT\)/', $desc)) {
1626  $desc = preg_replace('/\(DEPOSIT\)/', $langs->trans("Deposit"), $desc);
1627  }
1628  if (preg_match('/\(EXCESS RECEIVED\)/', $desc)) {
1629  $desc = preg_replace('/\(EXCESS RECEIVED\)/', $langs->trans("ExcessReceived"), $desc);
1630  }
1631  if (preg_match('/\(EXCESS PAID\)/', $desc)) {
1632  $desc = preg_replace('/\(EXCESS PAID\)/', $langs->trans("ExcessPaid"), $desc);
1633  }
1634 
1635  $selectstring = '';
1636  if ($selected > 0 && $selected == $obj->rowid) {
1637  $selectstring = ' selected';
1638  }
1639 
1640  $disabled = '';
1641  if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
1642  $qualifiedlines--;
1643  $disabled = ' disabled';
1644  }
1645 
1646  if (!empty($conf->global->MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST) && !empty($obj->fk_facture_source)) {
1647  $tmpfac = new Facture($this->db);
1648  if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
1649  $desc = $desc.' - '.$tmpfac->ref;
1650  }
1651  }
1652 
1653  print '<option value="'.$obj->rowid.'"'.$selectstring.$disabled.'>'.$desc.' ('.price($obj->amount_ht).' '.$langs->trans("HT").' - '.price($obj->amount_ttc).' '.$langs->trans("TTC").')</option>';
1654  $i++;
1655  }
1656  }
1657  print '</select>';
1658  print ajax_combobox('select_'.$htmlname);
1659 
1660  return $qualifiedlines;
1661  } else {
1662  dol_print_error($this->db);
1663  return -1;
1664  }
1665  }
1666 
1667  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1688  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 = '')
1689  {
1690  // phpcs:enable
1691  print $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $morecss, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid);
1692  return $this->num;
1693  }
1694 
1719  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)
1720  {
1721  global $conf, $langs, $hookmanager, $action;
1722 
1723  $langs->load('companies');
1724 
1725  if (empty($htmlid)) {
1726  $htmlid = $htmlname;
1727  }
1728  $num = 0;
1729 
1730  if ($selected === '') {
1731  $selected = array();
1732  } elseif (!is_array($selected)) {
1733  $selected = array($selected);
1734  }
1735  $out = '';
1736 
1737  if (!is_object($hookmanager)) {
1738  include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
1739  $hookmanager = new HookManager($this->db);
1740  }
1741 
1742  // We search third parties
1743  $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";
1744  if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1745  $sql .= ", s.nom as company, s.town AS company_town";
1746  }
1747  $sql .= " FROM ".$this->db->prefix()."socpeople as sp";
1748  if ($showsoc > 0 || !empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1749  $sql .= " LEFT OUTER JOIN ".$this->db->prefix()."societe as s ON s.rowid=sp.fk_soc";
1750  }
1751  $sql .= " WHERE sp.entity IN (".getEntity('contact').")";
1752  if ($socid > 0 || $socid == -1) {
1753  $sql .= " AND sp.fk_soc = ".((int) $socid);
1754  }
1755  if (!empty($conf->global->CONTACT_HIDE_INACTIVE_IN_COMBOBOX)) {
1756  $sql .= " AND sp.statut <> 0";
1757  }
1758  // Add where from hooks
1759  $parameters = array();
1760  $reshook = $hookmanager->executeHooks('selectContactListWhere', $parameters); // Note that $action and $object may have been modified by hook
1761  $sql .= $hookmanager->resPrint;
1762  $sql .= " ORDER BY sp.lastname ASC";
1763 
1764  dol_syslog(get_class($this)."::selectcontacts", LOG_DEBUG);
1765  $resql = $this->db->query($sql);
1766  if ($resql) {
1767  $num = $this->db->num_rows($resql);
1768 
1769  if ($htmlname != 'none' && !$options_only) {
1770  $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="'.$htmlid.'" name="'.$htmlname.(($num || empty($disableifempty)) ? '' : ' disabled').($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.(!empty($moreparam) ? $moreparam : '').'>';
1771  }
1772 
1773  if ($showempty && ! is_numeric($showempty)) {
1774  $textforempty = $showempty;
1775  $out .= '<option class="optiongrey" value="-1"'.(in_array(-1, $selected) ? ' selected' : '').'>'.$textforempty.'</option>';
1776  } else {
1777  if (($showempty == 1 || ($showempty == 3 && $num > 1)) && ! $multiple) {
1778  $out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>&nbsp;</option>';
1779  }
1780  if ($showempty == 2) {
1781  $out .= '<option value="0"'.(in_array(0, $selected) ? ' selected' : '').'>-- '.$langs->trans("Internal").' --</option>';
1782  }
1783  }
1784 
1785  $i = 0;
1786  if ($num) {
1787  include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1788  $contactstatic = new Contact($this->db);
1789 
1790  while ($i < $num) {
1791  $obj = $this->db->fetch_object($resql);
1792 
1793  // Set email (or phones) and town extended infos
1794  $extendedInfos = '';
1795  if (!empty($conf->global->CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST)) {
1796  $extendedInfos = array();
1797  $email = trim($obj->email);
1798  if (!empty($email)) {
1799  $extendedInfos[] = $email;
1800  } else {
1801  $phone = trim($obj->phone);
1802  $phone_perso = trim($obj->phone_perso);
1803  $phone_mobile = trim($obj->phone_mobile);
1804  if (!empty($phone)) {
1805  $extendedInfos[] = $phone;
1806  }
1807  if (!empty($phone_perso)) {
1808  $extendedInfos[] = $phone_perso;
1809  }
1810  if (!empty($phone_mobile)) {
1811  $extendedInfos[] = $phone_mobile;
1812  }
1813  }
1814  $contact_town = trim($obj->contact_town);
1815  $company_town = trim($obj->company_town);
1816  if (!empty($contact_town)) {
1817  $extendedInfos[] = $contact_town;
1818  } elseif (!empty($company_town)) {
1819  $extendedInfos[] = $company_town;
1820  }
1821  $extendedInfos = implode(' - ', $extendedInfos);
1822  if (!empty($extendedInfos)) {
1823  $extendedInfos = ' - '.$extendedInfos;
1824  }
1825  }
1826 
1827  $contactstatic->id = $obj->rowid;
1828  $contactstatic->lastname = $obj->lastname;
1829  $contactstatic->firstname = $obj->firstname;
1830  if ($obj->statut == 1) {
1831  if ($htmlname != 'none') {
1832  $disabled = 0;
1833  if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
1834  $disabled = 1;
1835  }
1836  if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
1837  $disabled = 1;
1838  }
1839  if (!empty($selected) && in_array($obj->rowid, $selected)) {
1840  $out .= '<option value="'.$obj->rowid.'"';
1841  if ($disabled) {
1842  $out .= ' disabled';
1843  }
1844  $out .= ' selected>';
1845  $out .= $contactstatic->getFullName($langs).$extendedInfos;
1846  if ($showfunction && $obj->poste) {
1847  $out .= ' ('.$obj->poste.')';
1848  }
1849  if (($showsoc > 0) && $obj->company) {
1850  $out .= ' - ('.$obj->company.')';
1851  }
1852  $out .= '</option>';
1853  } else {
1854  $out .= '<option value="'.$obj->rowid.'"';
1855  if ($disabled) {
1856  $out .= ' disabled';
1857  }
1858  $out .= '>';
1859  $out .= $contactstatic->getFullName($langs).$extendedInfos;
1860  if ($showfunction && $obj->poste) {
1861  $out .= ' ('.$obj->poste.')';
1862  }
1863  if (($showsoc > 0) && $obj->company) {
1864  $out .= ' - ('.$obj->company.')';
1865  }
1866  $out .= '</option>';
1867  }
1868  } else {
1869  if (in_array($obj->rowid, $selected)) {
1870  $out .= $contactstatic->getFullName($langs).$extendedInfos;
1871  if ($showfunction && $obj->poste) {
1872  $out .= ' ('.$obj->poste.')';
1873  }
1874  if (($showsoc > 0) && $obj->company) {
1875  $out .= ' - ('.$obj->company.')';
1876  }
1877  }
1878  }
1879  }
1880  $i++;
1881  }
1882  } else {
1883  $labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
1884  $out .= '<option class="disabled" value="-1"'.(($showempty == 2 || $multiple) ? '' : ' selected').' disabled="disabled">';
1885  $out .= $labeltoshow;
1886  $out .= '</option>';
1887  }
1888 
1889  $parameters = array(
1890  'socid'=>$socid,
1891  'htmlname'=>$htmlname,
1892  'resql'=>$resql,
1893  'out'=>&$out,
1894  'showfunction'=>$showfunction,
1895  'showsoc'=>$showsoc,
1896  );
1897 
1898  $reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1899 
1900  if ($htmlname != 'none' && !$options_only) {
1901  $out .= '</select>';
1902  }
1903 
1904  if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
1905  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1906  $out .= ajax_combobox($htmlid, $events, getDolGlobalString("CONTACT_USE_SEARCH_TO_SELECT"));
1907  }
1908 
1909  $this->num = $num;
1910  return $out;
1911  } else {
1912  dol_print_error($this->db);
1913  return -1;
1914  }
1915  }
1916 
1917  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1933  public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0')
1934  {
1935  // phpcs:enable
1936  print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
1937  }
1938 
1939  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1964  public function select_dolusers($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $show_every = 0, $enableonlytext = '', $morecss = '', $notdisabled = 0, $outputmode = 0, $multiple = false, $forcecombo = 0)
1965  {
1966  // phpcs:enable
1967  global $conf, $user, $langs, $hookmanager;
1968  global $action;
1969 
1970  // If no preselected user defined, we take current user
1971  if ((is_numeric($selected) && ($selected < -2 || empty($selected))) && empty($conf->global->SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE)) {
1972  $selected = $user->id;
1973  }
1974 
1975  if ($selected === '') {
1976  $selected = array();
1977  } elseif (!is_array($selected)) {
1978  $selected = array($selected);
1979  }
1980 
1981  $excludeUsers = null;
1982  $includeUsers = null;
1983 
1984  // Permettre l'exclusion d'utilisateurs
1985  if (is_array($exclude)) {
1986  $excludeUsers = implode(",", $exclude);
1987  }
1988  // Permettre l'inclusion d'utilisateurs
1989  if (is_array($include)) {
1990  $includeUsers = implode(",", $include);
1991  } elseif ($include == 'hierarchy') {
1992  // Build list includeUsers to have only hierarchy
1993  $includeUsers = implode(",", $user->getAllChildIds(0));
1994  } elseif ($include == 'hierarchyme') {
1995  // Build list includeUsers to have only hierarchy and current user
1996  $includeUsers = implode(",", $user->getAllChildIds(1));
1997  }
1998 
1999  $out = '';
2000  $outarray = array();
2001 
2002  // Forge request to select users
2003  $sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.photo";
2004  if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
2005  $sql .= ", e.label";
2006  }
2007  $sql .= " FROM ".$this->db->prefix()."user as u";
2008  if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
2009  $sql .= " LEFT JOIN ".$this->db->prefix()."entity as e ON e.rowid = u.entity";
2010  if ($force_entity) {
2011  $sql .= " WHERE u.entity IN (0, ".$this->db->sanitize($force_entity).")";
2012  } else {
2013  $sql .= " WHERE u.entity IS NOT NULL";
2014  }
2015  } else {
2016  if (isModEnabled('multicompany') && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2017  $sql .= " LEFT JOIN ".$this->db->prefix()."usergroup_user as ug";
2018  $sql .= " ON ug.fk_user = u.rowid";
2019  $sql .= " WHERE ug.entity = ".$conf->entity;
2020  } else {
2021  $sql .= " WHERE u.entity IN (0, ".$conf->entity.")";
2022  }
2023  }
2024  if (!empty($user->socid)) {
2025  $sql .= " AND u.fk_soc = ".((int) $user->socid);
2026  }
2027  if (is_array($exclude) && $excludeUsers) {
2028  $sql .= " AND u.rowid NOT IN (".$this->db->sanitize($excludeUsers).")";
2029  }
2030  if ($includeUsers) {
2031  $sql .= " AND u.rowid IN (".$this->db->sanitize($includeUsers).")";
2032  }
2033  if (!empty($conf->global->USER_HIDE_INACTIVE_IN_COMBOBOX) || $notdisabled) {
2034  $sql .= " AND u.statut <> 0";
2035  }
2036  if (!empty($morefilter)) {
2037  $sql .= " ".$morefilter;
2038  }
2039 
2040  //Add hook to filter on user (for exemple on usergroup define in custom modules)
2041  $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
2042  if (!empty($reshook)) {
2043  $sql .= $hookmanager->resPrint;
2044  }
2045 
2046  if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) { // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
2047  $sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
2048  } else {
2049  $sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
2050  }
2051 
2052  dol_syslog(get_class($this)."::select_dolusers", LOG_DEBUG);
2053 
2054  $resql = $this->db->query($sql);
2055  if ($resql) {
2056  $num = $this->db->num_rows($resql);
2057  $i = 0;
2058  if ($num) {
2059  // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
2060  $out .= '<select class="flat'.($morecss ? ' '.$morecss : ' minwidth200').'" id="'.$htmlname.'" name="'.$htmlname.($multiple ? '[]' : '').'" '.($multiple ? 'multiple' : '').' '.($disabled ? ' disabled' : '').'>';
2061  if ($show_empty && !$multiple) {
2062  $textforempty = ' ';
2063  if (!empty($conf->use_javascript_ajax)) {
2064  $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
2065  }
2066  if (!is_numeric($show_empty)) {
2067  $textforempty = $show_empty;
2068  }
2069  $out .= '<option class="optiongrey" value="'.($show_empty < 0 ? $show_empty : -1).'"'.((empty($selected) || in_array(-1, $selected)) ? ' selected' : '').'>'.$textforempty.'</option>'."\n";
2070  }
2071  if ($show_every) {
2072  $out .= '<option value="-2"'.((in_array(-2, $selected)) ? ' selected' : '').'>-- '.$langs->trans("Everybody").' --</option>'."\n";
2073  }
2074 
2075  $userstatic = new User($this->db);
2076 
2077  while ($i < $num) {
2078  $obj = $this->db->fetch_object($resql);
2079 
2080  $userstatic->id = $obj->rowid;
2081  $userstatic->lastname = $obj->lastname;
2082  $userstatic->firstname = $obj->firstname;
2083  $userstatic->photo = $obj->photo;
2084  $userstatic->statut = $obj->status;
2085  $userstatic->entity = $obj->entity;
2086  $userstatic->admin = $obj->admin;
2087 
2088  $disableline = '';
2089  if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
2090  $disableline = ($enableonlytext ? $enableonlytext : '1');
2091  }
2092 
2093  $labeltoshow = ''; $labeltoshowhtml = '';
2094 
2095  // $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2096  $fullNameMode = 0;
2097  if (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)) {
2098  $fullNameMode = 1; //Firstname+lastname
2099  }
2100  $labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2101  $labeltoshowhtml .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2102  if (empty($obj->firstname) && empty($obj->lastname)) {
2103  $labeltoshow .= $obj->login;
2104  $labeltoshowhtml .= $obj->login;
2105  }
2106 
2107  // Complete name with a more info string like: ' (info1 - info2 - ...)'
2108  $moreinfo = ''; $moreinfohtml = '';
2109  if (!empty($conf->global->MAIN_SHOW_LOGIN)) {
2110  $moreinfo .= ($moreinfo ? ' - ' : ' (');
2111  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(');
2112  $moreinfo .= $obj->login;
2113  $moreinfohtml .= $obj->login;
2114  }
2115  if ($showstatus >= 0) {
2116  if ($obj->status == 1 && $showstatus == 1) {
2117  $moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Enabled');
2118  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').$langs->trans('Enabled');
2119  }
2120  if ($obj->status == 0 && $showstatus == 1) {
2121  $moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans('Disabled');
2122  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').$langs->trans('Disabled');
2123  }
2124  }
2125  if (isModEnabled('multicompany') && empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE) && $conf->entity == 1 && $user->admin && !$user->entity) {
2126  if (!$obj->entity) {
2127  $moreinfo .= ($moreinfo ? ' - ' : ' (').$langs->trans("AllEntities");
2128  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').$langs->trans("AllEntities");
2129  } else {
2130  if ($obj->entity != $conf->entity) {
2131  $moreinfo .= ($moreinfo ? ' - ' : ' (').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2132  $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2133  }
2134  }
2135  }
2136  $moreinfo .= ($moreinfo ? ')' : '');
2137  $moreinfohtml .= ($moreinfohtml ? ')' : '');
2138  if ($disableline && $disableline != '1') {
2139  // Add text from $enableonlytext parameter
2140  $moreinfo .= ' - '.$disableline;
2141  $moreinfohtml .= ' - '.$disableline;
2142  }
2143  $labeltoshow .= $moreinfo;
2144  $labeltoshowhtml .= $moreinfohtml;
2145 
2146  $out .= '<option value="'.$obj->rowid.'"';
2147  if ($disableline) {
2148  $out .= ' disabled';
2149  }
2150  if ((is_object($selected) && $selected->id == $obj->rowid) || (!is_object($selected) && in_array($obj->rowid, $selected))) {
2151  $out .= ' selected';
2152  }
2153  $out .= ' data-html="';
2154  $outhtml = $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1).' ';
2155  if ($showstatus >= 0 && $obj->status == 0) {
2156  $outhtml .= '<strike class="opacitymediumxxx">';
2157  }
2158  $outhtml .= $labeltoshowhtml;
2159  if ($showstatus >= 0 && $obj->status == 0) {
2160  $outhtml .= '</strike>';
2161  }
2162  $out .= dol_escape_htmltag($outhtml);
2163  $out .= '">';
2164  $out .= $labeltoshow;
2165  $out .= '</option>';
2166 
2167  $outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength).$moreinfo;
2168 
2169  $i++;
2170  }
2171  } else {
2172  $out .= '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'" disabled>';
2173  $out .= '<option value="">'.$langs->trans("None").'</option>';
2174  }
2175  $out .= '</select>';
2176 
2177  if ($num && !$forcecombo) {
2178  // Enhance with select2
2179  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2180  $out .= ajax_combobox($htmlname);
2181  }
2182  } else {
2183  dol_print_error($this->db);
2184  }
2185 
2186  if ($outputmode) {
2187  return $outarray;
2188  }
2189 
2190  return $out;
2191  }
2192 
2193 
2194  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2217  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())
2218  {
2219  // phpcs:enable
2220  global $conf, $user, $langs;
2221 
2222  $userstatic = new User($this->db);
2223  $out = '';
2224 
2225 
2226  $assignedtouser = array();
2227  if (!empty($_SESSION['assignedtouser'])) {
2228  $assignedtouser = json_decode($_SESSION['assignedtouser'], true);
2229  }
2230  $nbassignetouser = count($assignedtouser);
2231 
2232  //if ($nbassignetouser && $action != 'view') $out .= '<br>';
2233  if ($nbassignetouser) {
2234  $out .= '<ul class="attendees">';
2235  }
2236  $i = 0;
2237  $ownerid = 0;
2238  foreach ($assignedtouser as $key => $value) {
2239  if ($value['id'] == $ownerid) {
2240  continue;
2241  }
2242 
2243  $out .= '<li>';
2244  $userstatic->fetch($value['id']);
2245  $out .= $userstatic->getNomUrl(-1);
2246  if ($i == 0) {
2247  $ownerid = $value['id'];
2248  $out .= ' ('.$langs->trans("Owner").')';
2249  }
2250  if ($nbassignetouser > 1 && $action != 'view') {
2251  $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.'">';
2252  }
2253  // Show my availability
2254  if ($showproperties) {
2255  if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
2256  $out .= '<div class="myavailability inline-block">';
2257  $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>';
2258  $out .= '</div>';
2259  }
2260  }
2261  //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
2262  //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
2263 
2264  $out .= '</li>';
2265  $i++;
2266  }
2267  if ($nbassignetouser) {
2268  $out .= '</ul>';
2269  }
2270 
2271  // Method with no ajax
2272  if ($action != 'view') {
2273  $out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
2274  $out .= '<script nonce="'.getNonce().'" type="text/javascript">jQuery(document).ready(function () {';
2275  $out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
2276  $out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
2277  $out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#'.$action.'assignedtouser").attr("disabled", false); }';
2278  $out .= ' else { jQuery("#'.$action.'assignedtouser").attr("disabled", true); }';
2279  $out .= '});';
2280  $out .= '})</script>';
2281  $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
2282  $out .= ' <input type="submit" disabled class="button valignmiddle smallpaddingimp reposition" id="'.$action.'assignedtouser" name="'.$action.'assignedtouser" value="'.dol_escape_htmltag($langs->trans("Add")).'">';
2283  $out .= '<br>';
2284  }
2285 
2286  return $out;
2287  }
2288 
2289 
2290  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2318  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)
2319  {
2320  // phpcs:enable
2321  global $langs, $conf;
2322 
2323  $out = '';
2324 
2325  // check parameters
2326  $price_level = (!empty($price_level) ? $price_level : 0);
2327  if (is_null($ajaxoptions)) {
2328  $ajaxoptions = array();
2329  }
2330 
2331  if (strval($filtertype) === '' && (isModEnabled("product") || isModEnabled("service"))) {
2332  if (isModEnabled("product") && !isModEnabled('service')) {
2333  $filtertype = '0';
2334  } elseif (!isModEnabled('product') && isModEnabled("service")) {
2335  $filtertype = '1';
2336  }
2337  }
2338 
2339  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2340  $placeholder = '';
2341 
2342  if ($selected && empty($selected_input_value)) {
2343  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2344  $producttmpselect = new Product($this->db);
2345  $producttmpselect->fetch($selected);
2346  $selected_input_value = $producttmpselect->ref;
2347  unset($producttmpselect);
2348  }
2349  // handle case where product or service module is disabled + no filter specified
2350  if ($filtertype == '') {
2351  if (!isModEnabled('product')) { // when product module is disabled, show services only
2352  $filtertype = 1;
2353  } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
2354  $filtertype = 0;
2355  }
2356  }
2357  // mode=1 means customers products
2358  $urloption = ($socid > 0 ? 'socid='.$socid.'&' : '').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=1&status='.$status.'&status_purchase='.$status_purchase.'&finished='.$finished.'&hidepriceinlabel='.$hidepriceinlabel.'&warehousestatus='.$warehouseStatus;
2359  $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
2360 
2361  if (isModEnabled('variants') && is_array($selected_combinations)) {
2362  // Code to automatically insert with javascript the select of attributes under the select of product
2363  // when a parent of variant has been selected.
2364  $out .= '
2365  <!-- script to auto show attributes select tags if a variant was selected -->
2366  <script nonce="'.getNonce().'">
2367  // auto show attributes fields
2368  selected = '.json_encode($selected_combinations).';
2369  combvalues = {};
2370 
2371  jQuery(document).ready(function () {
2372 
2373  jQuery("input[name=\'prod_entry_mode\']").change(function () {
2374  if (jQuery(this).val() == \'free\') {
2375  jQuery(\'div#attributes_box\').empty();
2376  }
2377  });
2378 
2379  jQuery("input#'.$htmlname.'").change(function () {
2380 
2381  if (!jQuery(this).val()) {
2382  jQuery(\'div#attributes_box\').empty();
2383  return;
2384  }
2385 
2386  console.log("A change has started. We get variants fields to inject html select");
2387 
2388  jQuery.getJSON("'.DOL_URL_ROOT.'/variants/ajax/getCombinations.php", {
2389  id: jQuery(this).val()
2390  }, function (data) {
2391  jQuery(\'div#attributes_box\').empty();
2392 
2393  jQuery.each(data, function (key, val) {
2394 
2395  combvalues[val.id] = val.values;
2396 
2397  var span = jQuery(document.createElement(\'div\')).css({
2398  \'display\': \'table-row\'
2399  });
2400 
2401  span.append(
2402  jQuery(document.createElement(\'div\')).text(val.label).css({
2403  \'font-weight\': \'bold\',
2404  \'display\': \'table-cell\'
2405  })
2406  );
2407 
2408  var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
2409  \'margin-left\': \'15px\',
2410  \'white-space\': \'pre\'
2411  }).append(
2412  jQuery(document.createElement(\'option\')).val(\'\')
2413  );
2414 
2415  jQuery.each(combvalues[val.id], function (key, val) {
2416  var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
2417 
2418  if (selected[val.fk_product_attribute] == val.id) {
2419  tag.attr(\'selected\', \'selected\');
2420  }
2421 
2422  html.append(tag);
2423  });
2424 
2425  span.append(html);
2426  jQuery(\'div#attributes_box\').append(span);
2427  });
2428  })
2429  });
2430 
2431  '.($selected ? 'jQuery("input#'.$htmlname.'").change();' : '').'
2432  });
2433  </script>
2434  ';
2435  }
2436 
2437  if (empty($hidelabel)) {
2438  $out .= $langs->trans("RefOrLabel").' : ';
2439  } elseif ($hidelabel > 1) {
2440  $placeholder = ' placeholder="'.$langs->trans("RefOrLabel").'"';
2441  if ($hidelabel == 2) {
2442  $out .= img_picto($langs->trans("Search"), 'search');
2443  }
2444  }
2445  $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' : '').' />';
2446  if ($hidelabel == 3) {
2447  $out .= img_picto($langs->trans("Search"), 'search');
2448  }
2449  } else {
2450  $out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus, $status_purchase);
2451  }
2452 
2453  if (empty($nooutput)) {
2454  print $out;
2455  } else {
2456  return $out;
2457  }
2458  }
2459 
2460  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2461 
2477  public function select_bom($selected = '', $htmlname = 'bom_id', $limit = 0, $status = 1, $type = 0, $showempty = '1', $morecss = '', $nooutput = '', $forcecombo = 0, $TProducts = [])
2478  {
2479  // phpcs:enable
2480  global $conf, $user, $langs, $db;
2481 
2482  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2483 
2484  $error = 0;
2485  $out = '';
2486 
2487  if (!$forcecombo) {
2488  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2489  $events = array();
2490  $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
2491  }
2492 
2493  $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
2494 
2495  $sql = 'SELECT b.rowid, b.ref, b.label, b.fk_product';
2496  $sql.= ' FROM '.MAIN_DB_PREFIX.'bom_bom as b';
2497  $sql.= ' WHERE b.entity IN ('.getEntity('bom').')';
2498  if (!empty($status)) $sql.= ' AND status = '. (int) $status;
2499  if (!empty($type)) $sql.= ' AND bomtype = '. (int) $type;
2500  if (!empty($TProducts)) $sql .= ' AND fk_product IN ('.$this->db->sanitize(implode(',', $TProducts)).')';
2501  if (!empty($limit)) $sql.= ' LIMIT '. (int) $limit;
2502  $resql = $db->query($sql);
2503  if ($resql) {
2504  if ($showempty) {
2505  $out .= '<option value="-1"';
2506  if (empty($selected)) $out .= ' selected';
2507  $out .= '>&nbsp;</option>';
2508  }
2509  while ($obj = $db->fetch_object($resql)) {
2510  $product = new Product($db);
2511  $res = $product->fetch($obj->fk_product);
2512  $out .= '<option value="'.$obj->rowid.'"';
2513  if ($obj->rowid == $selected) $out .= 'selected';
2514  $out .= '>'.$obj->ref.' - '.$product->label .' - '. $obj->label.'</option>';
2515  }
2516  } else {
2517  $error++;
2518  dol_print_error($db);
2519  }
2520  if (empty($nooutput)) {
2521  print $out;
2522  } else {
2523  return $out;
2524  }
2525  }
2526 
2527  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2553  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)
2554  {
2555  // phpcs:enable
2556  global $langs, $conf;
2557  global $hookmanager;
2558 
2559  $out = '';
2560  $outarray = array();
2561 
2562  // Units
2563  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2564  $langs->load('other');
2565  }
2566 
2567  $warehouseStatusArray = array();
2568  if (!empty($warehouseStatus)) {
2569  require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
2570  if (preg_match('/warehouseclosed/', $warehouseStatus)) {
2571  $warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
2572  }
2573  if (preg_match('/warehouseopen/', $warehouseStatus)) {
2574  $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
2575  }
2576  if (preg_match('/warehouseinternal/', $warehouseStatus)) {
2577  $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
2578  }
2579  }
2580 
2581  $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";
2582  if (count($warehouseStatusArray)) {
2583  $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
2584  } else {
2585  $selectFieldsGrouped = ", ".$this->db->ifsql("p.stock IS NULL", 0, "p.stock")." AS stock";
2586  }
2587 
2588  $sql = "SELECT ";
2589  $sql .= $selectFields.$selectFieldsGrouped;
2590 
2591  if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2592  //Product category
2593  $sql .= ", (SELECT ".$this->db->prefix()."categorie_product.fk_categorie
2594  FROM ".$this->db->prefix()."categorie_product
2595  WHERE ".$this->db->prefix()."categorie_product.fk_product=p.rowid
2596  LIMIT 1
2597  ) AS categorie_product_id ";
2598  }
2599 
2600  //Price by customer
2601  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2602  $sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
2603  $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';
2604  $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custdefault_vat_code, custref";
2605  }
2606  // Units
2607  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2608  $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";
2609  $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';
2610  }
2611 
2612  // Multilang : we add translation
2613  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2614  $sql .= ", pl.label as label_translated";
2615  $sql .= ", pl.description as description_translated";
2616  $selectFields .= ", label_translated";
2617  $selectFields .= ", description_translated";
2618  }
2619  // Price by quantity
2620  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2621  $sql .= ", (SELECT pp.rowid FROM ".$this->db->prefix()."product_price as pp WHERE pp.fk_product = p.rowid";
2622  if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2623  $sql .= " AND price_level = ".((int) $price_level);
2624  }
2625  $sql .= " ORDER BY date_price";
2626  $sql .= " DESC LIMIT 1) as price_rowid";
2627  $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
2628  if ($price_level >= 1 && !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES)) {
2629  $sql .= " AND price_level = ".((int) $price_level);
2630  }
2631  $sql .= " ORDER BY date_price";
2632  $sql .= " DESC LIMIT 1) as price_by_qty";
2633  $selectFields .= ", price_rowid, price_by_qty";
2634  }
2635  $sql .= " FROM ".$this->db->prefix()."product as p";
2636  if (count($warehouseStatusArray)) {
2637  $sql .= " LEFT JOIN ".$this->db->prefix()."product_stock as ps on ps.fk_product = p.rowid";
2638  $sql .= " LEFT JOIN ".$this->db->prefix()."entrepot as e on ps.fk_entrepot = e.rowid AND e.entity IN (".getEntity('stock').")";
2639  $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.
2640  }
2641 
2642  // include search in supplier ref
2643  if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2644  $sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
2645  }
2646 
2647  //Price by customer
2648  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2649  $sql .= " LEFT JOIN ".$this->db->prefix()."product_customer_price as pcp ON pcp.fk_soc=".((int) $socid)." AND pcp.fk_product=p.rowid";
2650  }
2651  // Units
2652  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2653  $sql .= " LEFT JOIN ".$this->db->prefix()."c_units u ON u.rowid = p.fk_unit";
2654  }
2655  // Multilang : we add translation
2656  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2657  $sql .= " LEFT JOIN ".$this->db->prefix()."product_lang as pl ON pl.fk_product = p.rowid ";
2658  if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && !empty($socid)) {
2659  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
2660  $soc = new Societe($this->db);
2661  $result = $soc->fetch($socid);
2662  if ($result > 0 && !empty($soc->default_lang)) {
2663  $sql .= " AND pl.lang = '".$this->db->escape($soc->default_lang)."'";
2664  } else {
2665  $sql .= " AND pl.lang = '".$this->db->escape($langs->getDefaultLang())."'";
2666  }
2667  } else {
2668  $sql .= " AND pl.lang = '".$this->db->escape($langs->getDefaultLang())."'";
2669  }
2670  }
2671 
2672  if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2673  $sql .= " LEFT JOIN ".$this->db->prefix()."product_attribute_combination pac ON pac.fk_product_child = p.rowid";
2674  }
2675 
2676  $sql .= ' WHERE p.entity IN ('.getEntity('product').')';
2677 
2678  if (!empty($conf->global->PRODUIT_ATTRIBUTES_HIDECHILD)) {
2679  $sql .= " AND pac.rowid IS NULL";
2680  }
2681 
2682  if ($finished == 0) {
2683  $sql .= " AND p.finished = ".((int) $finished);
2684  } elseif ($finished == 1) {
2685  $sql .= " AND p.finished = ".((int) $finished);
2686  if ($status >= 0) {
2687  $sql .= " AND p.tosell = ".((int) $status);
2688  }
2689  } elseif ($status >= 0) {
2690  $sql .= " AND p.tosell = ".((int) $status);
2691  }
2692  if ($status_purchase >= 0) {
2693  $sql .= " AND p.tobuy = ".((int) $status_purchase);
2694  }
2695  // Filter by product type
2696  if (strval($filtertype) != '') {
2697  $sql .= " AND p.fk_product_type = ".((int) $filtertype);
2698  } elseif (!isModEnabled('product')) { // when product module is disabled, show services only
2699  $sql .= " AND p.fk_product_type = 1";
2700  } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
2701  $sql .= " AND p.fk_product_type = 0";
2702  }
2703  // Add where from hooks
2704  $parameters = array();
2705  $reshook = $hookmanager->executeHooks('selectProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
2706  $sql .= $hookmanager->resPrint;
2707  // Add criteria on ref/label
2708  if ($filterkey != '') {
2709  $sql .= ' AND (';
2710  $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
2711  // For natural search
2712  $scrit = explode(' ', $filterkey);
2713  $i = 0;
2714  if (count($scrit) > 1) {
2715  $sql .= "(";
2716  }
2717  foreach ($scrit as $crit) {
2718  if ($i > 0) {
2719  $sql .= " AND ";
2720  }
2721  $sql .= "(p.ref LIKE '".$this->db->escape($prefix.$crit)."%' OR p.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2722  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2723  $sql .= " OR pl.label LIKE '".$this->db->escape($prefix.$crit)."%'";
2724  }
2725  if (!empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
2726  $sql .= " OR pcp.ref_customer LIKE '".$this->db->escape($prefix.$crit)."%'";
2727  }
2728  if (!empty($conf->global->PRODUCT_AJAX_SEARCH_ON_DESCRIPTION)) {
2729  $sql .= " OR p.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2730  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2731  $sql .= " OR pl.description LIKE '".$this->db->escape($prefix.$crit)."%'";
2732  }
2733  }
2734  if (!empty($conf->global->MAIN_SEARCH_PRODUCT_BY_FOURN_REF)) {
2735  $sql .= " OR pfp.ref_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
2736  }
2737  $sql .= ")";
2738  $i++;
2739  }
2740  if (count($scrit) > 1) {
2741  $sql .= ")";
2742  }
2743  if (isModEnabled('barcode')) {
2744  $sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
2745  }
2746  $sql .= ')';
2747  }
2748  if (count($warehouseStatusArray)) {
2749  $sql .= " GROUP BY ".$selectFields;
2750  }
2751 
2752  //Sort by category
2753  if (!empty($conf->global->PRODUCT_SORT_BY_CATEGORY)) {
2754  $sql .= " ORDER BY categorie_product_id ";
2755  //ASC OR DESC order
2756  ($conf->global->PRODUCT_SORT_BY_CATEGORY == 1) ? $sql .= "ASC" : $sql .= "DESC";
2757  } else {
2758  $sql .= $this->db->order("p.ref");
2759  }
2760 
2761  $sql .= $this->db->plimit($limit, 0);
2762 
2763  // Build output string
2764  dol_syslog(get_class($this)."::select_produits_list search products", LOG_DEBUG);
2765  $result = $this->db->query($sql);
2766  if ($result) {
2767  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2768  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
2769  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
2770 
2771  $num = $this->db->num_rows($result);
2772 
2773  $events = null;
2774 
2775  if (!$forcecombo) {
2776  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
2777  $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
2778  }
2779 
2780  $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
2781 
2782  $textifempty = '';
2783  // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2784  //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2785  if (!empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
2786  if ($showempty && !is_numeric($showempty)) {
2787  $textifempty = $langs->trans($showempty);
2788  } else {
2789  $textifempty .= $langs->trans("All");
2790  }
2791  } else {
2792  if ($showempty && !is_numeric($showempty)) {
2793  $textifempty = $langs->trans($showempty);
2794  }
2795  }
2796  if ($showempty) {
2797  $out .= '<option value="-1" selected>'.($textifempty ? $textifempty : '&nbsp;').'</option>';
2798  }
2799 
2800  $i = 0;
2801  while ($num && $i < $num) {
2802  $opt = '';
2803  $optJson = array();
2804  $objp = $this->db->fetch_object($result);
2805 
2806  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
2807  $sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
2808  $sql .= " FROM ".$this->db->prefix()."product_price_by_qty";
2809  $sql .= " WHERE fk_product_price = ".((int) $objp->price_rowid);
2810  $sql .= " ORDER BY quantity ASC";
2811 
2812  dol_syslog(get_class($this)."::select_produits_list search prices by qty", LOG_DEBUG);
2813  $result2 = $this->db->query($sql);
2814  if ($result2) {
2815  $nb_prices = $this->db->num_rows($result2);
2816  $j = 0;
2817  while ($nb_prices && $j < $nb_prices) {
2818  $objp2 = $this->db->fetch_object($result2);
2819 
2820  $objp->price_by_qty_rowid = $objp2->rowid;
2821  $objp->price_by_qty_price_base_type = $objp2->price_base_type;
2822  $objp->price_by_qty_quantity = $objp2->quantity;
2823  $objp->price_by_qty_unitprice = $objp2->unitprice;
2824  $objp->price_by_qty_remise_percent = $objp2->remise_percent;
2825  // For backward compatibility
2826  $objp->quantity = $objp2->quantity;
2827  $objp->price = $objp2->price;
2828  $objp->unitprice = $objp2->unitprice;
2829  $objp->remise_percent = $objp2->remise_percent;
2830 
2831  //$objp->tva_tx is not overwritten by $objp2 value
2832  //$objp->default_vat_code is not overwritten by $objp2 value
2833 
2834  $this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
2835 
2836  $j++;
2837 
2838  // Add new entry
2839  // "key" value of json key array is used by jQuery automatically as selected value
2840  // "label" value of json key array is used by jQuery automatically as text for combo box
2841  $out .= $opt;
2842  array_push($outarray, $optJson);
2843  }
2844  }
2845  } else {
2846  if (isModEnabled('dynamicprices') && !empty($objp->fk_price_expression)) {
2847  $price_product = new Product($this->db);
2848  $price_product->fetch($objp->rowid, '', '', 1);
2849 
2850  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
2851  $priceparser = new PriceParser($this->db);
2852  $price_result = $priceparser->parseProduct($price_product);
2853  if ($price_result >= 0) {
2854  $objp->price = $price_result;
2855  $objp->unitprice = $price_result;
2856  //Calculate the VAT
2857  $objp->price_ttc = price2num($objp->price) * (1 + ($objp->tva_tx / 100));
2858  $objp->price_ttc = price2num($objp->price_ttc, 'MU');
2859  }
2860  }
2861 
2862  $this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
2863  // Add new entry
2864  // "key" value of json key array is used by jQuery automatically as selected value
2865  // "label" value of json key array is used by jQuery automatically as text for combo box
2866  $out .= $opt;
2867  array_push($outarray, $optJson);
2868  }
2869 
2870  $i++;
2871  }
2872 
2873  $out .= '</select>';
2874 
2875  $this->db->free($result);
2876 
2877  if (empty($outputmode)) {
2878  return $out;
2879  }
2880 
2881  return $outarray;
2882  } else {
2883  dol_print_error($this->db);
2884  }
2885 
2886  return '';
2887  }
2888 
2904  protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
2905  {
2906  global $langs, $conf, $user;
2907 
2908  $outkey = '';
2909  $outval = '';
2910  $outref = '';
2911  $outlabel = '';
2912  $outlabel_translated = '';
2913  $outdesc = '';
2914  $outdesc_translated = '';
2915  $outbarcode = '';
2916  $outorigin = '';
2917  $outtype = '';
2918  $outprice_ht = '';
2919  $outprice_ttc = '';
2920  $outpricebasetype = '';
2921  $outtva_tx = '';
2922  $outdefault_vat_code = '';
2923  $outqty = 1;
2924  $outdiscount = 0;
2925 
2926  $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
2927 
2928  $label = $objp->label;
2929  if (!empty($objp->label_translated)) {
2930  $label = $objp->label_translated;
2931  }
2932  if (!empty($filterkey) && $filterkey != '') {
2933  $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
2934  }
2935 
2936  $outkey = $objp->rowid;
2937  $outref = $objp->ref;
2938  $outrefcust = empty($objp->custref) ? '' : $objp->custref;
2939  $outlabel = $objp->label;
2940  $outdesc = $objp->description;
2941  if (getDolGlobalInt('MAIN_MULTILANGS')) {
2942  $outlabel_translated = $objp->label_translated;
2943  $outdesc_translated = $objp->description_translated;
2944  }
2945  $outbarcode = $objp->barcode;
2946  $outorigin = $objp->fk_country;
2947  $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
2948 
2949  $outtype = $objp->fk_product_type;
2950  $outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
2951  $outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
2952 
2953  if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
2954  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
2955  }
2956 
2957  // Units
2958  $outvalUnits = '';
2959  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
2960  if (!empty($objp->unit_short)) {
2961  $outvalUnits .= ' - '.$objp->unit_short;
2962  }
2963  }
2964  if (!empty($conf->global->PRODUCT_SHOW_DIMENSIONS_IN_COMBO)) {
2965  if (!empty($objp->weight) && $objp->weight_units !== null) {
2966  $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
2967  $outvalUnits .= ' - '.$unitToShow;
2968  }
2969  if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
2970  $unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
2971  $outvalUnits .= ' - '.$unitToShow;
2972  }
2973  if (!empty($objp->surface) && $objp->surface_units !== null) {
2974  $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
2975  $outvalUnits .= ' - '.$unitToShow;
2976  }
2977  if (!empty($objp->volume) && $objp->volume_units !== null) {
2978  $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
2979  $outvalUnits .= ' - '.$unitToShow;
2980  }
2981  }
2982  if ($outdurationvalue && $outdurationunit) {
2983  $da = array(
2984  'h' => $langs->trans('Hour'),
2985  'd' => $langs->trans('Day'),
2986  'w' => $langs->trans('Week'),
2987  'm' => $langs->trans('Month'),
2988  'y' => $langs->trans('Year')
2989  );
2990  if (isset($da[$outdurationunit])) {
2991  $outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
2992  }
2993  }
2994 
2995  $opt = '<option value="'.$objp->rowid.'"';
2996  $opt .= ($objp->rowid == $selected) ? ' selected' : '';
2997  if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
2998  $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.'"';
2999  }
3000  if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3001  if (!empty($user->rights->stock->lire)) {
3002  if ($objp->stock > 0) {
3003  $opt .= ' class="product_line_stock_ok"';
3004  } elseif ($objp->stock <= 0) {
3005  $opt .= ' class="product_line_stock_too_low"';
3006  }
3007  }
3008  }
3009  if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) {
3010  $opt .= ' data-labeltrans="'.$outlabel_translated.'"';
3011  $opt .= ' data-desctrans="'.dol_escape_htmltag($outdesc_translated).'"';
3012  }
3013  $opt .= '>';
3014  $opt .= $objp->ref;
3015  if (!empty($objp->custref)) {
3016  $opt.= ' (' . $objp->custref . ')';
3017  }
3018  if ($outbarcode) {
3019  $opt .= ' ('.$outbarcode.')';
3020  }
3021  $opt .= ' - '.dol_trunc($label, $maxlengtharticle);
3022  if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
3023  $opt .= ' ('.getCountry($outorigin, 1).')';
3024  }
3025 
3026  $objRef = $objp->ref;
3027  if (!empty($objp->custref)) {
3028  $objRef .= ' (' . $objp->custref . ')';
3029  }
3030  if (!empty($filterkey) && $filterkey != '') {
3031  $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
3032  }
3033  $outval .= $objRef;
3034  if ($outbarcode) {
3035  $outval .= ' ('.$outbarcode.')';
3036  }
3037  $outval .= ' - '.dol_trunc($label, $maxlengtharticle);
3038  if ($outorigin && !empty($conf->global->PRODUCT_SHOW_ORIGIN_IN_COMBO)) {
3039  $outval .= ' ('.getCountry($outorigin, 1).')';
3040  }
3041 
3042  // Units
3043  $opt .= $outvalUnits;
3044  $outval .= $outvalUnits;
3045 
3046  $found = 0;
3047 
3048  // Multiprice
3049  // If we need a particular price level (from 1 to n)
3050  if (empty($hidepriceinlabel) && $price_level >= 1 && (!empty($conf->global->PRODUIT_MULTIPRICES) || !empty($conf->global->PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES))) {
3051  $sql = "SELECT price, price_ttc, price_base_type, tva_tx, default_vat_code";
3052  $sql .= " FROM ".$this->db->prefix()."product_price";
3053  $sql .= " WHERE fk_product = ".((int) $objp->rowid);
3054  $sql .= " AND entity IN (".getEntity('productprice').")";
3055  $sql .= " AND price_level = ".((int) $price_level);
3056  $sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
3057  $sql .= " LIMIT 1";
3058 
3059  dol_syslog(get_class($this).'::constructProductListOption search price for product '.$objp->rowid.' AND level '.$price_level, LOG_DEBUG);
3060  $result2 = $this->db->query($sql);
3061  if ($result2) {
3062  $objp2 = $this->db->fetch_object($result2);
3063  if ($objp2) {
3064  $found = 1;
3065  if ($objp2->price_base_type == 'HT') {
3066  $opt .= ' - '.price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
3067  $outval .= ' - '.price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
3068  } else {
3069  $opt .= ' - '.price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
3070  $outval .= ' - '.price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
3071  }
3072  $outprice_ht = price($objp2->price);
3073  $outprice_ttc = price($objp2->price_ttc);
3074  $outpricebasetype = $objp2->price_base_type;
3075  if (!empty($conf->global->PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL)) { // using this option is a bug. kept for backward compatibility
3076  $outtva_tx = $objp2->tva_tx; // We use the vat rate on line of multiprice
3077  $outdefault_vat_code = $objp2->default_vat_code; // We use the vat code on line of multiprice
3078  } else {
3079  $outtva_tx = $objp->tva_tx; // We use the vat rate of product, not the one on line of multiprice
3080  $outdefault_vat_code = $objp->default_vat_code; // We use the vat code or product, not the one on line of multiprice
3081  }
3082  }
3083  } else {
3084  dol_print_error($this->db);
3085  }
3086  }
3087 
3088  // Price by quantity
3089  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))) {
3090  $found = 1;
3091  $outqty = $objp->quantity;
3092  $outdiscount = $objp->remise_percent;
3093  if ($objp->quantity == 1) {
3094  $opt .= ' - '.price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/";
3095  $outval .= ' - '.price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/";
3096  $opt .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3097  $outval .= $langs->transnoentities("Unit");
3098  } else {
3099  $opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3100  $outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency)."/".$objp->quantity;
3101  $opt .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3102  $outval .= $langs->transnoentities("Units");
3103  }
3104 
3105  $outprice_ht = price($objp->unitprice);
3106  $outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
3107  $outpricebasetype = $objp->price_base_type;
3108  $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
3109  $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
3110  }
3111  if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
3112  $opt .= " (".price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency)."/".$langs->trans("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
3113  $outval .= " (".price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency)."/".$langs->transnoentities("Unit").")"; // Do not use strtolower because it breaks utf8 encoding
3114  }
3115  if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
3116  $opt .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
3117  $outval .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
3118  }
3119 
3120  // Price by customer
3121  if (empty($hidepriceinlabel) && !empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
3122  if (!empty($objp->idprodcustprice)) {
3123  $found = 1;
3124 
3125  if ($objp->custprice_base_type == 'HT') {
3126  $opt .= ' - '.price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
3127  $outval .= ' - '.price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
3128  } else {
3129  $opt .= ' - '.price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
3130  $outval .= ' - '.price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
3131  }
3132 
3133  $outprice_ht = price($objp->custprice);
3134  $outprice_ttc = price($objp->custprice_ttc);
3135  $outpricebasetype = $objp->custprice_base_type;
3136  $outtva_tx = $objp->custtva_tx;
3137  $outdefault_vat_code = $objp->custdefault_vat_code;
3138  }
3139  }
3140 
3141  // If level no defined or multiprice not found, we used the default price
3142  if (empty($hidepriceinlabel) && !$found) {
3143  if ($objp->price_base_type == 'HT') {
3144  $opt .= ' - '.price($objp->price, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("HT");
3145  $outval .= ' - '.price($objp->price, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("HT");
3146  } else {
3147  $opt .= ' - '.price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency).' '.$langs->trans("TTC");
3148  $outval .= ' - '.price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency).' '.$langs->transnoentities("TTC");
3149  }
3150  $outprice_ht = price($objp->price);
3151  $outprice_ttc = price($objp->price_ttc);
3152  $outpricebasetype = $objp->price_base_type;
3153  $outtva_tx = $objp->tva_tx;
3154  $outdefault_vat_code = $objp->default_vat_code;
3155  }
3156 
3157  if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3158  if (!empty($user->rights->stock->lire)) {
3159  $opt .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
3160 
3161  if ($objp->stock > 0) {
3162  $outval .= ' - <span class="product_line_stock_ok">';
3163  } elseif ($objp->stock <= 0) {
3164  $outval .= ' - <span class="product_line_stock_too_low">';
3165  }
3166  $outval .= $langs->transnoentities("Stock").': '.price(price2num($objp->stock, 'MS'));
3167  $outval .= '</span>';
3168  if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) { // Warning, this option may slow down combo list generation
3169  $langs->load("stocks");
3170 
3171  $tmpproduct = new Product($this->db);
3172  $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3173  $tmpproduct->load_virtual_stock();
3174  $virtualstock = $tmpproduct->stock_theorique;
3175 
3176  $opt .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
3177 
3178  $outval .= ' - '.$langs->transnoentities("VirtualStock").':';
3179  if ($virtualstock > 0) {
3180  $outval .= '<span class="product_line_stock_ok">';
3181  } elseif ($virtualstock <= 0) {
3182  $outval .= '<span class="product_line_stock_too_low">';
3183  }
3184  $outval .= $virtualstock;
3185  $outval .= '</span>';
3186 
3187  unset($tmpproduct);
3188  }
3189  }
3190  }
3191 
3192  $opt .= "</option>\n";
3193  $optJson = array(
3194  'key'=>$outkey,
3195  'value'=>$outref,
3196  'label'=>$outval,
3197  'label2'=>$outlabel,
3198  'desc'=>$outdesc,
3199  'type'=>$outtype,
3200  'price_ht'=>price2num($outprice_ht),
3201  'price_ttc'=>price2num($outprice_ttc),
3202  'price_ht_locale'=>price(price2num($outprice_ht)),
3203  'price_ttc_locale'=>price(price2num($outprice_ttc)),
3204  'pricebasetype'=>$outpricebasetype,
3205  'tva_tx'=>$outtva_tx,
3206  'default_vat_code'=>$outdefault_vat_code,
3207  'qty'=>$outqty,
3208  'discount'=>$outdiscount,
3209  'duration_value'=>$outdurationvalue,
3210  'duration_unit'=>$outdurationunit,
3211  'pbq'=>$outpbq,
3212  'labeltrans'=>$outlabel_translated,
3213  'desctrans'=>$outdesc_translated,
3214  'ref_customer'=>$outrefcust
3215  );
3216  }
3217 
3218  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3234  public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $filtre = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '')
3235  {
3236  // phpcs:enable
3237  global $langs, $conf;
3238  global $price_level, $status, $finished;
3239 
3240  if (!isset($status)) {
3241  $status = 1;
3242  }
3243 
3244  $selected_input_value = '';
3245  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->PRODUIT_USE_SEARCH_TO_SELECT)) {
3246  if ($selected > 0) {
3247  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
3248  $producttmpselect = new Product($this->db);
3249  $producttmpselect->fetch($selected);
3250  $selected_input_value = $producttmpselect->ref;
3251  unset($producttmpselect);
3252  }
3253 
3254  // mode=2 means suppliers products
3255  $urloption = ($socid > 0 ? 'socid='.$socid.'&' : '').'htmlname='.$htmlname.'&outjson=1&price_level='.$price_level.'&type='.$filtertype.'&mode=2&status='.$status.'&finished='.$finished.'&alsoproductwithnosupplierprice='.$alsoproductwithnosupplierprice;
3256  print ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/product/ajax/products.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 0, $ajaxoptions);
3257 
3258  print ($hidelabel ? '' : $langs->trans("RefOrLabel").' : ').'<input type="text" class="minwidth300" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.($placeholder ? ' placeholder="'.$placeholder.'"' : '').'>';
3259  } else {
3260  print $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $filtre, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, 0, $placeholder);
3261  }
3262  }
3263 
3264  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3283  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 = '')
3284  {
3285  // phpcs:enable
3286  global $langs, $conf, $user;
3287  global $hookmanager;
3288 
3289  $out = '';
3290  $outarray = array();
3291 
3292  $maxlengtharticle = (empty($conf->global->PRODUCT_MAX_LENGTH_COMBO) ? 48 : $conf->global->PRODUCT_MAX_LENGTH_COMBO);
3293 
3294  $langs->load('stocks');
3295  // Units
3296  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3297  $langs->load('other');
3298  }
3299 
3300  $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, p.fk_product_type, p.stock, p.tva_tx as tva_tx_sale, p.default_vat_code as default_vat_code_sale,";
3301  $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,";
3302  $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.default_vat_code, pfp.fk_soc, s.nom as name,";
3303  $sql .= " pfp.supplier_reputation";
3304  // if we use supplier description of the products
3305  if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3306  $sql .= ", pfp.desc_fourn as description";
3307  } else {
3308  $sql .= ", p.description";
3309  }
3310  // Units
3311  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3312  $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";
3313  }
3314  if (isModEnabled('barcode')) {
3315  $sql .= ", pfp.barcode";
3316  }
3317  $sql .= " FROM ".$this->db->prefix()."product as p";
3318  $sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (".getEntity('product').") )";
3319  if ($socid > 0) {
3320  $sql .= " AND pfp.fk_soc = ".((int) $socid);
3321  }
3322  $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON pfp.fk_soc = s.rowid";
3323  // Units
3324  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3325  $sql .= " LEFT JOIN ".$this->db->prefix()."c_units u ON u.rowid = p.fk_unit";
3326  }
3327  $sql .= " WHERE p.entity IN (".getEntity('product').")";
3328  if ($statut != -1) {
3329  $sql .= " AND p.tobuy = ".((int) $statut);
3330  }
3331  if (strval($filtertype) != '') {
3332  $sql .= " AND p.fk_product_type = ".((int) $filtertype);
3333  }
3334  if (!empty($filtre)) {
3335  $sql .= " ".$filtre;
3336  }
3337  // Add where from hooks
3338  $parameters = array();
3339  $reshook = $hookmanager->executeHooks('selectSuppliersProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3340  $sql .= $hookmanager->resPrint;
3341  // Add criteria on ref/label
3342  if ($filterkey != '') {
3343  $sql .= ' AND (';
3344  $prefix = empty($conf->global->PRODUCT_DONOTSEARCH_ANYWHERE) ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3345  // For natural search
3346  $scrit = explode(' ', $filterkey);
3347  $i = 0;
3348  if (count($scrit) > 1) {
3349  $sql .= "(";
3350  }
3351  foreach ($scrit as $crit) {
3352  if ($i > 0) {
3353  $sql .= " AND ";
3354  }
3355  $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)."%'";
3356  if (!empty($conf->global->PRODUIT_FOURN_TEXTS)) {
3357  $sql .= " OR pfp.desc_fourn LIKE '".$this->db->escape($prefix.$crit)."%'";
3358  }
3359  $sql .= ")";
3360  $i++;
3361  }
3362  if (count($scrit) > 1) {
3363  $sql .= ")";
3364  }
3365  if (isModEnabled('barcode')) {
3366  $sql .= " OR p.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3367  $sql .= " OR pfp.barcode LIKE '".$this->db->escape($prefix.$filterkey)."%'";
3368  }
3369  $sql .= ')';
3370  }
3371  $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
3372  $sql .= $this->db->plimit($limit, 0);
3373 
3374  // Build output string
3375 
3376  dol_syslog(get_class($this)."::select_produits_fournisseurs_list", LOG_DEBUG);
3377  $result = $this->db->query($sql);
3378  if ($result) {
3379  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3380  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
3381 
3382  $num = $this->db->num_rows($result);
3383 
3384  //$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">'; // remove select to have id same with combo and ajax
3385  $out .= '<select class="flat '.($morecss ? ' '.$morecss : '').'" id="'.$htmlname.'" name="'.$htmlname.'">';
3386  if (!$selected) {
3387  $out .= '<option value="-1" selected>'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3388  } else {
3389  $out .= '<option value="-1">'.($placeholder ? $placeholder : '&nbsp;').'</option>';
3390  }
3391 
3392  $i = 0;
3393  while ($i < $num) {
3394  $objp = $this->db->fetch_object($result);
3395 
3396  if (is_null($objp->idprodfournprice)) {
3397  // There is no supplier price found, we will use the vat rate for sale
3398  $objp->tva_tx = $objp->tva_tx_sale;
3399  $objp->default_vat_code = $objp->default_vat_code_sale;
3400  }
3401 
3402  $outkey = $objp->idprodfournprice; // id in table of price
3403  if (!$outkey && $alsoproductwithnosupplierprice) {
3404  $outkey = 'idprod_'.$objp->rowid; // id of product
3405  }
3406 
3407  $outref = $objp->ref;
3408  $outbarcode = $objp->barcode;
3409  $outqty = 1;
3410  $outdiscount = 0;
3411  $outtype = $objp->fk_product_type;
3412  $outdurationvalue = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
3413  $outdurationunit = $outtype == Product::TYPE_SERVICE ?substr($objp->duration, -1) : '';
3414 
3415  // Units
3416  $outvalUnits = '';
3417  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3418  if (!empty($objp->unit_short)) {
3419  $outvalUnits .= ' - '.$objp->unit_short;
3420  }
3421  if (!empty($objp->weight) && $objp->weight_units !== null) {
3422  $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
3423  $outvalUnits .= ' - '.$unitToShow;
3424  }
3425  if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
3426  $unitToShow = $objp->length.' x '.$objp->width.' x '.$objp->height.' '.measuringUnitString(0, 'size', $objp->length_units);
3427  $outvalUnits .= ' - '.$unitToShow;
3428  }
3429  if (!empty($objp->surface) && $objp->surface_units !== null) {
3430  $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
3431  $outvalUnits .= ' - '.$unitToShow;
3432  }
3433  if (!empty($objp->volume) && $objp->volume_units !== null) {
3434  $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
3435  $outvalUnits .= ' - '.$unitToShow;
3436  }
3437  if ($outdurationvalue && $outdurationunit) {
3438  $da = array(
3439  'h' => $langs->trans('Hour'),
3440  'd' => $langs->trans('Day'),
3441  'w' => $langs->trans('Week'),
3442  'm' => $langs->trans('Month'),
3443  'y' => $langs->trans('Year')
3444  );
3445  if (isset($da[$outdurationunit])) {
3446  $outvalUnits .= ' - '.$outdurationvalue.' '.$langs->transnoentities($da[$outdurationunit].($outdurationvalue > 1 ? 's' : ''));
3447  }
3448  }
3449  }
3450 
3451  $objRef = $objp->ref;
3452  if ($filterkey && $filterkey != '') {
3453  $objRef = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRef, 1);
3454  }
3455  $objRefFourn = $objp->ref_fourn;
3456  if ($filterkey && $filterkey != '') {
3457  $objRefFourn = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $objRefFourn, 1);
3458  }
3459  $label = $objp->label;
3460  if ($filterkey && $filterkey != '') {
3461  $label = preg_replace('/('.preg_quote($filterkey, '/').')/i', '<strong>$1</strong>', $label, 1);
3462  }
3463 
3464  $optlabel = $objp->ref;
3465  if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3466  $optlabel .= ' <span class="opacitymedium">('.$objp->ref_fourn.')</span>';
3467  }
3468  if (isModEnabled('barcode') && !empty($objp->barcode)) {
3469  $optlabel .= ' ('.$outbarcode.')';
3470  }
3471  $optlabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3472 
3473  $outvallabel = $objRef;
3474  if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
3475  $outvallabel .= ' ('.$objRefFourn.')';
3476  }
3477  if (isModEnabled('barcode') && !empty($objp->barcode)) {
3478  $outvallabel .= ' ('.$outbarcode.')';
3479  }
3480  $outvallabel .= ' - '.dol_trunc($label, $maxlengtharticle);
3481 
3482  // Units
3483  $optlabel .= $outvalUnits;
3484  $outvallabel .= $outvalUnits;
3485 
3486  if (!empty($objp->idprodfournprice)) {
3487  $outqty = $objp->quantity;
3488  $outdiscount = $objp->remise_percent;
3489  if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
3490  $prod_supplier = new ProductFournisseur($this->db);
3491  $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3492  $prod_supplier->id = $objp->fk_product;
3493  $prod_supplier->fourn_qty = $objp->quantity;
3494  $prod_supplier->fourn_tva_tx = $objp->tva_tx;
3495  $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3496 
3497  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3498  $priceparser = new PriceParser($this->db);
3499  $price_result = $priceparser->parseProductSupplier($prod_supplier);
3500  if ($price_result >= 0) {
3501  $objp->fprice = $price_result;
3502  if ($objp->quantity >= 1) {
3503  $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
3504  }
3505  }
3506  }
3507  if ($objp->quantity == 1) {
3508  $optlabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3509  $outvallabel .= ' - '.price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency)."/";
3510  $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
3511  $outvallabel .= $langs->transnoentities("Unit");
3512  } else {
3513  $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;
3514  $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;
3515  $optlabel .= ' '.$langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
3516  $outvallabel .= ' '.$langs->transnoentities("Units");
3517  }
3518 
3519  if ($objp->quantity > 1) {
3520  $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
3521  $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
3522  }
3523  if ($objp->remise_percent >= 1) {
3524  $optlabel .= " - ".$langs->trans("Discount")." : ".vatrate($objp->remise_percent).' %';
3525  $outvallabel .= " - ".$langs->transnoentities("Discount")." : ".vatrate($objp->remise_percent).' %';
3526  }
3527  if ($objp->duration) {
3528  $optlabel .= " - ".$objp->duration;
3529  $outvallabel .= " - ".$objp->duration;
3530  }
3531  if (!$socid) {
3532  $optlabel .= " - ".dol_trunc($objp->name, 8);
3533  $outvallabel .= " - ".dol_trunc($objp->name, 8);
3534  }
3535  if ($objp->supplier_reputation) {
3536  //TODO dictionary
3537  $reputations = array(''=>$langs->trans('Standard'), 'FAVORITE'=>$langs->trans('Favorite'), 'NOTTHGOOD'=>$langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER'=>$langs->trans('DoNotOrderThisProductToThisSupplier'));
3538 
3539  $optlabel .= " - ".$reputations[$objp->supplier_reputation];
3540  $outvallabel .= " - ".$reputations[$objp->supplier_reputation];
3541  }
3542  } else {
3543  if (empty($alsoproductwithnosupplierprice)) { // No supplier price defined for couple product/supplier
3544  $optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3545  $outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3546  } else // No supplier price defined for product, even on other suppliers
3547  {
3548  $optlabel .= " - <span class='opacitymedium'>".$langs->trans("NoPriceDefinedForThisSupplier").'</span>';
3549  $outvallabel .= ' - '.$langs->transnoentities("NoPriceDefinedForThisSupplier");
3550  }
3551  }
3552 
3553  if (isModEnabled('stock') && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || !empty($conf->global->STOCK_SUPPORTS_SERVICES))) {
3554  $novirtualstock = ($showstockinlist == 2);
3555 
3556  if (!empty($user->rights->stock->lire)) {
3557  $outvallabel .= ' - '.$langs->trans("Stock").': '.price(price2num($objp->stock, 'MS'));
3558 
3559  if ($objp->stock > 0) {
3560  $optlabel .= ' - <span class="product_line_stock_ok">';
3561  } elseif ($objp->stock <= 0) {
3562  $optlabel .= ' - <span class="product_line_stock_too_low">';
3563  }
3564  $optlabel .= $langs->transnoentities("Stock").':'.price(price2num($objp->stock, 'MS'));
3565  $optlabel .= '</span>';
3566  if (empty($novirtualstock) && !empty($conf->global->STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO)) { // Warning, this option may slow down combo list generation
3567  $langs->load("stocks");
3568 
3569  $tmpproduct = new Product($this->db);
3570  $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
3571  $tmpproduct->load_virtual_stock();
3572  $virtualstock = $tmpproduct->stock_theorique;
3573 
3574  $outvallabel .= ' - '.$langs->trans("VirtualStock").':'.$virtualstock;
3575 
3576  $optlabel .= ' - '.$langs->transnoentities("VirtualStock").':';
3577  if ($virtualstock > 0) {
3578  $optlabel .= '<span class="product_line_stock_ok">';
3579  } elseif ($virtualstock <= 0) {
3580  $optlabel .= '<span class="product_line_stock_too_low">';
3581  }
3582  $optlabel .= $virtualstock;
3583  $optlabel .= '</span>';
3584 
3585  unset($tmpproduct);
3586  }
3587  }
3588  }
3589 
3590  $optstart = '<option value="'.$outkey.'"';
3591  if ($selected && $selected == $objp->idprodfournprice) {
3592  $optstart .= ' selected';
3593  }
3594  if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
3595  $optstart .= ' disabled';
3596  }
3597 
3598  if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
3599  $optstart .= ' data-product-id="'.dol_escape_htmltag($objp->rowid).'"';
3600  $optstart .= ' data-price-id="'.dol_escape_htmltag($objp->idprodfournprice).'"';
3601  $optstart .= ' data-qty="'.dol_escape_htmltag($objp->quantity).'"';
3602  $optstart .= ' data-up="'.dol_escape_htmltag(price2num($objp->unitprice)).'"';
3603  $optstart .= ' data-up-locale="'.dol_escape_htmltag(price($objp->unitprice)).'"';
3604  $optstart .= ' data-discount="'.dol_escape_htmltag($outdiscount).'"';
3605  $optstart .= ' data-tvatx="'.dol_escape_htmltag(price2num($objp->tva_tx)).'"';
3606  $optstart .= ' data-tvatx-formated="'.dol_escape_htmltag(price($objp->tva_tx, 0, $langs, 1, -1, 2)).'"';
3607  $optstart .= ' data-default-vat-code="'.dol_escape_htmltag($objp->default_vat_code).'"';
3608  }
3609  $optstart .= ' data-description="'.dol_escape_htmltag($objp->description, 0, 1).'"';
3610 
3611  $outarrayentry = array(
3612  'key' => $outkey,
3613  'value' => $outref,
3614  'label' => $outvallabel,
3615  'qty' => $outqty,
3616  'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
3617  'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
3618  'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
3619  'tva_tx_formated' => price($objp->tva_tx, 0, $langs, 1, -1, 2),
3620  'tva_tx' => price2num($objp->tva_tx),
3621  'default_vat_code' => $objp->default_vat_code,
3622  'discount' => $outdiscount,
3623  'type' => $outtype,
3624  'duration_value' => $outdurationvalue,
3625  'duration_unit' => $outdurationunit,
3626  'disabled' => (empty($objp->idprodfournprice) ? true : false),
3627  'description' => $objp->description
3628  );
3629 
3630  $parameters = array(
3631  'objp' => &$objp,
3632  'optstart' => &$optstart,
3633  'optlabel' => &$optlabel,
3634  'outvallabel' => &$outvallabel,
3635  'outarrayentry' => &$outarrayentry
3636  );
3637  $reshook = $hookmanager->executeHooks('selectProduitsFournisseurListOption', $parameters, $this);
3638 
3639 
3640  // Add new entry
3641  // "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
3642  // "label" value of json key array is used by jQuery automatically as text for combo box
3643  $out .= $optstart . ' data-html="'.dol_escape_htmltag($optlabel).'">' . $optlabel . "</option>\n";
3644  array_push(
3645  $outarray,
3646  array('key'=>$outkey,
3647  'value'=>$outref,
3648  'label'=>$outvallabel,
3649  'qty'=>$outqty,
3650  'price_qty_ht'=>price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
3651  'price_qty_ht_locale'=>price($objp->fprice),
3652  'price_unit_ht'=>price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
3653  'price_unit_ht_locale'=>price($objp->unitprice),
3654  'price_ht'=>price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
3655  'tva_tx_formated' => price($objp->tva_tx),
3656  'tva_tx'=>price2num($objp->tva_tx),
3657  'default_vat_code'=>$objp->default_vat_code,
3658  'discount'=>$outdiscount,
3659  'type'=>$outtype,
3660  'duration_value'=>$outdurationvalue,
3661  'duration_unit'=>$outdurationunit,
3662  'disabled'=>(empty($objp->idprodfournprice) ? true : false),
3663  'description'=>$objp->description
3664  )
3665  );
3666  // Exemple of var_dump $outarray
3667  // array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
3668  // ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/1unité (20,00 Euros/unité)"
3669  // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
3670  //}
3671  //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3672  //$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unité (20,00 Euros/unité)');
3673  //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
3674 
3675  $i++;
3676  }
3677  $out .= '</select>';
3678 
3679  $this->db->free($result);
3680 
3681  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
3682  $out .= ajax_combobox($htmlname);
3683  } else {
3684  dol_print_error($this->db);
3685  }
3686 
3687  if (empty($outputmode)) {
3688  return $out;
3689  }
3690  return $outarray;
3691  }
3692 
3693  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3702  public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = '')
3703  {
3704  // phpcs:enable
3705  global $langs, $conf;
3706 
3707  $langs->load('stocks');
3708 
3709  $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
3710  $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
3711  $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
3712  $sql .= " FROM ".$this->db->prefix()."product as p";
3713  $sql .= " LEFT JOIN ".$this->db->prefix()."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
3714  $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON pfp.fk_soc = s.rowid";
3715  $sql .= " WHERE pfp.entity IN (".getEntity('productsupplierprice').")";
3716  $sql .= " AND p.tobuy = 1";
3717  $sql .= " AND s.fournisseur = 1";
3718  $sql .= " AND p.rowid = ".((int) $productid);
3719  if (empty($conf->global->PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED)) {
3720  $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
3721  } else {
3722  $sql .= " ORDER BY pfp.unitprice ASC";
3723  }
3724 
3725  dol_syslog(get_class($this)."::select_product_fourn_price", LOG_DEBUG);
3726  $result = $this->db->query($sql);
3727 
3728  if ($result) {
3729  $num = $this->db->num_rows($result);
3730 
3731  $form = '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3732 
3733  if (!$num) {
3734  $form .= '<option value="0">-- '.$langs->trans("NoSupplierPriceDefinedForThisProduct").' --</option>';
3735  } else {
3736  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3737  $form .= '<option value="0">&nbsp;</option>';
3738 
3739  $i = 0;
3740  while ($i < $num) {
3741  $objp = $this->db->fetch_object($result);
3742 
3743  $opt = '<option value="'.$objp->idprodfournprice.'"';
3744  //if there is only one supplier, preselect it
3745  if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier) || ($i == 0 && !empty($conf->global->PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED))) {
3746  $opt .= ' selected';
3747  }
3748  $opt .= '>'.$objp->name.' - '.$objp->ref_fourn.' - ';
3749 
3750  if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
3751  $prod_supplier = new ProductFournisseur($this->db);
3752  $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
3753  $prod_supplier->id = $productid;
3754  $prod_supplier->fourn_qty = $objp->quantity;
3755  $prod_supplier->fourn_tva_tx = $objp->tva_tx;
3756  $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
3757 
3758  require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
3759  $priceparser = new PriceParser($this->db);
3760  $price_result = $priceparser->parseProductSupplier($prod_supplier);
3761  if ($price_result >= 0) {
3762  $objp->fprice = $price_result;
3763  if ($objp->quantity >= 1) {
3764  $objp->unitprice = $objp->fprice / $objp->quantity;
3765  }
3766  }
3767  }
3768  if ($objp->quantity == 1) {
3769  $opt .= price($objp->fprice * (!empty($conf->global->DISPLAY_DISCOUNTED_SUPPLIER_PRICE) ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency)."/";
3770  }
3771 
3772  $opt .= $objp->quantity.' ';
3773 
3774  if ($objp->quantity == 1) {
3775  $opt .= $langs->trans("Unit");
3776  } else {
3777  $opt .= $langs->trans("Units");
3778  }
3779  if ($objp->quantity > 1) {
3780  $opt .= " - ";
3781  $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");
3782  }
3783  if ($objp->duration) {
3784  $opt .= " - ".$objp->duration;
3785  }
3786  $opt .= "</option>\n";
3787 
3788  $form .= $opt;
3789  $i++;
3790  }
3791  }
3792 
3793  $form .= '</select>';
3794  $this->db->free($result);
3795  return $form;
3796  } else {
3797  dol_print_error($this->db);
3798  return '';
3799  }
3800  }
3801 
3802  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3812  public function select_address($selected, $socid, $htmlname = 'address_id', $showempty = 0)
3813  {
3814  // phpcs:enable
3815  // looking for users
3816  $sql = "SELECT a.rowid, a.label";
3817  $sql .= " FROM ".$this->db->prefix()."societe_address as a";
3818  $sql .= " WHERE a.fk_soc = ".((int) $socid);
3819  $sql .= " ORDER BY a.label ASC";
3820 
3821  dol_syslog(get_class($this)."::select_address", LOG_DEBUG);
3822  $resql = $this->db->query($sql);
3823  if ($resql) {
3824  print '<select class="flat" id="select_'.$htmlname.'" name="'.$htmlname.'">';
3825  if ($showempty) {
3826  print '<option value="0">&nbsp;</option>';
3827  }
3828  $num = $this->db->num_rows($resql);
3829  $i = 0;
3830  if ($num) {
3831  while ($i < $num) {
3832  $obj = $this->db->fetch_object($resql);
3833 
3834  if ($selected && $selected == $obj->rowid) {
3835  print '<option value="'.$obj->rowid.'" selected>'.$obj->label.'</option>';
3836  } else {
3837  print '<option value="'.$obj->rowid.'">'.$obj->label.'</option>';
3838  }
3839  $i++;
3840  }
3841  }
3842  print '</select>';
3843  return $num;
3844  } else {
3845  dol_print_error($this->db);
3846  return -1;
3847  }
3848  }
3849 
3850  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3857  {
3858  // phpcs:enable
3859  global $langs;
3860 
3861  $num = count($this->cache_conditions_paiements);
3862  if ($num > 0) {
3863  return 0; // Cache already loaded
3864  }
3865 
3866  dol_syslog(__METHOD__, LOG_DEBUG);
3867 
3868  $sql = "SELECT rowid, code, libelle as label, deposit_percent";
3869  $sql .= " FROM ".$this->db->prefix().'c_payment_term';
3870  $sql .= " WHERE entity IN (".getEntity('c_payment_term').")";
3871  $sql .= " AND active > 0";
3872  $sql .= " ORDER BY sortorder";
3873 
3874  $resql = $this->db->query($sql);
3875  if ($resql) {
3876  $num = $this->db->num_rows($resql);
3877  $i = 0;
3878  while ($i < $num) {
3879  $obj = $this->db->fetch_object($resql);
3880 
3881  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3882  $label = ($langs->trans("PaymentConditionShort".$obj->code) != ("PaymentConditionShort".$obj->code) ? $langs->trans("PaymentConditionShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3883  $this->cache_conditions_paiements[$obj->rowid]['code'] = $obj->code;
3884  $this->cache_conditions_paiements[$obj->rowid]['label'] = $label;
3885  $this->cache_conditions_paiements[$obj->rowid]['deposit_percent'] = $obj->deposit_percent;
3886  $i++;
3887  }
3888 
3889  //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table
3890 
3891  return $num;
3892  } else {
3893  dol_print_error($this->db);
3894  return -1;
3895  }
3896  }
3897 
3898  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3904  public function load_cache_availability()
3905  {
3906  // phpcs:enable
3907  global $langs;
3908 
3909  $num = count($this->cache_availability); // TODO Use $conf->cache['availability'] instead of $this->cache_availability
3910  if ($num > 0) {
3911  return 0; // Cache already loaded
3912  }
3913 
3914  dol_syslog(__METHOD__, LOG_DEBUG);
3915 
3916  $langs->load('propal');
3917 
3918  $sql = "SELECT rowid, code, label, position";
3919  $sql .= " FROM ".$this->db->prefix().'c_availability';
3920  $sql .= " WHERE active > 0";
3921 
3922  $resql = $this->db->query($sql);
3923  if ($resql) {
3924  $num = $this->db->num_rows($resql);
3925  $i = 0;
3926  while ($i < $num) {
3927  $obj = $this->db->fetch_object($resql);
3928 
3929  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
3930  $label = ($langs->trans("AvailabilityType".$obj->code) != ("AvailabilityType".$obj->code) ? $langs->trans("AvailabilityType".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
3931  $this->cache_availability[$obj->rowid]['code'] = $obj->code;
3932  $this->cache_availability[$obj->rowid]['label'] = $label;
3933  $this->cache_availability[$obj->rowid]['position'] = $obj->position;
3934  $i++;
3935  }
3936 
3937  $this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
3938 
3939  return $num;
3940  } else {
3941  dol_print_error($this->db);
3942  return -1;
3943  }
3944  }
3945 
3956  public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '')
3957  {
3958  global $langs, $user;
3959 
3960  $this->load_cache_availability();
3961 
3962  dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
3963 
3964  print '<select id="'.$htmlname.'" class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
3965  if ($addempty) {
3966  print '<option value="0">&nbsp;</option>';
3967  }
3968  foreach ($this->cache_availability as $id => $arrayavailability) {
3969  if ($selected == $id) {
3970  print '<option value="'.$id.'" selected>';
3971  } else {
3972  print '<option value="'.$id.'">';
3973  }
3974  print dol_escape_htmltag($arrayavailability['label']);
3975  print '</option>';
3976  }
3977  print '</select>';
3978  if ($user->admin) {
3979  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3980  }
3981  print ajax_combobox($htmlname);
3982  }
3983 
3989  public function loadCacheInputReason()
3990  {
3991  global $langs;
3992 
3993  $num = count($this->cache_demand_reason); // TODO Use $conf->cache['input_reason'] instead of $this->cache_demand_reason
3994  if ($num > 0) {
3995  return 0; // Cache already loaded
3996  }
3997 
3998  $sql = "SELECT rowid, code, label";
3999  $sql .= " FROM ".$this->db->prefix().'c_input_reason';
4000  $sql .= " WHERE active > 0";
4001 
4002  $resql = $this->db->query($sql);
4003  if ($resql) {
4004  $num = $this->db->num_rows($resql);
4005  $i = 0;
4006  $tmparray = array();
4007  while ($i < $num) {
4008  $obj = $this->db->fetch_object($resql);
4009 
4010  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
4011  $label = ($obj->label != '-' ? $obj->label : '');
4012  if ($langs->trans("DemandReasonType".$obj->code) != ("DemandReasonType".$obj->code)) {
4013  $label = $langs->trans("DemandReasonType".$obj->code); // So translation key DemandReasonTypeSRC_XXX will work
4014  }
4015  if ($langs->trans($obj->code) != $obj->code) {
4016  $label = $langs->trans($obj->code); // So translation key SRC_XXX will work
4017  }
4018 
4019  $tmparray[$obj->rowid]['id'] = $obj->rowid;
4020  $tmparray[$obj->rowid]['code'] = $obj->code;
4021  $tmparray[$obj->rowid]['label'] = $label;
4022  $i++;
4023  }
4024 
4025  $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
4026 
4027  unset($tmparray);
4028  return $num;
4029  } else {
4030  dol_print_error($this->db);
4031  return -1;
4032  }
4033  }
4034 
4047  public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
4048  {
4049  global $langs, $user;
4050 
4051  $this->loadCacheInputReason();
4052 
4053  print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_'.$htmlname.'" name="'.$htmlname.'">';
4054  if ($addempty) {
4055  print '<option value="0"'.(empty($selected) ? ' selected' : '').'>&nbsp;</option>';
4056  }
4057  foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
4058  if ($arraydemandreason['code'] == $exclude) {
4059  continue;
4060  }
4061 
4062  if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
4063  print '<option value="'.$arraydemandreason['id'].'" selected>';
4064  } else {
4065  print '<option value="'.$arraydemandreason['id'].'">';
4066  }
4067  $label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
4068  print $langs->trans($label);
4069  print '</option>';
4070  }
4071  print '</select>';
4072  if ($user->admin && empty($notooltip)) {
4073  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4074  }
4075  print ajax_combobox('select_'.$htmlname);
4076  }
4077 
4078  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4084  public function load_cache_types_paiements()
4085  {
4086  // phpcs:enable
4087  global $langs;
4088 
4089  $num = count($this->cache_types_paiements); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_types_paiements
4090  if ($num > 0) {
4091  return $num; // Cache already loaded
4092  }
4093 
4094  dol_syslog(__METHOD__, LOG_DEBUG);
4095 
4096  $this->cache_types_paiements = array();
4097 
4098  $sql = "SELECT id, code, libelle as label, type, active";
4099  $sql .= " FROM ".$this->db->prefix()."c_paiement";
4100  $sql .= " WHERE entity IN (".getEntity('c_paiement').")";
4101 
4102  $resql = $this->db->query($sql);
4103  if ($resql) {
4104  $num = $this->db->num_rows($resql);
4105  $i = 0;
4106  while ($i < $num) {
4107  $obj = $this->db->fetch_object($resql);
4108 
4109  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
4110  $label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
4111  $this->cache_types_paiements[$obj->id]['id'] = $obj->id;
4112  $this->cache_types_paiements[$obj->id]['code'] = $obj->code;
4113  $this->cache_types_paiements[$obj->id]['label'] = $label;
4114  $this->cache_types_paiements[$obj->id]['type'] = $obj->type;
4115  $this->cache_types_paiements[$obj->id]['active'] = $obj->active;
4116  $i++;
4117  }
4118 
4119  $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
4120 
4121  return $num;
4122  } else {
4123  dol_print_error($this->db);
4124  return -1;
4125  }
4126  }
4127 
4128 
4129  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4147  public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
4148  {
4149  // phpcs:enable
4150  print $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, $noinfoadmin, $morecss, $deposit_percent = -1);
4151  }
4152 
4153 
4170  public function getSelectConditionsPaiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
4171  {
4172  global $langs, $user, $conf;
4173 
4174  $out = '';
4175  dol_syslog(__METHOD__." selected=".$selected.", htmlname=".$htmlname, LOG_DEBUG);
4176 
4178 
4179  // Set default value if not already set by caller
4180  if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID)) {
4181  $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TERM_ID;
4182  }
4183 
4184  $out.= '<select id="'.$htmlname.'" class="flat selectpaymentterms'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4185  if ($addempty) {
4186  $out.= '<option value="0">&nbsp;</option>';
4187  }
4188 
4189  $selectedDepositPercent = null;
4190 
4191  foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
4192  if ($filtertype <= 0 && !empty($arrayconditions['deposit_percent'])) {
4193  continue;
4194  }
4195 
4196  if ($selected == $id) {
4197  $selectedDepositPercent = $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'];
4198  $out .= '<option value="'.$id.'" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '" selected>';
4199  } else {
4200  $out .= '<option value="'.$id.'" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '">';
4201  }
4202  $label = $arrayconditions['label'];
4203 
4204  if (!empty($arrayconditions['deposit_percent'])) {
4205  $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'], $label);
4206  }
4207 
4208  $out.= $label;
4209  $out.= '</option>';
4210  }
4211  $out.= '</select>';
4212  if ($user->admin && empty($noinfoadmin)) {
4213  $out.= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4214  }
4215  $out.= ajax_combobox($htmlname);
4216 
4217  if ($deposit_percent >= 0) {
4218  $out .= ' <span id="'.$htmlname.'_deposit_percent_container"' . (empty($selectedDepositPercent) ? ' style="display: none"' : '') . '>';
4219  $out .= $langs->trans('DepositPercent') . ' : ';
4220  $out .= '<input id="'.$htmlname.'_deposit_percent" name="'.$htmlname.'_deposit_percent" class="maxwidth50" value="' . $deposit_percent . '" />';
4221  $out .= '</span>';
4222  $out .= '
4223  <script nonce="'.getNonce().'">
4224  $(document).ready(function () {
4225  $("#' . $htmlname . '").change(function () {
4226  let $selected = $(this).find("option:selected");
4227  let depositPercent = $selected.attr("data-deposit_percent");
4228 
4229  if (depositPercent.length > 0) {
4230  $("#'.$htmlname.'_deposit_percent_container").show().find("#'.$htmlname.'_deposit_percent").val(depositPercent);
4231  } else {
4232  $("#'.$htmlname.'_deposit_percent_container").hide();
4233  }
4234 
4235  return true;
4236  });
4237  });
4238  </script>';
4239  }
4240 
4241  return $out;
4242  }
4243 
4244 
4245  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4262  public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
4263  {
4264  // phpcs:enable
4265  global $langs, $user, $conf;
4266 
4267  $out = '';
4268 
4269  dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
4270 
4271  $filterarray = array();
4272  if ($filtertype == 'CRDT') {
4273  $filterarray = array(0, 2, 3);
4274  } elseif ($filtertype == 'DBIT') {
4275  $filterarray = array(1, 2, 3);
4276  } elseif ($filtertype != '' && $filtertype != '-1') {
4277  $filterarray = explode(',', $filtertype);
4278  }
4279 
4280  $this->load_cache_types_paiements();
4281 
4282  // Set default value if not already set by caller
4283  if (empty($selected) && !empty($conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID)) {
4284  $selected = $conf->global->MAIN_DEFAULT_PAYMENT_TYPE_ID;
4285  }
4286 
4287  $out .= '<select id="select'.$htmlname.'" class="flat selectpaymenttypes'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4288  if ($empty) {
4289  $out .= '<option value="">&nbsp;</option>';
4290  }
4291  foreach ($this->cache_types_paiements as $id => $arraytypes) {
4292  // If not good status
4293  if ($active >= 0 && $arraytypes['active'] != $active) {
4294  continue;
4295  }
4296 
4297  // On passe si on a demande de filtrer sur des modes de paiments particuliers
4298  if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
4299  continue;
4300  }
4301 
4302  // We discard empty line if showempty is on because an empty line has already been output.
4303  if ($empty && empty($arraytypes['code'])) {
4304  continue;
4305  }
4306 
4307  if ($format == 0) {
4308  $out .= '<option value="'.$id.'"';
4309  } elseif ($format == 1) {
4310  $out .= '<option value="'.$arraytypes['code'].'"';
4311  } elseif ($format == 2) {
4312  $out .= '<option value="'.$arraytypes['code'].'"';
4313  } elseif ($format == 3) {
4314  $out .= '<option value="'.$id.'"';
4315  }
4316  // Print attribute selected or not
4317  if ($format == 1 || $format == 2) {
4318  if ($selected == $arraytypes['code']) {
4319  $out .= ' selected';
4320  }
4321  } else {
4322  if ($selected == $id) {
4323  $out .= ' selected';
4324  }
4325  }
4326  $out .= '>';
4327  $value = '';
4328  if ($format == 0) {
4329  $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4330  } elseif ($format == 1) {
4331  $value = $arraytypes['code'];
4332  } elseif ($format == 2) {
4333  $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4334  } elseif ($format == 3) {
4335  $value = $arraytypes['code'];
4336  }
4337  $out .= $value ? $value : '&nbsp;';
4338  $out .= '</option>';
4339  }
4340  $out .= '</select>';
4341  if ($user->admin && !$noadmininfo) {
4342  $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4343  }
4344  $out .= ajax_combobox('select'.$htmlname);
4345 
4346  if (empty($nooutput)) {
4347  print $out;
4348  } else {
4349  return $out;
4350  }
4351  }
4352 
4353 
4362  public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
4363  {
4364  global $langs;
4365 
4366  $return = '<select class="flat maxwidth100" id="select_'.$htmlname.'" name="'.$htmlname.'">';
4367  $options = array(
4368  'HT'=>$langs->trans("HT"),
4369  'TTC'=>$langs->trans("TTC")
4370  );
4371  foreach ($options as $id => $value) {
4372  if ($selected == $id) {
4373  $return .= '<option value="'.$id.'" selected>'.$value;
4374  } else {
4375  $return .= '<option value="'.$id.'">'.$value;
4376  }
4377  $return .= '</option>';
4378  }
4379  $return .= '</select>';
4380  if ($addjscombo) {
4381  $return .= ajax_combobox('select_'.$htmlname);
4382  }
4383 
4384  return $return;
4385  }
4386 
4387  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4393  public function load_cache_transport_mode()
4394  {
4395  // phpcs:enable
4396  global $langs;
4397 
4398  $num = count($this->cache_transport_mode); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_transport_mode
4399  if ($num > 0) {
4400  return $num; // Cache already loaded
4401  }
4402 
4403  dol_syslog(__METHOD__, LOG_DEBUG);
4404 
4405  $this->cache_transport_mode = array();
4406 
4407  $sql = "SELECT rowid, code, label, active";
4408  $sql .= " FROM ".$this->db->prefix()."c_transport_mode";
4409  $sql .= " WHERE entity IN (".getEntity('c_transport_mode').")";
4410 
4411  $resql = $this->db->query($sql);
4412  if ($resql) {
4413  $num = $this->db->num_rows($resql);
4414  $i = 0;
4415  while ($i < $num) {
4416  $obj = $this->db->fetch_object($resql);
4417 
4418  // If traduction exist, we use it else we take the default label
4419  $label = ($langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) != ("PaymentTypeShort".$obj->code) ? $langs->transnoentitiesnoconv("PaymentTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
4420  $this->cache_transport_mode[$obj->rowid]['rowid'] = $obj->rowid;
4421  $this->cache_transport_mode[$obj->rowid]['code'] = $obj->code;
4422  $this->cache_transport_mode[$obj->rowid]['label'] = $label;
4423  $this->cache_transport_mode[$obj->rowid]['active'] = $obj->active;
4424  $i++;
4425  }
4426 
4427  $this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
4428 
4429  return $num;
4430  } else {
4431  dol_print_error($this->db);
4432  return -1;
4433  }
4434  }
4435 
4449  public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
4450  {
4451  global $langs, $user;
4452 
4453  dol_syslog(__METHOD__." ".$selected.", ".$htmlname.", ".$format, LOG_DEBUG);
4454 
4455  $this->load_cache_transport_mode();
4456 
4457  print '<select id="select'.$htmlname.'" class="flat selectmodetransport'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
4458  if ($empty) {
4459  print '<option value="">&nbsp;</option>';
4460  }
4461  foreach ($this->cache_transport_mode as $id => $arraytypes) {
4462  // If not good status
4463  if ($active >= 0 && $arraytypes['active'] != $active) {
4464  continue;
4465  }
4466 
4467  // We discard empty line if showempty is on because an empty line has already been output.
4468  if ($empty && empty($arraytypes['code'])) {
4469  continue;
4470  }
4471 
4472  if ($format == 0) {
4473  print '<option value="'.$id.'"';
4474  } elseif ($format == 1) {
4475  print '<option value="'.$arraytypes['code'].'"';
4476  } elseif ($format == 2) {
4477  print '<option value="'.$arraytypes['code'].'"';
4478  } elseif ($format == 3) {
4479  print '<option value="'.$id.'"';
4480  }
4481  // If text is selected, we compare with code, else with id
4482  if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
4483  print ' selected';
4484  } elseif ($selected == $id) {
4485  print ' selected';
4486  }
4487  print '>';
4488  $value = '';
4489  if ($format == 0) {
4490  $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4491  } elseif ($format == 1) {
4492  $value = $arraytypes['code'];
4493  } elseif ($format == 2) {
4494  $value = ($maxlength ?dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
4495  } elseif ($format == 3) {
4496  $value = $arraytypes['code'];
4497  }
4498  print $value ? $value : '&nbsp;';
4499  print '</option>';
4500  }
4501  print '</select>';
4502  if ($user->admin && !$noadmininfo) {
4503  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4504  }
4505  }
4506 
4519  public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
4520  {
4521  global $langs, $conf, $user;
4522 
4523  $langs->load("admin");
4524  $langs->load("deliveries");
4525 
4526  $sql = "SELECT rowid, code, libelle as label";
4527  $sql .= " FROM ".$this->db->prefix()."c_shipment_mode";
4528  $sql .= " WHERE active > 0";
4529  if ($filtre) {
4530  $sql .= " AND ".$filtre;
4531  }
4532  $sql .= " ORDER BY libelle ASC";
4533 
4534  dol_syslog(get_class($this)."::selectShippingMode", LOG_DEBUG);
4535  $result = $this->db->query($sql);
4536  if ($result) {
4537  $num = $this->db->num_rows($result);
4538  $i = 0;
4539  if ($num) {
4540  print '<select id="select'.$htmlname.'" class="flat selectshippingmethod'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4541  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4542  print '<option value="-1">&nbsp;</option>';
4543  }
4544  while ($i < $num) {
4545  $obj = $this->db->fetch_object($result);
4546  if ($selected == $obj->rowid) {
4547  print '<option value="'.$obj->rowid.'" selected>';
4548  } else {
4549  print '<option value="'.$obj->rowid.'">';
4550  }
4551  print ($langs->trans("SendingMethod".strtoupper($obj->code)) != "SendingMethod".strtoupper($obj->code)) ? $langs->trans("SendingMethod".strtoupper($obj->code)) : $obj->label;
4552  print '</option>';
4553  $i++;
4554  }
4555  print "</select>";
4556  if ($user->admin && empty($noinfoadmin)) {
4557  print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
4558  }
4559 
4560  print ajax_combobox('select'.$htmlname);
4561  } else {
4562  print $langs->trans("NoShippingMethodDefined");
4563  }
4564  } else {
4565  dol_print_error($this->db);
4566  }
4567  }
4568 
4578  public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
4579  {
4580  global $langs;
4581 
4582  $langs->load("deliveries");
4583 
4584  if ($htmlname != "none") {
4585  print '<form method="POST" action="'.$page.'">';
4586  print '<input type="hidden" name="action" value="setshippingmethod">';
4587  print '<input type="hidden" name="token" value="'.newToken().'">';
4588  $this->selectShippingMethod($selected, $htmlname, '', $addempty);
4589  print '<input type="submit" class="button valignmiddle" value="'.$langs->trans("Modify").'">';
4590  print '</form>';
4591  } else {
4592  if ($selected) {
4593  $code = $langs->getLabelFromKey($this->db, $selected, 'c_shipment_mode', 'rowid', 'code');
4594  print $langs->trans("SendingMethod".strtoupper($code));
4595  } else {
4596  print "&nbsp;";
4597  }
4598  }
4599  }
4600 
4609  public function selectSituationInvoices($selected = '', $socid = 0)
4610  {
4611  global $langs;
4612 
4613  $langs->load('bills');
4614 
4615  $opt = '<option value="" selected></option>';
4616  $sql = "SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc";
4617  $sql .= ' FROM '.$this->db->prefix().'facture';
4618  $sql .= ' WHERE entity IN ('.getEntity('invoice').')';
4619  $sql .= ' AND situation_counter >= 1';
4620  $sql .= ' AND fk_soc = '.(int) $socid;
4621  $sql .= ' AND type <> 2';
4622  $sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
4623  $resql = $this->db->query($sql);
4624 
4625  if ($resql && $this->db->num_rows($resql) > 0) {
4626  // Last seen cycle
4627  $ref = 0;
4628  while ($obj = $this->db->fetch_object($resql)) {
4629  //Same cycle ?
4630  if ($obj->situation_cycle_ref != $ref) {
4631  // Just seen this cycle
4632  $ref = $obj->situation_cycle_ref;
4633  //not final ?
4634  if ($obj->situation_final != 1) {
4635  //Not prov?
4636  if (substr($obj->ref, 1, 4) != 'PROV') {
4637  if ($selected == $obj->rowid) {
4638  $opt .= '<option value="'.$obj->rowid.'" selected>'.$obj->ref.'</option>';
4639  } else {
4640  $opt .= '<option value="'.$obj->rowid.'">'.$obj->ref.'</option>';
4641  }
4642  }
4643  }
4644  }
4645  }
4646  } else {
4647  dol_syslog("Error sql=".$sql.", error=".$this->error, LOG_ERR);
4648  }
4649  if ($opt == '<option value ="" selected></option>') {
4650  $opt = '<option value ="0" selected>'.$langs->trans('NoSituations').'</option>';
4651  }
4652  return $opt;
4653  }
4654 
4664  public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
4665  {
4666  global $langs;
4667 
4668  $langs->load('products');
4669 
4670  $return = '<select class="flat" id="'.$htmlname.'" name="'.$htmlname.'">';
4671 
4672  $sql = "SELECT rowid, label, code FROM ".$this->db->prefix()."c_units";
4673  $sql .= ' WHERE active > 0';
4674  if (!empty($unit_type)) {
4675  $sql .= " AND unit_type = '".$this->db->escape($unit_type)."'";
4676  }
4677  $sql .= " ORDER BY sortorder";
4678 
4679  $resql = $this->db->query($sql);
4680  if ($resql && $this->db->num_rows($resql) > 0) {
4681  if ($showempty) {
4682  $return .= '<option value="none"></option>';
4683  }
4684 
4685  while ($res = $this->db->fetch_object($resql)) {
4686  $unitLabel = $res->label;
4687  if (!empty($langs->tab_translate['unit'.$res->code])) { // check if Translation is available before
4688  $unitLabel = $langs->trans('unit'.$res->code) != $res->label ? $langs->trans('unit'.$res->code) : $res->label;
4689  }
4690 
4691  if ($selected == $res->rowid) {
4692  $return .= '<option value="'.$res->rowid.'" selected>'.$unitLabel.'</option>';
4693  } else {
4694  $return .= '<option value="'.$res->rowid.'">'.$unitLabel.'</option>';
4695  }
4696  }
4697  $return .= '</select>';
4698  }
4699  return $return;
4700  }
4701 
4702  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4717  public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0)
4718  {
4719  // phpcs:enable
4720  global $langs, $conf;
4721 
4722  $out = '';
4723 
4724  $langs->load("admin");
4725  $num = 0;
4726 
4727  $sql = "SELECT rowid, label, bank, clos as status, currency_code";
4728  $sql .= " FROM ".$this->db->prefix()."bank_account";
4729  $sql .= " WHERE entity IN (".getEntity('bank_account').")";
4730  if ($status != 2) {
4731  $sql .= " AND clos = ".(int) $status;
4732  }
4733  if ($filtre) {
4734  $sql .= " AND ".$filtre;
4735  }
4736  $sql .= " ORDER BY label";
4737 
4738  dol_syslog(get_class($this)."::select_comptes", LOG_DEBUG);
4739  $result = $this->db->query($sql);
4740  if ($result) {
4741  $num = $this->db->num_rows($result);
4742  $i = 0;
4743  if ($num) {
4744  $out .= '<select id="select'.$htmlname.'" class="flat selectbankaccount'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4745  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4746  $out .= '<option value="-1">&nbsp;</option>';
4747  }
4748 
4749  while ($i < $num) {
4750  $obj = $this->db->fetch_object($result);
4751  if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
4752  $out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'" selected>';
4753  } else {
4754  $out .= '<option value="'.$obj->rowid.'" data-currency-code="'.$obj->currency_code.'">';
4755  }
4756  $out .= trim($obj->label);
4757  if ($showcurrency) {
4758  $out .= ' ('.$obj->currency_code.')';
4759  }
4760  if ($status == 2 && $obj->status == 1) {
4761  $out .= ' ('.$langs->trans("Closed").')';
4762  }
4763  $out .= '</option>';
4764  $i++;
4765  }
4766  $out .= "</select>";
4767  $out .= ajax_combobox('select'.$htmlname);
4768  } else {
4769  if ($status == 0) {
4770  $out .= '<span class="opacitymedium">'.$langs->trans("NoActiveBankAccountDefined").'</span>';
4771  } else {
4772  $out .= '<span class="opacitymedium">'.$langs->trans("NoBankAccountFound").'</span>';
4773  }
4774  }
4775  } else {
4776  dol_print_error($this->db);
4777  }
4778 
4779  // Output or return
4780  if (empty($nooutput)) {
4781  print $out;
4782  } else {
4783  return $out;
4784  }
4785 
4786  return $num;
4787  }
4788 
4800  public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
4801  {
4802  global $langs, $conf;
4803 
4804  $langs->load("admin");
4805  $num = 0;
4806 
4807  $sql = "SELECT rowid, name, fk_country, status, entity";
4808  $sql .= " FROM ".$this->db->prefix()."establishment";
4809  $sql .= " WHERE 1=1";
4810  if ($status != 2) {
4811  $sql .= " AND status = ".(int) $status;
4812  }
4813  if ($filtre) {
4814  $sql .= " AND ".$filtre;
4815  }
4816  $sql .= " ORDER BY name";
4817 
4818  dol_syslog(get_class($this)."::select_establishment", LOG_DEBUG);
4819  $result = $this->db->query($sql);
4820  if ($result) {
4821  $num = $this->db->num_rows($result);
4822  $i = 0;
4823  if ($num) {
4824  print '<select id="select'.$htmlname.'" class="flat selectestablishment" name="'.$htmlname.'"'.($moreattrib ? ' '.$moreattrib : '').'>';
4825  if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
4826  print '<option value="-1">&nbsp;</option>';
4827  }
4828 
4829  while ($i < $num) {
4830  $obj = $this->db->fetch_object($result);
4831  if ($selected == $obj->rowid) {
4832  print '<option value="'.$obj->rowid.'" selected>';
4833  } else {
4834  print '<option value="'.$obj->rowid.'">';
4835  }
4836  print trim($obj->name);
4837  if ($status == 2 && $obj->status == 1) {
4838  print ' ('.$langs->trans("Closed").')';
4839  }
4840  print '</option>';
4841  $i++;
4842  }
4843  print "</select>";
4844  } else {
4845  if ($status == 0) {
4846  print '<span class="opacitymedium">'.$langs->trans("NoActiveEstablishmentDefined").'</span>';
4847  } else {
4848  print '<span class="opacitymedium">'.$langs->trans("NoEstablishmentFound").'</span>';
4849  }
4850  }
4851  } else {
4852  dol_print_error($this->db);
4853  return -1;
4854  }
4855  }
4856 
4866  public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
4867  {
4868  global $langs;
4869  if ($htmlname != "none") {
4870  print '<form method="POST" action="'.$page.'">';
4871  print '<input type="hidden" name="action" value="setbankaccount">';
4872  print '<input type="hidden" name="token" value="'.newToken().'">';
4873  print img_picto('', 'bank_account', 'class="pictofixedwidth"');
4874  $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
4875  if ($nbaccountfound > 0) {
4876  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
4877  }
4878  print '</form>';
4879  } else {
4880  $langs->load('banks');
4881 
4882  if ($selected) {
4883  require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
4884  $bankstatic = new Account($this->db);
4885  $result = $bankstatic->fetch($selected);
4886  if ($result) {
4887  print $bankstatic->getNomUrl(1);
4888  }
4889  } else {
4890  print "&nbsp;";
4891  }
4892  }
4893  }
4894 
4895  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4914  public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $markafterid = 0, $outputmode = 0, $include = 0, $morecss = '')
4915  {
4916  // phpcs:enable
4917  global $conf, $langs;
4918  $langs->load("categories");
4919 
4920  include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
4921 
4922  // For backward compatibility
4923  if (is_numeric($type)) {
4924  dol_syslog(__METHOD__.': using numeric value for parameter type is deprecated. Use string code instead.', LOG_WARNING);
4925  }
4926 
4927  if ($type === Categorie::TYPE_BANK_LINE) {
4928  // TODO Move this into common category feature
4929  $cate_arbo = array();
4930  $sql = "SELECT c.label, c.rowid";
4931  $sql .= " FROM ".$this->db->prefix()."bank_categ as c";
4932  $sql .= " WHERE entity = ".$conf->entity;
4933  $sql .= " ORDER BY c.label";
4934  $result = $this->db->query($sql);
4935  if ($result) {
4936  $num = $this->db->num_rows($result);
4937  $i = 0;
4938  while ($i < $num) {
4939  $objp = $this->db->fetch_object($result);
4940  if ($objp) {
4941  $cate_arbo[$objp->rowid] = array('id'=>$objp->rowid, 'fulllabel'=>$objp->label, 'color'=>'', 'picto'=>'category');
4942  }
4943  $i++;
4944  }
4945  $this->db->free($result);
4946  } else {
4947  dol_print_error($this->db);
4948  }
4949  } else {
4950  $cat = new Categorie($this->db);
4951  $cate_arbo = $cat->get_full_arbo($type, $markafterid, $include);
4952  }
4953 
4954  $outarray = array();
4955 
4956  $output = '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
4957  if (is_array($cate_arbo)) {
4958  if (!count($cate_arbo)) {
4959  $output .= '<option value="-1" disabled>'.$langs->trans("NoCategoriesDefined").'</option>';
4960  } else {
4961  $output .= '<option value="-1">&nbsp;</option>';
4962  foreach ($cate_arbo as $key => $value) {
4963  if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
4964  $add = 'selected ';
4965  } else {
4966  $add = '';
4967  }
4968  $output .= '<option '.$add.'value="'.$cate_arbo[$key]['id'].'"';
4969  $output .= ' data-html="'.dol_escape_htmltag(img_picto('', 'category', 'class="pictofixedwidth" style="color: #'.$cate_arbo[$key]['color'].'"').dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle')).'"';
4970  $output .= '>';
4971  $output .= dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle');
4972  $output .= '</option>';
4973 
4974  $outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
4975  }
4976  }
4977  }
4978  $output .= '</select>';
4979  $output .= "\n";
4980 
4981  if ($outputmode == 2) {
4982  return $cate_arbo;
4983  } elseif ($outputmode) {
4984  return $outarray;
4985  }
4986  return $output;
4987  }
4988 
4989  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5006  public function form_confirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
5007  {
5008  // phpcs:enable
5009  dol_syslog(__METHOD__.': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
5010  print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
5011  }
5012 
5040  public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 500, $disableformtag = 0, $labelbuttonyes = 'Yes', $labelbuttonno = 'No')
5041  {
5042  global $langs, $conf;
5043 
5044  $more = '<!-- formconfirm - before call, page='.dol_escape_htmltag($page).' -->';
5045  $formconfirm = '';
5046  $inputok = array();
5047  $inputko = array();
5048 
5049  // Clean parameters
5050  $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
5051  if ($conf->browser->layout == 'phone') {
5052  $width = '95%';
5053  }
5054 
5055  // Set height automatically if not defined
5056  if (empty($height)) {
5057  $height = 220;
5058  if (is_array($formquestion) && count($formquestion) > 2) {
5059  $height += ((count($formquestion) - 2) * 24);
5060  }
5061  }
5062 
5063  if (is_array($formquestion) && !empty($formquestion)) {
5064  // First add hidden fields and value
5065  foreach ($formquestion as $key => $input) {
5066  if (is_array($input) && !empty($input)) {
5067  if ($input['type'] == 'hidden') {
5068  $moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : '');
5069  $morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : '');
5070 
5071  $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";
5072  }
5073  }
5074  }
5075 
5076  // Now add questions
5077  $moreonecolumn = '';
5078  $more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">'."\n";
5079  foreach ($formquestion as $key => $input) {
5080  if (is_array($input) && !empty($input)) {
5081  $size = (!empty($input['size']) ? ' size="'.$input['size'].'"' : ''); // deprecated. Use morecss instead.
5082  $moreattr = (!empty($input['moreattr']) ? ' '.$input['moreattr'] : '');
5083  $morecss = (!empty($input['morecss']) ? ' '.$input['morecss'] : '');
5084 
5085  if ($input['type'] == 'text') {
5086  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd"><input type="text" class="flat'.$morecss.'" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'"'.$size.' value="'.(empty($input['value']) ? '' : $input['value']).'"'.$moreattr.' /></div></div>'."\n";
5087  } elseif ($input['type'] == 'password') {
5088  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd"><input type="password" class="flat'.$morecss.'" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'"'.$size.' value="'.(empty($input['value']) ? '' : $input['value']).'"'.$moreattr.' /></div></div>'."\n";
5089  } elseif ($input['type'] == 'textarea') {
5090  /*$more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div><div class="tagtd">';
5091  $more .= '<textarea name="'.$input['name'].'" class="'.$morecss.'"'.$moreattr.'>';
5092  $more .= $input['value'];
5093  $more .= '</textarea>';
5094  $more .= '</div></div>'."\n";*/
5095  $moreonecolumn .= '<div class="margintoponly">';
5096  $moreonecolumn .= $input['label'].'<br>';
5097  $moreonecolumn .= '<textarea name="'.dol_escape_htmltag($input['name']).'" id="'.dol_escape_htmltag($input['name']).'" class="'.$morecss.'"'.$moreattr.'>';
5098  $moreonecolumn .= $input['value'];
5099  $moreonecolumn .= '</textarea>';
5100  $moreonecolumn .= '</div>';
5101  } elseif (in_array($input['type'], ['select', 'multiselect'])) {
5102  if (empty($morecss)) {
5103  $morecss = 'minwidth100';
5104  }
5105 
5106  $show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
5107  $key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
5108  $value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
5109  $translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
5110  $maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
5111  $disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
5112  $sort = isset($input['select_sort']) ? $input['select_sort'] : '';
5113 
5114  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
5115  if (!empty($input['label'])) {
5116  $more .= $input['label'].'</div><div class="tagtd left">';
5117  }
5118  if ($input['type'] == 'select') {
5119  $more .= $this->selectarray($input['name'], $input['values'], !empty($input['default']) ? $input['default'] : '-1', $show_empty, $key_in_label, $value_as_key, $moreattr, $translate, $maxlen, $disabled, $sort, $morecss);
5120  } else {
5121  $more .= $this->multiselectarray($input['name'], $input['values'], is_array($input['default']) ? $input['default'] : [$input['default']], $key_in_label, $value_as_key, $morecss, $translate, $maxlen, $moreattr);
5122  }
5123  $more .= '</div></div>'."\n";
5124  } elseif ($input['type'] == 'checkbox') {
5125  $more .= '<div class="tagtr">';
5126  $more .= '<div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'"><label for="'.dol_escape_htmltag($input['name']).'">'.$input['label'].'</label></div><div class="tagtd">';
5127  $more .= '<input type="checkbox" class="flat'.($morecss ? ' '.$morecss : '').'" id="'.dol_escape_htmltag($input['name']).'" name="'.dol_escape_htmltag($input['name']).'"'.$moreattr;
5128  if (!is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0' && $input['value'] != '') {
5129  $more .= ' checked';
5130  }
5131  if (is_bool($input['value']) && $input['value']) {
5132  $more .= ' checked';
5133  }
5134  if (isset($input['disabled'])) {
5135  $more .= ' disabled';
5136  }
5137  $more .= ' /></div>';
5138  $more .= '</div>'."\n";
5139  } elseif ($input['type'] == 'radio') {
5140  $i = 0;
5141  foreach ($input['values'] as $selkey => $selval) {
5142  $more .= '<div class="tagtr">';
5143  if (isset($input['label'])) {
5144  if ($i == 0) {
5145  $more .= '<div class="tagtd'.(empty($input['tdclass']) ? ' tdtop' : (' tdtop '.$input['tdclass'])).'">'.$input['label'].'</div>';
5146  } else {
5147  $more .= '<div clas="tagtd'.(empty($input['tdclass']) ? '' : (' "'.$input['tdclass'])).'">&nbsp;</div>';
5148  }
5149  }
5150  $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;
5151  if (!empty($input['disabled'])) {
5152  $more .= ' disabled';
5153  }
5154  if (isset($input['default']) && $input['default'] === $selkey) {
5155  $more .= ' checked="checked"';
5156  }
5157  $more .= ' /> ';
5158  $more .= '<label for="'.dol_escape_htmltag($input['name'].$selkey).'" class="valignmiddle">'.$selval.'</label>';
5159  $more .= '</div></div>'."\n";
5160  $i++;
5161  }
5162  } elseif ($input['type'] == 'date' || $input['type'] == 'datetime') {
5163  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">'.$input['label'].'</div>';
5164  $more .= '<div class="tagtd">';
5165  $addnowlink = (empty($input['datenow']) ? 0 : 1);
5166  $more .= $this->selectDate($input['value'], $input['name'], ($input['type'] == 'datetime' ? 1 : 0), ($input['type'] == 'datetime' ? 1 : 0), 0, '', 1, $addnowlink);
5167  $more .= '</div></div>'."\n";
5168  $formquestion[] = array('name'=>$input['name'].'day');
5169  $formquestion[] = array('name'=>$input['name'].'month');
5170  $formquestion[] = array('name'=>$input['name'].'year');
5171  $formquestion[] = array('name'=>$input['name'].'hour');
5172  $formquestion[] = array('name'=>$input['name'].'min');
5173  } elseif ($input['type'] == 'other') { // can be 1 column or 2 depending if label is set or not
5174  $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
5175  if (!empty($input['label'])) {
5176  $more .= $input['label'].'</div><div class="tagtd">';
5177  }
5178  $more .= $input['value'];
5179  $more .= '</div></div>'."\n";
5180  } elseif ($input['type'] == 'onecolumn') {
5181  $moreonecolumn .= '<div class="margintoponly">';
5182  $moreonecolumn .= $input['value'];
5183  $moreonecolumn .= '</div>'."\n";
5184  } elseif ($input['type'] == 'hidden') {
5185  // Do nothing more, already added by a previous loop
5186  } elseif ($input['type'] == 'separator') {
5187  $more .= '<br>';
5188  } else {
5189  $more .= 'Error type '.$input['type'].' for the confirm box is not a supported type';
5190  }
5191  }
5192  }
5193  $more .= '</div>'."\n";
5194  $more .= $moreonecolumn;
5195  }
5196 
5197  // JQUERY method dialog is broken with smartphone, we use standard HTML.
5198  // 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
5199  // See page product/card.php for example
5200  if (!empty($conf->dol_use_jmobile)) {
5201  $useajax = 0;
5202  }
5203  if (empty($conf->use_javascript_ajax)) {
5204  $useajax = 0;
5205  }
5206 
5207  if ($useajax) {
5208  $autoOpen = true;
5209  $dialogconfirm = 'dialog-confirm';
5210  $button = '';
5211  if (!is_numeric($useajax)) {
5212  $button = $useajax;
5213  $useajax = 1;
5214  $autoOpen = false;
5215  $dialogconfirm .= '-'.$button;
5216  }
5217  $pageyes = $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.urlencode($action).'&confirm=yes';
5218  $pageno = ($useajax == 2 ? $page.(preg_match('/\?/', $page) ? '&' : '?').'action='.urlencode($action).'&confirm=no' : '');
5219 
5220  // Add input fields into list of fields to read during submit (inputok and inputko)
5221  if (is_array($formquestion)) {
5222  foreach ($formquestion as $key => $input) {
5223  //print "xx ".$key." rr ".is_array($input)."<br>\n";
5224  // Add name of fields to propagate with the GET when submitting the form with button OK.
5225  if (is_array($input) && isset($input['name'])) {
5226  if (strpos($input['name'], ',') > 0) {
5227  $inputok = array_merge($inputok, explode(',', $input['name']));
5228  } else {
5229  array_push($inputok, $input['name']);
5230  }
5231  }
5232  // Add name of fields to propagate with the GET when submitting the form with button KO.
5233  if (isset($input['inputko']) && $input['inputko'] == 1) {
5234  array_push($inputko, $input['name']);
5235  }
5236  }
5237  }
5238 
5239  // Show JQuery confirm box.
5240  $formconfirm .= '<div id="'.$dialogconfirm.'" title="'.dol_escape_htmltag($title).'" style="display: none;">';
5241  if (is_array($formquestion) && !empty($formquestion['text'])) {
5242  $formconfirm .= '<div class="confirmtext">'.$formquestion['text'].'</div>'."\n";
5243  }
5244  if (!empty($more)) {
5245  $formconfirm .= '<div class="confirmquestions">'.$more.'</div>'."\n";
5246  }
5247  $formconfirm .= ($question ? '<div class="confirmmessage">'.img_help('', '').' '.$question.'</div>' : '');
5248  $formconfirm .= '</div>'."\n";
5249 
5250  $formconfirm .= "\n<!-- begin code of popup for formconfirm page=".$page." -->\n";
5251  $formconfirm .= '<script nonce="'.getNonce().'" type="text/javascript">'."\n";
5252  $formconfirm .= "/* Code for the jQuery('#dialogforpopup').dialog() */\n";
5253  $formconfirm .= 'jQuery(document).ready(function() {
5254  $(function() {
5255  $( "#'.$dialogconfirm.'" ).dialog(
5256  {
5257  autoOpen: '.($autoOpen ? "true" : "false").',';
5258  if ($newselectedchoice == 'no') {
5259  $formconfirm .= '
5260  open: function() {
5261  $(this).parent().find("button.ui-button:eq(2)").focus();
5262  },';
5263  }
5264 
5265  $jsforcursor = '';
5266  if ($useajax == 1) {
5267  $jsforcursor = '// The call to urljump can be slow, so we set the wait cursor'."\n";
5268  $jsforcursor .= 'jQuery("html,body,#id-container").addClass("cursorwait");'."\n";
5269  }
5270 
5271  $postconfirmas = 'GET';
5272 
5273  $formconfirm .= '
5274  resizable: false,
5275  height: "'.$height.'",
5276  width: "'.$width.'",
5277  modal: true,
5278  closeOnEscape: false,
5279  buttons: {
5280  "'.dol_escape_js($langs->transnoentities($labelbuttonyes)).'": function() {
5281  var options = "token='.urlencode(newToken()).'";
5282  var inputok = '.json_encode($inputok).'; /* List of fields into form */
5283  var page = "'.dol_escape_js(!empty($page) ? $page : '').'";
5284  var pageyes = "'.dol_escape_js(!empty($pageyes) ? $pageyes : '').'";
5285 
5286  if (inputok.length > 0) {
5287  $.each(inputok, function(i, inputname) {
5288  var more = "";
5289  var inputvalue;
5290  if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
5291  inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
5292  } else {
5293  if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5294  inputvalue = $("#" + inputname + more).val();
5295  }
5296  if (typeof inputvalue == "undefined") { inputvalue=""; }
5297  console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
5298  options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5299  });
5300  }
5301  var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "&") + options;
5302  if (pageyes.length > 0) {';
5303  if ($postconfirmas == 'GET') {
5304  $formconfirm .= 'location.href = urljump;';
5305  } else {
5306  $formconfirm .= $jsforcursor;
5307  $formconfirm .= 'var post = $.post(
5308  pageyes,
5309  options,
5310  function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
5311  );';
5312  }
5313  $formconfirm .= '
5314  console.log("after post ok");
5315  }
5316  $(this).dialog("close");
5317  },
5318  "'.dol_escape_js($langs->transnoentities($labelbuttonno)).'": function() {
5319  var options = "token='.urlencode(newToken()).'";
5320  var inputko = '.json_encode($inputko).'; /* List of fields into form */
5321  var page = "'.dol_escape_js(!empty($page) ? $page : '').'";
5322  var pageno="'.dol_escape_js(!empty($pageno) ? $pageno : '').'";
5323  if (inputko.length > 0) {
5324  $.each(inputko, function(i, inputname) {
5325  var more = "";
5326  if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
5327  var inputvalue = $("#" + inputname + more).val();
5328  if (typeof inputvalue == "undefined") { inputvalue=""; }
5329  options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
5330  });
5331  }
5332  var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "&") + options;
5333  //alert(urljump);
5334  if (pageno.length > 0) {';
5335  if ($postconfirmas == 'GET') {
5336  $formconfirm .= 'location.href = urljump;';
5337  } else {
5338  $formconfirm .= $jsforcursor;
5339  $formconfirm .= 'var post = $.post(
5340  pageno,
5341  options,
5342  function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
5343  );';
5344  }
5345  $formconfirm .= '
5346  console.log("after post ko");
5347  }
5348  $(this).dialog("close");
5349  }
5350  }
5351  }
5352  );
5353 
5354  var button = "'.$button.'";
5355  if (button.length > 0) {
5356  $( "#" + button ).click(function() {
5357  $("#'.$dialogconfirm.'").dialog("open");
5358  });
5359  }
5360  });
5361  });
5362  </script>';
5363  $formconfirm .= "<!-- end ajax formconfirm -->\n";
5364  } else {
5365  $formconfirm .= "\n<!-- begin formconfirm page=".dol_escape_htmltag($page)." -->\n";
5366 
5367  if (empty($disableformtag)) {
5368  $formconfirm .= '<form method="POST" action="'.$page.'" class="notoptoleftroright">'."\n";
5369  }
5370 
5371  $formconfirm .= '<input type="hidden" name="action" value="'.$action.'">'."\n";
5372  $formconfirm .= '<input type="hidden" name="token" value="'.newToken().'">'."\n";
5373 
5374  $formconfirm .= '<table class="valid centpercent">'."\n";
5375 
5376  // Line title
5377  $formconfirm .= '<tr class="validtitre"><td class="validtitre" colspan="2">';
5378  $formconfirm .= img_picto('', 'pictoconfirm').' '.$title;
5379  $formconfirm .= '</td></tr>'."\n";
5380 
5381  // Line text
5382  if (is_array($formquestion) && !empty($formquestion['text'])) {
5383  $formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'.$formquestion['text'].'</td></tr>'."\n";
5384  }
5385 
5386  // Line form fields
5387  if ($more) {
5388  $formconfirm .= '<tr class="valid"><td class="valid" colspan="2">'."\n";
5389  $formconfirm .= $more;
5390  $formconfirm .= '</td></tr>'."\n";
5391  }
5392 
5393  // Line with question
5394  $formconfirm .= '<tr class="valid">';
5395  $formconfirm .= '<td class="valid">'.$question.'</td>';
5396  $formconfirm .= '<td class="valid center">';
5397  $formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly', $labelbuttonyes, $labelbuttonno);
5398  $formconfirm .= '<input class="button valignmiddle confirmvalidatebutton small" type="submit" value="'.$langs->trans("Validate").'">';
5399  $formconfirm .= '</td>';
5400  $formconfirm .= '</tr>'."\n";
5401 
5402  $formconfirm .= '</table>'."\n";
5403 
5404  if (empty($disableformtag)) {
5405  $formconfirm .= "</form>\n";
5406  }
5407  $formconfirm .= '<br>';
5408 
5409  if (!empty($conf->use_javascript_ajax)) {
5410  $formconfirm .= '<!-- code to disable button to avoid double clic -->';
5411  $formconfirm .= '<script nonce="'.getNonce().'" type="text/javascript">'."\n";
5412  $formconfirm .= '
5413  $(document).ready(function () {
5414  $(".confirmvalidatebutton").on("click", function() {
5415  console.log("We click on button");
5416  $(this).attr("disabled", "disabled");
5417  setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
5418  //console.log($(this).closest("form"));
5419  $(this).closest("form").submit();
5420  });
5421  });
5422  ';
5423  $formconfirm .= '</script>'."\n";
5424  }
5425 
5426  $formconfirm .= "<!-- end formconfirm -->\n";
5427  }
5428 
5429  return $formconfirm;
5430  }
5431 
5432 
5433  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5449  public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0, $textifnoproject = '', $morecss = '')
5450  {
5451  // phpcs:enable
5452  global $langs;
5453 
5454  require_once DOL_DOCUMENT_ROOT.'/core/lib/project.lib.php';
5455  require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
5456 
5457  $out = '';
5458 
5459  $formproject = new FormProjets($this->db);
5460 
5461  $langs->load("project");
5462  if ($htmlname != "none") {
5463  $out .= '<form method="post" action="'.$page.'">';
5464  $out .= '<input type="hidden" name="action" value="classin">';
5465  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
5466  $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1, 0, $morecss);
5467  $out .= '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5468  $out .= '</form>';
5469  } else {
5470  $out .= '<span class="project_head_block">';
5471  if ($selected) {
5472  $projet = new Project($this->db);
5473  $projet->fetch($selected);
5474  $out .= $projet->getNomUrl(0, '', 1);
5475  } else {
5476  $out .= '<span class="opacitymedium">'.$textifnoproject.'</span>';
5477  }
5478  $out .= '</span>';
5479  }
5480 
5481  if (empty($nooutput)) {
5482  print $out;
5483  return '';
5484  }
5485  return $out;
5486  }
5487 
5488  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5504  public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0, $type = '', $filtertype = -1, $deposit_percent = -1, $nooutput = 0)
5505  {
5506  // phpcs:enable
5507  global $langs;
5508 
5509  $out = '';
5510 
5511  if ($htmlname != "none") {
5512  $out .= '<form method="POST" action="'.$page.'">';
5513  $out .= '<input type="hidden" name="action" value="setconditions">';
5514  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
5515  if ($type) {
5516  $out .= '<input type="hidden" name="type" value="'.dol_escape_htmltag($type).'">';
5517  }
5518  $out .= $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, 0, '', $deposit_percent);
5519  $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="'.$langs->trans("Modify").'">';
5520  $out .= '</form>';
5521  } else {
5522  if ($selected) {
5524  if (isset($this->cache_conditions_paiements[$selected])) {
5525  $label = $this->cache_conditions_paiements[$selected]['label'];
5526 
5527  if (!empty($this->cache_conditions_paiements[$selected]['deposit_percent'])) {
5528  $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $this->cache_conditions_paiements[$selected]['deposit_percent'], $label);
5529  }
5530 
5531  $out .= $label;
5532  } else {
5533  $langs->load('errors');
5534  $out .= $langs->trans('ErrorNotInDictionaryPaymentConditions');
5535  }
5536  } else {
5537  $out .= '&nbsp;';
5538  }
5539  }
5540 
5541  if (empty($nooutput)) {
5542  print $out;
5543  return '';
5544  }
5545  return $out;
5546  }
5547 
5548  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5558  public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
5559  {
5560  // phpcs:enable
5561  global $langs;
5562  if ($htmlname != "none") {
5563  print '<form method="post" action="'.$page.'">';
5564  print '<input type="hidden" name="action" value="setavailability">';
5565  print '<input type="hidden" name="token" value="'.newToken().'">';
5566  $this->selectAvailabilityDelay($selected, $htmlname, -1, $addempty);
5567  print '<input type="submit" name="modify" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5568  print '<input type="submit" name="cancel" class="button smallpaddingimp" value="'.$langs->trans("Cancel").'">';
5569  print '</form>';
5570  } else {
5571  if ($selected) {
5572  $this->load_cache_availability();
5573  print $this->cache_availability[$selected]['label'];
5574  } else {
5575  print "&nbsp;";
5576  }
5577  }
5578  }
5579 
5590  public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0)
5591  {
5592  global $langs;
5593  if ($htmlname != "none") {
5594  print '<form method="post" action="'.$page.'">';
5595  print '<input type="hidden" name="action" value="setdemandreason">';
5596  print '<input type="hidden" name="token" value="'.newToken().'">';
5597  $this->selectInputReason($selected, $htmlname, -1, $addempty);
5598  print '<input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'">';
5599  print '</form>';
5600  } else {
5601  if ($selected) {
5602  $this->loadCacheInputReason();
5603  foreach ($this->cache_demand_reason as $key => $val) {
5604  if ($val['id'] == $selected) {
5605  print $val['label'];
5606  break;
5607  }
5608  }
5609  } else {
5610  print "&nbsp;";
5611  }
5612  }
5613  }
5614 
5615  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5629  public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0, $type = '')
5630  {
5631  // phpcs:enable
5632  global $langs;
5633 
5634  $ret = '';
5635 
5636  if ($htmlname != "none") {
5637  $ret .= '<form method="POST" action="'.$page.'" name="form'.$htmlname.'">';
5638  $ret .= '<input type="hidden" name="action" value="set'.$htmlname.'">';
5639  $ret .= '<input type="hidden" name="token" value="'.newToken().'">';
5640  if ($type) {
5641  $ret .= '<input type="hidden" name="type" value="'.dol_escape_htmltag($type).'">';
5642  }
5643  $ret .= '<table class="nobordernopadding">';
5644  $ret .= '<tr><td>';
5645  $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form'.$htmlname, 1, 0);
5646  $ret .= '</td>';
5647  $ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5648  $ret .= '</tr></table></form>';
5649  } else {
5650  if ($displayhour) {
5651  $ret .= dol_print_date($selected, 'dayhour');
5652  } else {
5653  $ret .= dol_print_date($selected, 'day');
5654  }
5655  }
5656 
5657  if (empty($nooutput)) {
5658  print $ret;
5659  }
5660  return $ret;
5661  }
5662 
5663 
5664  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5675  public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = '', $include = '')
5676  {
5677  // phpcs:enable
5678  global $langs;
5679 
5680  if ($htmlname != "none") {
5681  print '<form method="POST" action="'.$page.'" name="form'.$htmlname.'">';
5682  print '<input type="hidden" name="action" value="set'.$htmlname.'">';
5683  print '<input type="hidden" name="token" value="'.newToken().'">';
5684  print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
5685  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5686  print '</form>';
5687  } else {
5688  if ($selected) {
5689  require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
5690  $theuser = new User($this->db);
5691  $theuser->fetch($selected);
5692  print $theuser->getNomUrl(1);
5693  } else {
5694  print "&nbsp;";
5695  }
5696  }
5697  }
5698 
5699 
5700  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5714  public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0, $type = '', $nooutput = 0)
5715  {
5716  // phpcs:enable
5717  global $langs;
5718 
5719  $out = '';
5720  if ($htmlname != "none") {
5721  $out .= '<form method="POST" action="'.$page.'">';
5722  $out .= '<input type="hidden" name="action" value="setmode">';
5723  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
5724  if ($type) {
5725  $out .= '<input type="hidden" name="type" value="'.dol_escape_htmltag($type).'">';
5726  }
5727  $out .= $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active, '', 1);
5728  $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5729  $out .= '</form>';
5730  } else {
5731  if ($selected) {
5732  $this->load_cache_types_paiements();
5733  $out .= $this->cache_types_paiements[$selected]['label'];
5734  } else {
5735  $out .= "&nbsp;";
5736  }
5737  }
5738 
5739  if ($nooutput) {
5740  return $out;
5741  } else {
5742  print $out;
5743  }
5744  return '';
5745  }
5746 
5757  public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
5758  {
5759  global $langs;
5760  if ($htmlname != "none") {
5761  print '<form method="POST" action="'.$page.'">';
5762  print '<input type="hidden" name="action" value="settransportmode">';
5763  print '<input type="hidden" name="token" value="'.newToken().'">';
5764  $this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
5765  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5766  print '</form>';
5767  } else {
5768  if ($selected) {
5769  $this->load_cache_transport_mode();
5770  print $this->cache_transport_mode[$selected]['label'];
5771  } else {
5772  print "&nbsp;";
5773  }
5774  }
5775  }
5776 
5777  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5786  public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
5787  {
5788  // phpcs:enable
5789  global $langs;
5790  if ($htmlname != "none") {
5791  print '<form method="POST" action="'.$page.'">';
5792  print '<input type="hidden" name="action" value="setmulticurrencycode">';
5793  print '<input type="hidden" name="token" value="'.newToken().'">';
5794  print $this->selectMultiCurrency($selected, $htmlname, 0);
5795  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5796  print '</form>';
5797  } else {
5798  dol_include_once('/core/lib/company.lib.php');
5799  print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
5800  }
5801  }
5802 
5803  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5813  public function form_multicurrency_rate($page, $rate = '', $htmlname = 'multicurrency_tx', $currency = '')
5814  {
5815  // phpcs:enable
5816  global $langs, $mysoc, $conf;
5817 
5818  if ($htmlname != "none") {
5819  print '<form method="POST" action="'.$page.'">';
5820  print '<input type="hidden" name="action" value="setmulticurrencyrate">';
5821  print '<input type="hidden" name="token" value="'.newToken().'">';
5822  print '<input type="text" class="maxwidth100" name="'.$htmlname.'" value="'.(!empty($rate) ? price(price2num($rate, 'CU')) : 1).'" /> ';
5823  print '<select name="calculation_mode">';
5824  print '<option value="1">Change '.$langs->trans("PriceUHT").' of lines</option>';
5825  print '<option value="2">Change '.$langs->trans("PriceUHTCurrency").' of lines</option>';
5826  print '</select> ';
5827  print '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
5828  print '</form>';
5829  } else {
5830  if (!empty($rate)) {
5831  print price($rate, 1, $langs, 1, 0);
5832  if ($currency && $rate != 1) {
5833  print ' &nbsp; ('.price($rate, 1, $langs, 1, 0).' '.$currency.' = 1 '.$conf->currency.')';
5834  }
5835  } else {
5836  print 1;
5837  }
5838  }
5839  }
5840 
5841 
5842  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5858  public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0)
5859  {
5860  // phpcs:enable
5861  global $conf, $langs;
5862  if ($htmlname != "none") {
5863  print '<form method="post" action="'.$page.'">';
5864  print '<input type="hidden" name="action" value="setabsolutediscount">';
5865  print '<input type="hidden" name="token" value="'.newToken().'">';
5866  print '<div class="inline-block">';
5867  if (!empty($discount_type)) {
5868  if (!empty($conf->global->FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS)) {
5869  if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
5870  $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be substracted to payments only and not to total of final invoice
5871  } else {
5872  $translationKey = 'HasCreditNoteFromSupplier';
5873  }
5874  } else {
5875  if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5876  $translationKey = 'HasAbsoluteDiscountFromSupplier';
5877  } else {
5878  $translationKey = 'HasCreditNoteFromSupplier';
5879  }
5880  }
5881  } else {
5882  if (!empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
5883  if (!$filter || $filter == "fk_facture_source IS NULL") {
5884  $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be substracted to payments only and not to total of final invoice
5885  } else {
5886  $translationKey = 'CompanyHasCreditNote';
5887  }
5888  } else {
5889  if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5890  $translationKey = 'CompanyHasAbsoluteDiscount';
5891  } else {
5892  $translationKey = 'CompanyHasCreditNote';
5893  }
5894  }
5895  }
5896  print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
5897  if (empty($hidelist)) {
5898  print ' ';
5899  }
5900  print '</div>';
5901  if (empty($hidelist)) {
5902  print '<div class="inline-block" style="padding-right: 10px">';
5903  $newfilter = 'discount_type='.intval($discount_type);
5904  if (!empty($discount_type)) {
5905  $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
5906  } else {
5907  $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
5908  }
5909  if ($filter) {
5910  $newfilter .= ' AND ('.$filter.')';
5911  }
5912  // output the combo of discounts
5913  $nbqualifiedlines = $this->select_remises($selected, $htmlname, $newfilter, $socid, $maxvalue);
5914  if ($nbqualifiedlines > 0) {
5915  print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="'.dol_escape_htmltag($langs->trans("UseLine")).'"';
5916  if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
5917  print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5918  }
5919  if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
5920  print ' title="'.$langs->trans("UseCreditNoteInInvoicePayment").'"';
5921  }
5922 
5923  print '>';
5924  }
5925  print '</div>';
5926  }
5927  if ($more) {
5928  print '<div class="inline-block">';
5929  print $more;
5930  print '</div>';
5931  }
5932  print '</form>';
5933  } else {
5934  if ($selected) {
5935  print $selected;
5936  } else {
5937  print "0";
5938  }
5939  }
5940  }
5941 
5942 
5943  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5953  public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
5954  {
5955  // phpcs:enable
5956  global $langs, $conf;
5957 
5958  if ($htmlname != "none") {
5959  print '<form method="post" action="'.$page.'">';
5960  print '<input type="hidden" name="action" value="set_contact">';
5961  print '<input type="hidden" name="token" value="'.newToken().'">';
5962  print '<table class="nobordernopadding">';
5963  print '<tr><td>';
5964  print $this->selectcontacts($societe->id, $selected, $htmlname);
5965  $num = $this->num;
5966  if ($num == 0) {
5967  $addcontact = (!empty($conf->global->SOCIETE_ADDRESSES_MANAGEMENT) ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
5968  print '<a href="'.DOL_URL_ROOT.'/contact/card.php?socid='.$societe->id.'&amp;action=create&amp;backtoreferer=1">'.$addcontact.'</a>';
5969  }
5970  print '</td>';
5971  print '<td class="left"><input type="submit" class="button smallpaddingimp" value="'.$langs->trans("Modify").'"></td>';
5972  print '</tr></table></form>';
5973  } else {
5974  if ($selected) {
5975  require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
5976  $contact = new Contact($this->db);
5977  $contact->fetch($selected);
5978  print $contact->getFullName($langs);
5979  } else {
5980  print "&nbsp;";
5981  }
5982  }
5983  }
5984 
5985  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6002  public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array(), $textifnothirdparty = '')
6003  {
6004  // phpcs:enable
6005  global $langs;
6006 
6007  $out = '';
6008  if ($htmlname != "none") {
6009  $out .= '<form method="post" action="'.$page.'">';
6010  $out .= '<input type="hidden" name="action" value="set_thirdparty">';
6011  $out .= '<input type="hidden" name="token" value="'.newToken().'">';
6012  $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, 0, 'minwidth100', '', '', 1, array(), false, $excludeids);
6013  $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="'.$langs->trans("Modify").'">';
6014  $out .= '</form>';
6015  } else {
6016  if ($selected) {
6017  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
6018  $soc = new Societe($this->db);
6019  $soc->fetch($selected);
6020  $out .= $soc->getNomUrl(0, '');
6021  } else {
6022  $out .= '<span class="opacitymedium">'.$textifnothirdparty.'</span>';
6023  }
6024  }
6025 
6026  if ($nooutput) {
6027  return $out;
6028  } else {
6029  print $out;
6030  }
6031 
6032  return '';
6033  }
6034 
6035  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6044  public function select_currency($selected = '', $htmlname = 'currency_id')
6045  {
6046  // phpcs:enable
6047  print $this->selectCurrency($selected, $htmlname);
6048  }
6049 
6059  public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0, $useempty = '')
6060  {
6061  global $conf, $langs, $user;
6062 
6063  $langs->loadCacheCurrencies('');
6064 
6065  $out = '';
6066 
6067  if ($selected == 'euro' || $selected == 'euros') {
6068  $selected = 'EUR'; // Pour compatibilite
6069  }
6070 
6071  $out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="'.$htmlname.'" id="'.$htmlname.'">';
6072  if ($useempty) {
6073  $out .= '<option value="-1" selected></option>';
6074  }
6075  foreach ($langs->cache_currencies as $code_iso => $currency) {
6076  $labeltoshow = $currency['label'];
6077  if ($mode == 1) {
6078  $labeltoshow .= ' <span class="opacitymedium">('.$code_iso.')</span>';
6079  } else {
6080  $labeltoshow .= ' <span class="opacitymedium">('.$langs->getCurrencySymbol($code_iso).')</span>';
6081  }
6082 
6083  if ($selected && $selected == $code_iso) {
6084  $out .= '<option value="'.$code_iso.'" selected data-html="'.dol_escape_htmltag($labeltoshow).'">';
6085  } else {
6086  $out .= '<option value="'.$code_iso.'" data-html="'.dol_escape_htmltag($labeltoshow).'">';
6087  }
6088  $out .= $labeltoshow;
6089  $out .= '</option>';
6090  }
6091  $out .= '</select>';
6092  if ($user->admin) {
6093  $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
6094  }
6095 
6096  // Make select dynamic
6097  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6098  $out .= ajax_combobox($htmlname);
6099 
6100  return $out;
6101  }
6102 
6115  public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false, $morecss = '')
6116  {
6117  global $conf, $langs;
6118 
6119  $langs->loadCacheCurrencies(''); // Load ->cache_currencies
6120 
6121  $TCurrency = array();
6122 
6123  $sql = "SELECT code FROM ".$this->db->prefix()."multicurrency";
6124  $sql .= " WHERE entity IN ('".getEntity('mutlicurrency')."')";
6125  if ($filter) {
6126  $sql .= " AND ".$filter;
6127  }
6128  $resql = $this->db->query($sql);
6129  if ($resql) {
6130  while ($obj = $this->db->fetch_object($resql)) {
6131  $TCurrency[$obj->code] = $obj->code;
6132  }
6133  }
6134 
6135  $out = '';
6136  $out .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'" id="'.$htmlname.'">';
6137  if ($useempty) {
6138  $out .= '<option value="">&nbsp;</option>';
6139  }
6140  // If company current currency not in table, we add it into list. Should always be available.
6141  if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
6142  $TCurrency[$conf->currency] = $conf->currency;
6143  }
6144  if (count($TCurrency) > 0) {
6145  foreach ($langs->cache_currencies as $code_iso => $currency) {
6146  if (isset($TCurrency[$code_iso])) {
6147  if (!empty($selected) && $selected == $code_iso) {
6148  $out .= '<option value="'.$code_iso.'" selected="selected">';
6149  } else {
6150  $out .= '<option value="'.$code_iso.'">';
6151  }
6152 
6153  $out .= $currency['label'];
6154  $out .= ' ('.$langs->getCurrencySymbol($code_iso).')';
6155  $out .= '</option>';
6156  }
6157  }
6158  }
6159 
6160  $out .= '</select>';
6161 
6162  // Make select dynamic
6163  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6164  $out .= ajax_combobox($htmlname);
6165 
6166  return $out;
6167  }
6168 
6169  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6176  public function load_cache_vatrates($country_code)
6177  {
6178  // phpcs:enable
6179  global $langs;
6180 
6181  $num = count($this->cache_vatrates);
6182  if ($num > 0) {
6183  return $num; // Cache already loaded
6184  }
6185 
6186  dol_syslog(__METHOD__, LOG_DEBUG);
6187 
6188  $sql = "SELECT DISTINCT t.rowid, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly";
6189  $sql .= " FROM ".$this->db->prefix()."c_tva as t, ".$this->db->prefix()."c_country as c";
6190  $sql .= " WHERE t.fk_pays = c.rowid";
6191  $sql .= " AND t.active > 0";
6192  $sql .= " AND c.code IN (".$this->db->sanitize($country_code, 1).")";
6193  $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
6194 
6195  $resql = $this->db->query($sql);
6196  if ($resql) {
6197  $num = $this->db->num_rows($resql);
6198  if ($num) {
6199  for ($i = 0; $i < $num; $i++) {
6200  $obj = $this->db->fetch_object($resql);
6201  $this->cache_vatrates[$i]['rowid'] = $obj->rowid;
6202  $this->cache_vatrates[$i]['code'] = $obj->code;
6203  $this->cache_vatrates[$i]['txtva'] = $obj->taux;
6204  $this->cache_vatrates[$i]['nprtva'] = $obj->recuperableonly;
6205  $this->cache_vatrates[$i]['localtax1'] = $obj->localtax1;
6206  $this->cache_vatrates[$i]['localtax1_type'] = $obj->localtax1_type;
6207  $this->cache_vatrates[$i]['localtax2'] = $obj->localtax2;
6208  $this->cache_vatrates[$i]['localtax2_type'] = $obj->localtax1_type;
6209 
6210  $this->cache_vatrates[$i]['label'] = $obj->taux.'%'.($obj->code ? ' ('.$obj->code.')' : ''); // Label must contains only 0-9 , . % or *
6211  $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
6212  $positiverates = '';
6213  if ($obj->taux) {
6214  $positiverates .= ($positiverates ? '/' : '').$obj->taux;
6215  }
6216  if ($obj->localtax1) {
6217  $positiverates .= ($positiverates ? '/' : '').$obj->localtax1;
6218  }
6219  if ($obj->localtax2) {
6220  $positiverates .= ($positiverates ? '/' : '').$obj->localtax2;
6221  }
6222  if (empty($positiverates)) {
6223  $positiverates = '0';
6224  }
6225  $this->cache_vatrates[$i]['labelpositiverates'] = $positiverates.($obj->code ? ' ('.$obj->code.')' : ''); // Must never be used as key, only label
6226  }
6227 
6228  return $num;
6229  } else {
6230  $this->error = '<span class="error">'.$langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code).'</span>';
6231  return -1;
6232  }
6233  } else {
6234  $this->error = '<span class="error">'.$this->db->error().'</span>';
6235  return -2;
6236  }
6237  }
6238 
6239  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6261  public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = '', $societe_acheteuse = '', $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0)
6262  {
6263  // phpcs:enable
6264  global $langs, $conf, $mysoc;
6265 
6266  $langs->load('errors');
6267 
6268  $return = '';
6269 
6270  // Define defaultnpr, defaultttx and defaultcode
6271  $defaultnpr = ($info_bits & 0x01);
6272  $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
6273  $defaulttx = str_replace('*', '', $selectedrate);
6274  $defaultcode = '';
6275  $reg = array();
6276  if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
6277  $defaultcode = $reg[1];
6278  $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
6279  }
6280  //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
6281 
6282  // Check parameters
6283  if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
6284  if ($societe_vendeuse->id == $mysoc->id) {
6285  $return .= '<span class="error">'.$langs->trans("ErrorYourCountryIsNotDefined").'</span>';
6286  } else {
6287  $return .= '<span class="error">'.$langs->trans("ErrorSupplierCountryIsNotDefined").'</span>';
6288  }
6289  return $return;
6290  }
6291 
6292  //var_dump($societe_acheteuse);
6293  //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";
6294  //exit;
6295 
6296  // Define list of countries to use to search VAT rates to show
6297  // First we defined code_country to use to find list
6298  if (is_object($societe_vendeuse)) {
6299  $code_country = "'".$societe_vendeuse->country_code."'";
6300  } else {
6301  $code_country = "'".$mysoc->country_code."'"; // Pour compatibilite ascendente
6302  }
6303  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) { // If option to have vat for end customer for services is on
6304  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6305  if (!isInEEC($societe_vendeuse) && (!is_object($societe_acheteuse) || (isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()))) {
6306  // We also add the buyer country code
6307  if (is_numeric($type)) {
6308  if ($type == 1) { // We know product is a service
6309  $code_country .= ",'".$societe_acheteuse->country_code."'";
6310  }
6311  } elseif (!$idprod) { // We don't know type of product
6312  $code_country .= ",'".$societe_acheteuse->country_code."'";
6313  } else {
6314  $prodstatic = new Product($this->db);
6315  $prodstatic->fetch($idprod);
6316  if ($prodstatic->type == Product::TYPE_SERVICE) { // We know product is a service
6317  $code_country .= ",'".$societe_acheteuse->country_code."'";
6318  }
6319  }
6320  }
6321  }
6322 
6323  // Now we get list
6324  $num = $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
6325 
6326  if ($num > 0) {
6327  // Definition du taux a pre-selectionner (si defaulttx non force et donc vaut -1 ou '')
6328  if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
6329  $tmpthirdparty = new Societe($this->db);
6330 
6331  $defaulttx = get_default_tva($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
6332  $defaultnpr = get_default_npr($societe_vendeuse, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
6333 
6334  if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
6335  $defaultcode = $reg[1];
6336  $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
6337  }
6338  if (empty($defaulttx)) {
6339  $defaultnpr = 0;
6340  }
6341  }
6342 
6343  // If we fails to find a default vat rate, we take the last one in list
6344  // Because they are sorted in ascending order, the last one will be the higher one (we suppose the higher one is the current rate)
6345  if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
6346  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6347  // We take the last one found in list
6348  $defaulttx = $this->cache_vatrates[$num - 1]['txtva'];
6349  } else {
6350  // We will use the rate defined into MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS
6351  $defaulttx = '';
6352  if ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS != 'none') {
6353  $defaulttx = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;
6354  }
6355  if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
6356  $defaultcode = $reg[1];
6357  $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
6358  }
6359  }
6360  }
6361 
6362  // Disabled if seller is not subject to VAT
6363  $disabled = false;
6364  $title = '';
6365  if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && $societe_vendeuse->tva_assuj == "0") {
6366  // Override/enable VAT for expense report regardless of global setting - needed if expense report used for business expenses instead
6367  // of using supplier invoices (this is a very bad idea !)
6368  if (empty($conf->global->EXPENSEREPORT_OVERRIDE_VAT)) {
6369  $title = ' title="'.dol_escape_htmltag($langs->trans('VATIsNotUsed')).'"';
6370  $disabled = true;
6371  }
6372  }
6373 
6374  if (!$options_only) {
6375  $return .= '<select class="flat minwidth50imp maxwidth100" id="'.$htmlname.'" name="'.$htmlname.'"'.($disabled ? ' disabled' : '').$title.'>';
6376  }
6377 
6378  $selectedfound = false;
6379  foreach ($this->cache_vatrates as $rate) {
6380  // Keep only 0 if seller is not subject to VAT
6381  if ($disabled && $rate['txtva'] != 0) {
6382  continue;
6383  }
6384 
6385  // Define key to use into select list
6386  $key = $rate['txtva'];
6387  $key .= $rate['nprtva'] ? '*' : '';
6388  if ($mode > 0 && $rate['code']) {
6389  $key .= ' ('.$rate['code'].')';
6390  }
6391  if ($mode < 0) {
6392  $key = $rate['rowid'];
6393  }
6394 
6395  $return .= '<option value="'.$key.'"';
6396  if (!$selectedfound) {
6397  if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
6398  if ($defaultcode == $rate['code']) {
6399  $return .= ' selected';
6400  $selectedfound = true;
6401  }
6402  } elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
6403  $return .= ' selected';
6404  $selectedfound = true;
6405  }
6406  }
6407  $return .= '>';
6408 
6409  // Show label of VAT
6410  if ($mysoc->country_code == 'IN' || !empty($conf->global->MAIN_VAT_LABEL_IS_POSITIVE_RATES)) {
6411  // Label with all localtax and code. For example: x.y / a.b / c.d (CODE)'
6412  $return .= $rate['labelpositiverates'];
6413  } else {
6414  // Simple label
6415  $return .= vatrate($rate['label']);
6416  }
6417 
6418  //$return.=($rate['code']?' '.$rate['code']:'');
6419  $return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the * (old behaviour only if new vat code is not used)
6420 
6421  $return .= '</option>';
6422  }
6423 
6424  if (!$options_only) {
6425  $return .= '</select>';
6426  //$return .= ajax_combobox($htmlname); // This break for the moment the dynamic autoselection of a value when selecting a product in object lines
6427  }
6428  } else {
6429  $return .= $this->error;
6430  }
6431 
6432  $this->num = $num;
6433  return $return;
6434  }
6435 
6436 
6437  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6462  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 = '')
6463  {
6464  // phpcs:enable
6465  $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
6466  if (!empty($nooutput)) {
6467  return $retstring;
6468  }
6469  print $retstring;
6470 
6471  return '';
6472&