dolibarr 25.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-2026 Alexandre Spangaro <alexandre@inovea-conseil.com>
19 * Copyright (C) 2018-2022 Ferran Marcet <fmarcet@2byte.es>
20 * Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
21 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
22 * Copyright (C) 2018 Christophe Battarel <christophe@altairis.fr>
23 * Copyright (C) 2018 Josep Lluis Amador <joseplluis@lliuretic.cat>
24 * Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
25 * Copyright (C) 2023 Nick Fragoulis
26 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
27 * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
28 * Copyright (C) 2026 Lenin Rivas <lenin.rivas777@gmail.com>
29 * Copyright (C) 2026 Open-Dsi <support@open-dsi.fr>
30 * Copyright (C) 2026 Jose MARTINEZ <jose.martinez@pichinov.com>
31 *
32 * This program is free software; you can redistribute it and/or modify
33 * it under the terms of the GNU General Public License as published by
34 * the Free Software Foundation; either version 3 of the License, or
35 * (at your option) any later version.
36 *
37 * This program is distributed in the hope that it will be useful,
38 * but WITHOUT ANY WARRANTY; without even the implied warranty of
39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40 * GNU General Public License for more details.
41 *
42 * You should have received a copy of the GNU General Public License
43 * along with this program. If not, see <https://www.gnu.org/licenses/>.
44 */
45
59class Form
60{
64 public $db;
65
69 public $error = '';
70
74 public $errors = array();
75
76 // Some properties used to return data by some methods
78 public $result;
79
81 public $num;
82
83 // Cache arrays
85 public $cache_types_paiements = array();
87 public $cache_conditions_paiements = array();
89 public $cache_transport_mode = array();
91 public $cache_availability = array();
93 public $cache_demand_reason = array();
95 public $cache_types_fees = array();
97 public $cache_vatrates = array();
99 public $cache_invoice_subtype = array();
101 public $cache_rule_for_lines_dates = array();
102
106 private $phoneInputSharedJsLoaded = false;
107
108
114 public function __construct($db)
115 {
116 $this->db = $db;
117 }
118
127 public function getDurationTypes(Translate $langs, $plurial = true, $reverse = false)
128 {
129 if ($plurial) {
130 $arrayoftypes = [
131 'y' => $langs->trans('Years'),
132 'm' => $langs->trans('Month'),
133 'w' => $langs->trans('Weeks'),
134 'd' => $langs->trans('Days'),
135 'h' => $langs->trans('Hours'),
136 'i' => $langs->trans('Minutes'),
137 's' => $langs->trans('Seconds'),
138 ];
139 } else {
140 $arrayoftypes = [
141 "y" => $langs->trans("Year"),
142 "m" => $langs->trans("Month"),
143 "w" => $langs->trans("Week"),
144 "d" => $langs->trans("Day"),
145 "h" => $langs->trans("Hour"),
146 "i" => $langs->trans("Minute"),
147 's' => $langs->trans('Second'),
148 ];
149 }
150 if ($reverse) {
151 return array_reverse($arrayoftypes);
152 } else {
153 return $arrayoftypes;
154 }
155 }
156
173 public function editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata = 'string', $moreparam = '', $fieldrequired = 0, $notabletag = 0, $paramid = 'id', $help = '')
174 {
175 global $langs;
176
177 $ret = '';
178
179 // TODO change for compatibility
180 if (getDolGlobalString('MAIN_USE_EDIT_IN_PLACE') && !preg_match('/^select;/', $typeofdata)) {
181 if ($perm) {
182 $tmp = explode(':', $typeofdata);
183 $ret .= '<div class="editkey_' . $tmp[0] . (!empty($tmp[1]) ? ' ' . $tmp[1] : '') . '" id="' . $htmlname . '">';
184 if ($fieldrequired) {
185 $ret .= '<span class="fieldrequired">';
186 }
187 if ($help) {
188 $ret .= $this->textwithpicto($langs->trans($text), $help);
189 } else {
190 $ret .= $langs->trans($text);
191 }
192 if ($fieldrequired) {
193 $ret .= '</span>';
194 }
195 $ret .= '</div>' . "\n";
196 } else {
197 if ($fieldrequired) {
198 $ret .= '<span class="fieldrequired">';
199 }
200 if ($help) {
201 $ret .= $this->textwithpicto($langs->trans($text), $help);
202 } else {
203 $ret .= $langs->trans($text);
204 }
205 if ($fieldrequired) {
206 $ret .= '</span>';
207 }
208 }
209 } else {
210 if (empty($notabletag) && $perm) {
211 $ret .= '<table class="nobordernopadding centpercent"><tr><td class="nowrap">';
212 }
213 if ($fieldrequired) {
214 $ret .= '<span class="fieldrequired">';
215 }
216 if ($help) {
217 $ret .= $this->textwithpicto($langs->trans($text), $help);
218 } else {
219 $ret .= $langs->trans($text);
220 }
221 if ($fieldrequired) {
222 $ret .= '</span>';
223 }
224 if (!empty($notabletag)) {
225 $ret .= ' ';
226 }
227 if (empty($notabletag) && $perm) {
228 $ret .= '</td>';
229 }
230 if (empty($notabletag) && $perm) {
231 $ret .= '<td class="right">';
232 }
233 if ($htmlname && GETPOST('action', 'aZ09') != 'edit' . $htmlname && $perm && is_object($object)) {
234 $ret .= '<a class="editfielda reposition" href="' . dolBuildUrl($_SERVER["PHP_SELF"], ['action' => 'edit' . $htmlname, $paramid => $object->id], true) . $moreparam . '">';
235 $ret .= img_edit($langs->trans('Edit'), ($notabletag ? 0 : 1));
236 $ret .= '</a>';
237 }
238 if (!empty($notabletag) && $notabletag == 1) {
239 if ($text) {
240 $ret .= ' : ';
241 } else {
242 $ret .= ' ';
243 }
244 }
245 if (!empty($notabletag) && $notabletag == 3) {
246 $ret .= ' ';
247 }
248 if (empty($notabletag) && $perm) {
249 $ret .= '</td>';
250 }
251 if (empty($notabletag) && $perm) {
252 $ret .= '</tr></table>';
253 }
254 }
255
256 return $ret;
257 }
258
282 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 = '')
283 {
284 global $conf, $langs;
285
286 $ret = '';
287
288 // Check parameters
289 if (empty($typeofdata)) {
290 return 'ErrorBadParameter typeofdata is empty';
291 }
292 // Clean parameter $typeofdata
293 if ($typeofdata == 'datetime') {
294 $typeofdata = 'dayhour';
295 }
296 if ($typeofdata == 'date') {
297 $typeofdata = 'day';
298 }
299 $reg = array();
300 if (preg_match('/^(\w+)\‍((\d+)\‍)$/', $typeofdata, $reg)) {
301 if ($reg[1] == 'varchar') {
302 $typeofdata = 'string';
303 } elseif ($reg[1] == 'int') {
304 $typeofdata = 'numeric';
305 } else {
306 return 'ErrorBadParameter ' . $typeofdata;
307 }
308 }
309
310 // When option to edit inline is activated
311 if (getDolGlobalString('MAIN_USE_EDIT_IN_PLACE') && !preg_match('/^select;|day|datepicker|dayhour|datehourpicker/', $typeofdata)) { // TODO add jquery timepicker and support select
312 $ret .= $this->editInPlace($object, $value, $htmlname, ($perm ? 1 : 0), $typeofdata, $editvalue, $extObject, $custommsg);
313 } else {
314 if ($editaction == '') {
315 $editaction = GETPOST('action', 'aZ09');
316 }
317 $editmode = ($editaction == 'edit' . $htmlname);
318 if ($editmode) { // edit mode
319 $ret .= "<!-- formeditfieldval -->\n";
320 $ret .= '<form method="post" action="' . $_SERVER["PHP_SELF"] . ($moreparam ? '?' . $moreparam : '') . '">';
321 $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
322 $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
323 $ret .= '<input type="hidden" name="' . $paramid . '" value="' . $object->id . '">';
324 if (empty($notabletag)) {
325 $ret .= '<table class="nobordernopadding centpercent">';
326 }
327 if (empty($notabletag)) {
328 $ret .= '<tr><td>';
329 }
330 if (preg_match('/^(string|safehtmlstring|email|phone|url)/', $typeofdata)) {
331 $tmp = explode(':', $typeofdata);
332 $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($editvalue ? $editvalue : $value) . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus spellcheck="false">';
333 } elseif (preg_match('/^(integer)/', $typeofdata)) {
334 $tmp = explode(':', $typeofdata);
335 $valuetoshow = price2num($editvalue ? $editvalue : $value, 0);
336 $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . $valuetoshow . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
337 } elseif (preg_match('/^(numeric|amount)/', $typeofdata)) {
338 $tmp = explode(':', $typeofdata);
339 $valuetoshow = price2num($editvalue ? $editvalue : $value);
340 $ret .= '<input type="text" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($valuetoshow != '' ? price($valuetoshow) : '') . '"' . (empty($tmp[1]) ? '' : ' size="' . $tmp[1] . '"') . ' autofocus>';
341 } elseif (preg_match('/^(checkbox)/', $typeofdata)) {
342 $tmp = explode(':', $typeofdata);
343 $ret .= '<input type="checkbox" id="' . $htmlname . '" name="' . $htmlname . '" value="' . ($value ? $value : 'on') . '"' . ($value ? ' checked' : '') . (empty($tmp[1]) ? '' : $tmp[1]) . '/>';
344 } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) { // if wysiwyg is enabled $typeofdata = 'ckeditor'
345 $tmp = explode(':', $typeofdata);
346 $cols = (empty($tmp[2]) ? '' : $tmp[2]);
347 $morealt = '';
348 if (preg_match('/%/', $cols)) {
349 $morealt = ' style="width: ' . $cols . '"';
350 $cols = '';
351 }
352 $valuetoshow = ($editvalue ? $editvalue : $value);
353 $ret .= '<textarea id="' . $htmlname . '" name="' . $htmlname . '" wrap="soft" rows="' . (empty($tmp[1]) ? '20' : $tmp[1]) . '"' . ($cols ? ' cols="' . $cols . '"' : 'class="quatrevingtpercent"') . $morealt . '" autofocus>';
354 // textarea convert automatically entities chars into simple chars.
355 // So we convert & into &amp; so a string like 'a &lt; <b>b</b><br>é<br>&lt;script&gt;alert('X');&lt;script&gt;' stay a correct html and is not converted by textarea component when wysiwyg is off.
356 $valuetoshow = str_replace('&', '&amp;', $valuetoshow);
357 $ret .= dol_htmlwithnojs(dol_string_neverthesehtmltags($valuetoshow, array('textarea')));
358 $ret .= '</textarea><div class="clearboth"></div>';
359 } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
360 $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
361 $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
362 $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
363 $ret .= $this->selectDate($value, $htmlname, 0, 0, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
364 } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
365 $addnowlink = empty($moreoptions['addnowlink']) ? 0 : $moreoptions['addnowlink'];
366 $adddateof = empty($moreoptions['adddateof']) ? '' : $moreoptions['adddateof'];
367 $labeladddateof = empty($moreoptions['labeladddateof']) ? '' : $moreoptions['labeladddateof'];
368 $ret .= $this->selectDate($value, $htmlname, 1, 1, 1, 'form' . $htmlname, 1, $addnowlink, 0, '', '', $adddateof, '', 1, $labeladddateof, '', $gm);
369 } elseif (preg_match('/^select;/', $typeofdata)) {
370 $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
371 $arraylist = array();
372 foreach ($arraydata as $val) {
373 $tmp = explode(':', $val);
374 $tmpkey = str_replace('|', ':', $tmp[0]);
375 $arraylist[$tmpkey] = $tmp[1];
376 }
377 $ret .= $this->selectarray($htmlname, $arraylist, $value);
378 } elseif (preg_match('/^link/', $typeofdata)) {
379 // TODO Not yet implemented. See code for extrafields
380 } elseif (preg_match('/^ckeditor/', $typeofdata)) {
381 $tmp = explode(':', $typeofdata); // Example: ckeditor:dolibarr_zzz:width:height:savemethod:toolbarstartexpanded:rows:cols:uselocalbrowser
382 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
383 $doleditor = new DolEditor($htmlname, ($editvalue ? $editvalue : $value), (empty($tmp[2]) ? '' : $tmp[2]), (empty($tmp[3]) ? 100 : (int) $tmp[3]), (empty($tmp[1]) ? 'dolibarr_notes' : $tmp[1]), 'In', (empty($tmp[5]) ? false : (bool) $tmp[5]), (isset($tmp[8]) ? ($tmp[8] ? true : false) : true), true, (empty($tmp[6]) ? 20 : (int) $tmp[6]), (empty($tmp[7]) ? '100' : $tmp[7]));
384 $ret .= $doleditor->Create(1);
385 } elseif ($typeofdata == 'asis') {
386 $ret .= ($editvalue ? $editvalue : $value);
387 }
388 if (empty($notabletag)) {
389 $ret .= '</td>';
390 }
391
392 // Button save-cancel
393 if (empty($notabletag)) {
394 $ret .= '<td>';
395 }
396 //else $ret.='<div class="clearboth"></div>';
397 $ret .= '<input type="submit" class="smallpaddingimp nomargingtop nomarginbottom button' . (empty($notabletag) ? '' : ' ') . '" name="modify" value="' . $langs->trans("Save") . '">';
398 if (preg_match('/ckeditor|textarea/', $typeofdata) && empty($notabletag)) {
399 $ret .= '<br>' . "\n";
400 }
401 $ret .= '<input type="submit" class="smallpaddingimp nomargingtop nomarginbottom button button-cancel' . (empty($notabletag) ? '' : ' ') . '" name="cancel" value="' . $langs->trans("Cancel") . '">';
402 if (empty($notabletag)) {
403 $ret .= '</td>';
404 }
405
406 if (empty($notabletag)) {
407 $ret .= '</tr></table>' . "\n";
408 }
409 $ret .= '</form>' . "\n";
410 } else { // view mode
411 if (preg_match('/^email/', $typeofdata)) {
412 $ret .= dol_print_email($value, 0, 0, 0, 0, 1);
413 } elseif (preg_match('/^phone/', $typeofdata)) {
414 $ret .= dol_print_phone($value, '_blank', 32, 1);
415 } elseif (preg_match('/^url/', $typeofdata)) {
416 $ret .= dol_print_url($value, '_blank', 32, 1);
417 } elseif (preg_match('/^(amount|numeric)/', $typeofdata)) {
418 $ret .= ($value != '' ? price($value, 0, $langs, 0, -1, -1, $conf->currency) : '');
419 } elseif (preg_match('/^checkbox/', $typeofdata)) {
420 $tmp = explode(':', $typeofdata);
421 $ret .= '<input type="checkbox" disabled id="' . $htmlname . '" name="' . $htmlname . '" value="' . $value . '"' . ($value ? ' checked' : '') . ($tmp[1] ? $tmp[1] : '') . '/>';
422 } elseif (preg_match('/^text/', $typeofdata) || preg_match('/^note/', $typeofdata)) {
424 } elseif (preg_match('/^(safehtmlstring|restricthtml)/', $typeofdata)) { // 'restricthtml' is not an allowed type for editfieldval. Value is 'safehtmlstring'
426 } elseif ($typeofdata == 'day' || $typeofdata == 'datepicker') {
427 $ret .= '<span class="valuedate">' . dol_print_date($value, 'day', $gm) . '</span>';
428 } elseif ($typeofdata == 'dayhour' || $typeofdata == 'datehourpicker') {
429 $ret .= '<span class="valuedate">' . dol_print_date($value, 'dayhour', $gm) . '</span>';
430 } elseif (preg_match('/^select;/', $typeofdata)) {
431 $arraydata = explode(',', preg_replace('/^select;/', '', $typeofdata));
432 $arraylist = array();
433 foreach ($arraydata as $val) {
434 $tmp = explode(':', $val);
435 $arraylist[$tmp[0]] = $tmp[1];
436 }
437 $ret .= $arraylist[$value];
438 if ($htmlname == 'fk_product_type') {
439 if ($value == 0) {
440 $ret = img_picto($langs->trans("Product"), 'product', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
441 } else {
442 $ret = img_picto($langs->trans("Service"), 'service', 'class="paddingleftonly paddingrightonly colorgrey"') . $ret;
443 }
444 }
445 } elseif (preg_match('/^ckeditor/', $typeofdata)) {
446 $tmpcontent = dol_htmlentitiesbr($value);
447 if (getDolGlobalString('MAIN_DISABLE_NOTES_TAB')) {
448 $firstline = preg_replace('/<br>.*/', '', $tmpcontent);
449 $firstline = preg_replace('/[\n\r].*/', '', $firstline);
450 $tmpcontent = $firstline . ((strlen($firstline) != strlen($tmpcontent)) ? '...' : '');
451 }
452 // We don't use dol_escape_htmltag to get the html formatting active, but this need we must also
453 // clean data from some dangerous html
455 } else {
456 if (empty($moreoptions['valuealreadyhtmlescaped'])) {
457 $ret .= dol_escape_htmltag($value);
458 } else {
459 $ret .= $value; // $value must be already html escaped.
460 }
461 }
462
463 // Custom format if parameter $formatfunc has been provided
464 if ($formatfunc && method_exists($object, $formatfunc)) {
465 $ret = $object->$formatfunc($ret);
466 }
467 }
468 }
469 return $ret;
470 }
471
483 public function widgetForTranslation($fieldname, $object, $perm, $typeofdata = 'string', $check = '', $morecss = '')
484 {
485 global $conf, $langs, $extralanguages;
486
487 $result = '';
488
489 // List of extra languages
490 $arrayoflangcode = array();
491 if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
492 $arrayoflangcode[] = getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE');
493 }
494
495 if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
496 if (!is_object($extralanguages)) {
497 include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
498 $extralanguages = new ExtraLanguages($this->db);
499 }
500 $extralanguages->fetch_name_extralanguages('societe');
501
502 // ExtraLanguages::fetch_name_extralanguages() leaves $this->attributes empty
503 // when MAIN_USE_ALTERNATE_TRANSLATION_FOR is not configured, so PHP 8 raises
504 // 'Undefined array key' on the read below if we do not guard it (issue #34596).
505 if (empty($extralanguages->attributes[$object->element]) || !is_array($extralanguages->attributes[$object->element]) || empty($extralanguages->attributes[$object->element][$fieldname])) {
506 return ''; // No extralang field to show
507 }
508
509 $result .= '<!-- Widget for translation -->' . "\n";
510 $result .= '<div class="inline-block paddingleft image-' . $object->element . '-' . $fieldname . '">';
511 $s = img_picto($langs->trans("ShowOtherLanguages"), 'language', '', 0, 0, 0, '', 'fa-15 editfieldlang');
512 $result .= $s;
513 $result .= '</div>';
514
515 $result .= '<div class="inline-block hidden field-' . $object->element . '-' . $fieldname . '">';
516
517 $resultforextrlang = '';
518 foreach ($arrayoflangcode as $langcode) {
519 $valuetoshow = GETPOSTISSET('field-' . $object->element . "-" . $fieldname . "-" . $langcode) ? GETPOST('field-' . $object->element . '-' . $fieldname . "-" . $langcode, $check) : '';
520 if (empty($valuetoshow)) {
521 $object->fetchValuesForExtraLanguages();
522 //var_dump($object->array_languages);
523 $valuetoshow = $object->array_languages[$fieldname][$langcode];
524 }
525
526 $s = picto_from_langcode($langcode, 'class="pictoforlang paddingright"');
527 $resultforextrlang .= $s;
528
529 // TODO Use the showInputField() method of ExtraLanguages object
530 if ($typeofdata == 'textarea') {
531 $resultforextrlang .= '<textarea name="field-' . $object->element . "-" . $fieldname . "-" . $langcode . '" id="' . $fieldname . "-" . $langcode . '" class="' . $morecss . '" rows="' . ROWS_2 . '" wrap="soft">';
532 $resultforextrlang .= $valuetoshow;
533 $resultforextrlang .= '</textarea>';
534 } else {
535 $resultforextrlang .= '<input type="text" class="inputfieldforlang ' . ($morecss ? ' ' . $morecss : '') . '" name="field-' . $object->element . '-' . $fieldname . '-' . $langcode . '" value="' . $valuetoshow . '">';
536 }
537 }
538 $result .= $resultforextrlang;
539
540 $result .= '</div>';
541 $result .= '<script nonce="' . getNonce() . '">$(".image-' . $object->element . '-' . $fieldname . '").click(function() { console.log("Toggle lang widget"); jQuery(".field-' . $object->element . '-' . $fieldname . '").toggle(); });</script>';
542 }
543
544 return $result;
545 }
546
560 protected function editInPlace($object, $value, $htmlname, $condition, $inputType = 'textarea', $editvalue = null, $extObject = null, $custommsg = null)
561 {
562 $out = '';
563
564 // Check parameters
565 if (preg_match('/^text/', $inputType)) {
566 $value = dol_nl2br($value);
567 } elseif (preg_match('/^numeric/', $inputType)) {
568 $value = price($value);
569 } elseif ($inputType == 'day' || $inputType == 'datepicker') {
570 $value = dol_print_date($value, 'day');
571 }
572
573 if ($condition) {
574 $element = false;
575 $table_element = false;
576 $fk_element = false;
577 $loadmethod = false;
578 $savemethod = false;
579 $ext_element = false;
580 $button_only = false;
581 $inputOption = '';
582 $rows = '';
583 $cols = '';
584
585 if (is_object($object)) {
586 $element = $object->element;
587 $table_element = $object->table_element;
588 $fk_element = $object->id;
589 }
590
591 if (is_object($extObject)) {
592 $ext_element = $extObject->element;
593 }
594
595 if (preg_match('/^(string|email|numeric)/', $inputType)) {
596 $tmp = explode(':', $inputType);
597 $inputType = $tmp[0];
598 if (!empty($tmp[1])) {
599 $inputOption = $tmp[1];
600 }
601 if (!empty($tmp[2])) {
602 $savemethod = $tmp[2];
603 }
604 $out .= '<input id="width_' . $htmlname . '" value="' . $inputOption . '" type="hidden"/>' . "\n";
605 } elseif ((preg_match('/^day$/', $inputType)) || (preg_match('/^datepicker/', $inputType)) || (preg_match('/^datehourpicker/', $inputType))) {
606 $tmp = explode(':', $inputType);
607 $inputType = $tmp[0];
608 if (!empty($tmp[1])) {
609 $inputOption = $tmp[1];
610 }
611 if (!empty($tmp[2])) {
612 $savemethod = $tmp[2];
613 }
614
615 $out .= '<input id="timestamp" type="hidden"/>' . "\n"; // Use for timestamp format
616 } elseif (preg_match('/^(select|autocomplete)/', $inputType)) {
617 $tmp = explode(':', $inputType);
618 $inputType = $tmp[0];
619 $loadmethod = $tmp[1];
620 if (!empty($tmp[2])) {
621 $savemethod = $tmp[2];
622 }
623 if (!empty($tmp[3])) {
624 $button_only = true;
625 }
626 } elseif (preg_match('/^textarea/', $inputType)) {
627 $tmp = explode(':', $inputType);
628 $inputType = $tmp[0];
629 $rows = (empty($tmp[1]) ? '8' : $tmp[1]);
630 $cols = (empty($tmp[2]) ? '80' : $tmp[2]);
631 } elseif (preg_match('/^ckeditor/', $inputType)) {
632 $tmp = explode(':', $inputType);
633 $inputType = $tmp[0];
634 $toolbar = $tmp[1];
635 if (!empty($tmp[2])) {
636 $width = $tmp[2];
637 }
638 if (!empty($tmp[3])) {
639 $height = $tmp[3];
640 }
641 if (!empty($tmp[4])) {
642 $savemethod = $tmp[4];
643 }
644
645 if (isModEnabled('fckeditor')) {
646 $out .= '<input id="ckeditor_toolbar" value="' . $toolbar . '" type="hidden"/>' . "\n";
647 } else {
648 $inputType = 'textarea';
649 }
650 }
651
652 $out .= '<input id="element_' . $htmlname . '" value="' . $element . '" type="hidden"/>' . "\n";
653 $out .= '<input id="table_element_' . $htmlname . '" value="' . $table_element . '" type="hidden"/>' . "\n";
654 $out .= '<input id="fk_element_' . $htmlname . '" value="' . $fk_element . '" type="hidden"/>' . "\n";
655 $out .= '<input id="loadmethod_' . $htmlname . '" value="' . $loadmethod . '" type="hidden"/>' . "\n";
656 if (!empty($savemethod)) {
657 $out .= '<input id="savemethod_' . $htmlname . '" value="' . $savemethod . '" type="hidden"/>' . "\n";
658 }
659 if (!empty($ext_element)) {
660 $out .= '<input id="ext_element_' . $htmlname . '" value="' . $ext_element . '" type="hidden"/>' . "\n";
661 }
662 if (!empty($custommsg)) {
663 if (is_array($custommsg)) {
664 if (!empty($custommsg['success'])) {
665 $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg['success'] . '" type="hidden"/>' . "\n";
666 }
667 if (!empty($custommsg['error'])) {
668 $out .= '<input id="errormsg_' . $htmlname . '" value="' . $custommsg['error'] . '" type="hidden"/>' . "\n";
669 }
670 } else {
671 $out .= '<input id="successmsg_' . $htmlname . '" value="' . $custommsg . '" type="hidden"/>' . "\n";
672 }
673 }
674 if ($inputType == 'textarea') {
675 $out .= '<input id="textarea_' . $htmlname . '_rows" value="' . $rows . '" type="hidden"/>' . "\n";
676 $out .= '<input id="textarea_' . $htmlname . '_cols" value="' . $cols . '" type="hidden"/>' . "\n";
677 }
678 $out .= '<span id="viewval_' . $htmlname . '" class="viewval_' . $inputType . ($button_only ? ' inactive' : ' active') . '">' . $value . '</span>' . "\n";
679 $out .= '<span id="editval_' . $htmlname . '" class="editval_' . $inputType . ($button_only ? ' inactive' : ' active') . ' hideobject">' . (!empty($editvalue) ? $editvalue : $value) . '</span>' . "\n";
680 } else {
681 $out = $value;
682 }
683
684 return $out;
685 }
686
705 public function textwithtooltip($text, $htmltext, $tooltipon = 1, $direction = 0, $img = '', $extracss = '', $notabs = 3, $incbefore = '', $noencodehtmltext = 0, $tooltiptrigger = '', $forcenowrap = 0)
706 {
707 if ($incbefore) {
708 $text = $incbefore . $text;
709 }
710 if (!$htmltext) {
711 return $text;
712 }
713 $direction = (int) $direction; // For backward compatibility when $direction was set to '' instead of 0
714
715 $tag = 'td';
716 if ($notabs == 2) {
717 $tag = 'div';
718 }
719 if ($notabs == 3) {
720 $tag = 'span';
721 }
722 // Sanitize tooltip
723 $htmltext = str_replace(array("\r", "\n"), '', $htmltext);
724
725 $extrastyle = '';
726 if ($direction < 0) {
727 $extracss = ($extracss ? $extracss : '') . ($notabs != 3 ? ' inline-block' : '');
728 $extrastyle = 'padding: 0px; padding-left: 2px;';
729 }
730 if ($direction > 0) {
731 $extracss = ($extracss ? $extracss : '') . ($notabs != 3 ? ' inline-block' : '');
732 $extrastyle = 'padding: 0px; padding-right: 2px;';
733 }
734
735 $classfortooltip = 'classfortooltip';
736
737 $s = '';
738 $textfordialog = '';
739
740 if ($tooltiptrigger == '') {
741 $htmltext = str_replace('"', '&quot;', $htmltext);
742 } else {
743 $classfortooltip = 'classfortooltiponclick';
744 $textfordialog .= '<div style="display: none;" id="idfortooltiponclick_' . $tooltiptrigger . '" class="classfortooltiponclicktext"';
745 // Set default title of dialog
746 global $langs;
747 if ($langs instanceof Translate) {
748 $textfordialog .= ' title="'.$langs->trans("Note").'"';
749 }
750 $textfordialog .= '>' . $htmltext . '</div>';
751 }
752 if ($tooltipon == 2 || $tooltipon == 3) {
753 $paramfortooltipimg = ' class="' . $classfortooltip . ($notabs != 3 ? ' inline-block' : '') . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '"';
754 if ($tooltiptrigger == '') {
755 $paramfortooltipimg .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1, 0, 'span', 0, 1)) . '"'; // Attribute to put on img tag to store tooltip
756 } else {
757 $paramfortooltipimg .= ' dolid="' . $tooltiptrigger . '"';
758 }
759 } else {
760 $paramfortooltipimg = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribute to put on td text tag
761 }
762 if ($tooltipon == 1 || $tooltipon == 3) {
763 $paramfortooltiptd = ' class="' . ($tooltipon == 3 ? 'cursorpointer ' : '') . $classfortooltip . ($tag != 'td' ? ' inline-block' : '') . ($extracss ? ' ' . $extracss : '') . '" style="padding: 0px;' . ($extrastyle ? ' ' . $extrastyle : '') . '" ';
764 if ($tooltiptrigger == '') {
765 $paramfortooltiptd .= ' title="' . ($noencodehtmltext ? $htmltext : dol_escape_htmltag($htmltext, 1, 0, 'span', 0, 1)) . '"'; // Attribute to put on td tag to store tooltip
766 } else {
767 $paramfortooltiptd .= ' dolid="' . $tooltiptrigger . '"';
768 }
769 } else {
770 $paramfortooltiptd = ($extracss ? ' class="' . $extracss . '"' : '') . ($extrastyle ? ' style="' . $extrastyle . '"' : ''); // Attribute to put on td text tag
771 }
772 if (empty($notabs)) {
773 $s .= '<table class="nobordernopadding"><tr style="height: auto;">';
774 } elseif ($notabs == 2) {
775 $s .= '<div class="inline-block' . ($forcenowrap ? ' nowrap' : '') . '">';
776 }
777 // Define value if value is before
778 if ($direction < 0) {
779 $s .= '<' . $tag . $paramfortooltipimg;
780 if ($tag == 'td') {
781 $s .= ' class="valigntop" width="14"';
782 }
783 $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
784 }
785 // Use another method to help avoid having a space in value in order to use this value with jquery
786 // Define label
787 if ((string) $text != '') {
788 $s .= '<' . $tag . $paramfortooltiptd . '>' . $text . '</' . $tag . '>';
789 }
790 // Define value if value is after
791 if ($direction > 0) {
792 $s .= '<' . $tag . $paramfortooltipimg;
793 if ($tag == 'td') {
794 $s .= ' class="valignmiddle" width="14"';
795 }
796 $s .= '>' . $textfordialog . $img . '</' . $tag . '>';
797 }
798 if (empty($notabs)) {
799 $s .= '</tr></table>';
800 } elseif ($notabs == 2) {
801 $s .= '</div>';
802 }
803
804 return $s;
805 }
806
821 public function textwithpicto($text, $htmltooltip, $direction = 1, $type = 'help', $extracss = 'valignmiddle', $noencodehtmltext = 0, $notabs = 3, $tooltiptrigger = '', $forcenowrap = 0)
822 {
823 global $conf, $langs;
824
825 //For backwards compatibility
826 if ($type == '0') {
827 $type = 'info';
828 } elseif ($type == '1') {
829 $type = 'help';
830 }
831 // Clean parameters
832 $tooltiptrigger = preg_replace('/[^a-z0-9]/i', '', $tooltiptrigger);
833
834 if (preg_match('/onsmartphone$/', $tooltiptrigger) && empty($conf->dol_no_mouse_hover)) {
835 $tooltiptrigger = preg_replace('/^.*onsmartphone$/', '', $tooltiptrigger);
836 }
837 $alt = '';
838 if ($tooltiptrigger) {
839 $alt = $langs->transnoentitiesnoconv("ClickToShowHelp");
840 }
841
842 // If info or help with no javascript, show only text
843 if (empty($conf->use_javascript_ajax)) {
844 if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
845 return $text;
846 } else {
847 $alt = $htmltooltip;
848 $htmltooltip = '';
849 }
850 }
851
852 // If info or help with smartphone, show only text (tooltip hover can't works)
853 if (!empty($conf->dol_no_mouse_hover) && empty($tooltiptrigger)) {
854 if ($type == 'info' || $type == 'infoclickable' || $type == 'help' || $type == 'helpclickable') {
855 return $text;
856 }
857 }
858 // If info or help with smartphone, show only text (tooltip on click does not works with dialog on smaprtphone)
859 //if (!empty($conf->dol_no_mouse_hover) && !empty($tooltiptrigger))
860 //{
861 //if ($type == 'info' || $type == 'help') return '<a href="'..'">'.$text.'</a>';
862 //}
863
864 $img = '';
865 if ($type == 'info') {
866 $img = img_help(($tooltiptrigger != '' ? 2 : 0), $alt);
867 } elseif ($type == 'help') {
868 $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
869 } elseif ($type == 'helpclickable') {
870 $img = img_help(($tooltiptrigger != '' ? 2 : 1), $alt);
871 } elseif ($type == 'warning') {
872 $img = img_warning($alt);
873 } elseif ($type != 'none') {
874 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
875 $img = img_picto($alt, $type); // $type can be an image path
876 }
877
878 $tooltipon = ((($tooltiptrigger && !$img) || strpos($type, 'clickable')) ? 3 : 2);
879
880 return $this->textwithtooltip($text, $htmltooltip, $tooltipon, $direction, $img, $extracss, $notabs, '', $noencodehtmltext, $tooltiptrigger, $forcenowrap);
881 }
882
893 public function selectMassAction($selected, $arrayofaction, $alwaysvisible = 0, $name = 'massaction', $cssclass = 'checkforselect')
894 {
895 global $conf, $langs, $hookmanager;
896
897 $disabled = 0;
898 $ret = '<div class="centpercent center">';
899 $ret .= '<select class="flat' . (empty($conf->use_javascript_ajax) ? '' : ' hideobject') . ' ' . $name . ' ' . $name . 'select valignmiddle alignstart" id="' . $name . '" name="' . $name . '"' . ($disabled ? ' disabled="disabled"' : '') . '>';
900
901 // 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.
902 $parameters = array();
903 $reshook = $hookmanager->executeHooks('addMoreMassActions', $parameters); // Note that $action and $object may have been modified by hook
904 // check if there is a mass action
905
906 if (is_array($arrayofaction) && count($arrayofaction) == 0 && empty($hookmanager->resPrint)) {
907 return;
908 }
909 if (empty($reshook)) {
910 $ret .= '<option value="0"' . ($disabled ? ' disabled="disabled"' : '') . '>-- ' . $langs->trans("SelectAction") . ' --</option>';
911 if (is_array($arrayofaction)) {
912 foreach ($arrayofaction as $code => $label) {
913 $ret .= '<option value="' . $code . '"' . ($disabled ? ' disabled="disabled"' : '') . ' data-html="' . dol_escape_htmltag($label) . '">' . $label . '</option>';
914 }
915 }
916 }
917 $ret .= $hookmanager->resPrint;
918
919 $ret .= '</select>';
920
921 if (empty($conf->dol_optimize_smallscreen)) {
922 $ret .= ajax_combobox('.' . $name . 'select');
923 }
924
925 // 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
926 $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.
927 $ret .= '<input type="submit" disabled name="confirmmassaction"' . (empty($conf->use_javascript_ajax) ? '' : ' style="display: none"') . ' class="reposition button smallpaddingimp' . (empty($conf->use_javascript_ajax) ? '' : ' hideobject') . ' ' . $name . ' ' . $name . 'confirmed" value="' . dol_escape_htmltag($langs->trans("Confirm")) . '">';
928 $ret .= '</div>';
929
930 if (!empty($conf->use_javascript_ajax)) {
931 $ret .= '<!-- JS CODE TO ENABLE mass action select -->
932 <script nonce="' . getNonce() . '">
933 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 check for select boxes */
934 atleastoneselected=0;
935 jQuery("."+cssclass).each(function( index ) {
936 /* console.log( index + ": " + $( this ).text() ); */
937 if ($(this).is(\':checked\')) atleastoneselected++;
938 });
939
940 console.log("initCheckForSelect mode="+mode+" name="+name+" cssclass="+cssclass+" atleastoneselected="+atleastoneselected);
941
942 if (atleastoneselected || ' . ((int) $alwaysvisible) . ') {
943 jQuery("."+name).show();
944 ' . ($selected ? 'if (atleastoneselected) { jQuery("."+name+"select").val("' . $selected . '").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', false); }' : '') . '
945 ' . ($selected ? 'if (! atleastoneselected) { jQuery("."+name+"select").val("0").trigger(\'change\'); jQuery("."+name+"confirmed").prop(\'disabled\', true); } ' : '') . '
946 } else {
947 jQuery("."+name).hide();
948 jQuery("."+name+"other").hide();
949 }
950 }
951
952 jQuery(document).ready(function () {
953 initCheckForSelect(0, "' . $name . '", "' . $cssclass . '");
954 jQuery(".' . $cssclass . '").change(function() {
955 console.log("A change was done on .' . $cssclass . '");
956 initCheckForSelect(1, "' . $name . '", "' . $cssclass . '");
957 });
958 jQuery(".' . $name . 'select").change(function() {
959 var massaction = $( this ).val();
960 var urlform = $( this ).closest("form").attr("action").replace("#show_files","");
961 if (massaction == "builddoc") {
962 urlform = urlform + "#show_files";
963 }
964 $( this ).closest("form").attr("action", urlform);
965 console.log("we select a mass action name=' . $name . ' massaction="+massaction+" - "+urlform);
966 /* Warning: if you set submit button to disabled, post using Enter will no more work if there is no other button */
967 if ($(this).val() != \'0\') {
968 jQuery(".' . $name . 'confirmed").prop(\'disabled\', false);
969 jQuery(".' . $name . 'other").hide(); /* To disable if another div was open */
970 jQuery(".' . $name . '"+massaction).show();
971 } else {
972 jQuery(".' . $name . 'confirmed").prop(\'disabled\', true);
973 jQuery(".' . $name . 'other").hide(); /* To disable any div open */
974 }
975 });
976 });
977 </script>
978 ';
979 }
980
981 return $ret;
982 }
983
984 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
985
1003 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, $forcecombo = 0)
1004 {
1005 // phpcs:enable
1006 global $langs, $mysoc;
1007
1008 $langs->load("dict");
1009
1010 $selected = (string) $selected;
1011
1012 $out = '';
1014 $countryArray = array();
1015 $favorite = array();
1016 $label = array();
1017 $atleastonefavorite = 0;
1018
1019 $sql = "SELECT rowid, code as code_iso, code_iso as code_iso3, label, favorite, eec";
1020 $sql .= " FROM " . $this->db->prefix() . "c_country";
1021 $sql .= " WHERE active > 0";
1022 //$sql.= " ORDER BY code ASC";
1023
1024 dol_syslog(get_class($this) . "::select_country", LOG_DEBUG);
1025
1026 $resql = $this->db->query($sql);
1027 if ($resql) {
1028 $out .= '<select id="select' . $htmlname . '" class="flat maxwidth200onsmartphone selectcountry' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" ' . $htmloption . '>';
1029 $num = $this->db->num_rows($resql);
1030 $i = 0;
1031 if ($num) {
1032 while ($i < $num) {
1033 $obj = $this->db->fetch_object($resql);
1034
1035 $countryArray[$i]
1036 = array(
1037 'rowid' => (int) $obj->rowid,
1038 'code_iso' => (string) $obj->code_iso,
1039 'code_iso3' => (string) $obj->code_iso3,
1040 'label' => (string) ($obj->code_iso && $langs->transnoentitiesnoconv("Country" . $obj->code_iso) != "Country" . $obj->code_iso ? $langs->transnoentitiesnoconv("Country" . $obj->code_iso) : ($obj->label != '-' ? $obj->label : '')),
1041 'favorite' => (string) $obj->favorite,
1042 'eec' => (string) $obj->eec,
1043 );
1044 $favorite[$i] = $obj->favorite;
1045 $label[$i] = dol_string_unaccent($countryArray[$i]['label']);
1046 $i++;
1047 }
1048
1049 if (empty($disablefavorites)) {
1050 $array1_sort_order = SORT_DESC;
1051 $array2_sort_order = SORT_ASC;
1052 array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $countryArray);
1053 } else {
1054 $countryArray = dol_sort_array($countryArray, 'label');
1055 }
1056
1057 if ($showempty) {
1058 if (is_numeric($showempty)) {
1059 $out .= '<option value="">&nbsp;</option>' . "\n";
1060 } else {
1061 $out .= '<option value="-1">' . $langs->trans($showempty) . '</option>' . "\n";
1062 }
1063 }
1064
1065 if ($addspecialentries) { // Add dedicated entries for groups of countries
1066 //if ($showempty) $out.= '<option value="" disabled class="selectoptiondisabledwhite">--------------</option>';
1067 $out .= '<option value="special_allnotme"' . ($selected == 'special_allnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
1068 $out .= '<option value="special_eec"' . ($selected == 'special_eec' ? ' selected' : '') . '>' . $langs->trans("CountriesInEEC") . '</option>';
1069 if ($mysoc->isInEEC()) {
1070 $out .= '<option value="special_eecnotme"' . ($selected == 'special_eecnotme' ? ' selected' : '') . '>' . $langs->trans("CountriesInEECExceptMe", $langs->transnoentitiesnoconv("Country" . $mysoc->country_code)) . '</option>';
1071 }
1072 $out .= '<option value="special_noteec"' . ($selected == 'special_noteec' ? ' selected' : '') . '>' . $langs->trans("CountriesNotInEEC") . '</option>';
1073 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1074 }
1075
1076 foreach ($countryArray as $row) {
1077 //if (empty($showempty) && empty($row['rowid'])) continue;
1078 if (empty($row['rowid'])) {
1079 continue;
1080 }
1081 if (is_array($exclude_country_code) && count($exclude_country_code) && in_array($row['code_iso'], $exclude_country_code)) {
1082 continue; // exclude some countries
1083 }
1084
1085 if (empty($disablefavorites) && $row['favorite'] && $row['code_iso']) {
1086 $atleastonefavorite++;
1087 }
1088 if (empty($row['favorite']) && $atleastonefavorite) {
1089 $atleastonefavorite = 0;
1090 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1091 }
1092
1093 $labeltoshow = '';
1094 if ($row['label']) {
1095 $labeltoshow .= dol_trunc($row['label'], $maxlength, 'middle');
1096 } else {
1097 $labeltoshow .= '&nbsp;';
1098 }
1099 if ($row['code_iso']) {
1100 $labeltoshow .= ' <span class="opacitymedium">(' . $row['code_iso'] . ')</span>';
1101 if (empty($hideflags)) {
1102 $tmpflag = picto_from_langcode($row['code_iso'], 'class="saturatemedium paddingrightonly"', 1);
1103 $labeltoshow = $tmpflag . ' ' . $labeltoshow;
1104 }
1105 }
1106
1107 if ($selected && $selected != '-1' && ($selected == $row['rowid'] || $selected == $row['code_iso'] || $selected == $row['code_iso3'] || $selected == $row['label'])) {
1108 $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']) . '">';
1109 } else {
1110 $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']) . '">';
1111 }
1112 $out .= dol_string_nohtmltag($labeltoshow);
1113 $out .= '</option>' . "\n";
1114 }
1115 }
1116 $out .= '</select>';
1117 } else {
1118 dol_print_error($this->db);
1119 }
1120
1121 // Make select dynamic
1122 if (empty($forcecombo)) {
1123 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1124 $out .= ajax_combobox('select' . $htmlname, array(), 0, 0, 'resolve');
1125 }
1126
1127 return $out;
1128 }
1129
1140 public function selectPhoneCode($selected = '', $htmlname = 'phone_code', $morecss = 'maxwidth150', $showempty = 0, $country_id_hint = 0)
1141 {
1142 global $langs;
1143
1144 $langs->load("dict");
1145
1146 $out = '';
1147 $codeArray = array();
1148 $favorite = array();
1149 $label = array();
1150 $atleastonefavorite = 0;
1151
1152 $sql = "SELECT rowid, code, label, phone_code, favorite, trunk_prefix";
1153 $sql .= " FROM ".$this->db->prefix()."c_country";
1154 $sql .= " WHERE active > 0 AND phone_code IS NOT NULL AND phone_code != ''";
1155
1156 dol_syslog(get_class($this)."::selectPhoneCode", LOG_DEBUG);
1157 $resql = $this->db->query($sql);
1158 if ($resql) {
1159 $num = $this->db->num_rows($resql);
1160 $i = 0;
1161 while ($i < $num) {
1162 $obj = $this->db->fetch_object($resql);
1163
1164 $translabel = ($obj->code && $langs->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langs->transnoentitiesnoconv("Country".$obj->code) : $obj->label;
1165
1166 $codeArray[$i]['rowid'] = $obj->rowid;
1167 $codeArray[$i]['code'] = $obj->code;
1168 $codeArray[$i]['label'] = $translabel;
1169 $codeArray[$i]['phone_code'] = '+'.$obj->phone_code;
1170 $codeArray[$i]['favorite'] = $obj->favorite;
1171 $codeArray[$i]['trunk_prefix'] = $obj->trunk_prefix;
1172 $favorite[$i] = $obj->favorite;
1173 $label[$i] = dol_string_unaccent($translabel);
1174 $i++;
1175 }
1176
1177 $array1_sort_order = SORT_DESC;
1178 $array2_sort_order = SORT_ASC;
1179 array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $codeArray);
1180
1181 $out .= '<select id="select'.$htmlname.'" class="flat selectphonecode'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
1182
1183 if ($showempty) {
1184 $out .= '<option value="">&nbsp;</option>'."\n";
1185 }
1186
1187 // Determine which row index to select: prefer country_id_hint match, fallback to first phone_code match
1188 $selectedIdx = -1;
1189 $firstMatchIdx = -1;
1190 if ($selected !== '') {
1191 foreach ($codeArray as $idx => $row) {
1192 if ($row['phone_code'] == $selected) {
1193 if ($firstMatchIdx < 0) {
1194 $firstMatchIdx = $idx;
1195 }
1196 if ($country_id_hint > 0 && $row['rowid'] == $country_id_hint) {
1197 $selectedIdx = $idx;
1198 break;
1199 }
1200 }
1201 }
1202 if ($selectedIdx < 0 && $firstMatchIdx >= 0) {
1203 $selectedIdx = $firstMatchIdx;
1204 }
1205 }
1206
1207 foreach ($codeArray as $idx => $row) {
1208 if (empty($row['code'])) {
1209 continue;
1210 }
1211
1212 if ($row['favorite']) {
1213 $atleastonefavorite++;
1214 }
1215 if (empty($row['favorite']) && $atleastonefavorite) {
1216 $atleastonefavorite = 0;
1217 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1218 }
1219
1220 $tmpflag = picto_from_langcode($row['code'], 'class="saturatemedium paddingrightonly"', 1);
1221
1222 // Short label for selected display: flag + country code
1223 $selectlabel = ($tmpflag ? $tmpflag.' ' : '').$row['code'];
1224
1225 // Detailed label for dropdown list: flag + country name + phone code
1226 $labeltoshow = ($tmpflag ? $tmpflag.' ' : '').$row['label'].' '.$row['phone_code'];
1227
1228 $out .= '<option value="'.dol_escape_htmltag($row['phone_code']).'"';
1229 if ($idx === $selectedIdx) {
1230 $out .= ' selected';
1231 }
1232 $out .= ' data-html="'.dol_escape_htmltag($labeltoshow).'"';
1233 $out .= ' data-select-html="'.dol_escape_htmltag($selectlabel).'"';
1234 $out .= ' data-country-id="'.((int) $row['rowid']).'"';
1235 $out .= ' data-trunk-prefix="'.dol_escape_htmltag((string) $row['trunk_prefix']).'"';
1236 $out .= '>';
1237 $out .= dol_string_nohtmltag($labeltoshow);
1238 $out .= '</option>'."\n";
1239 }
1240 $out .= '</select>';
1241 } else {
1242 dol_print_error($this->db);
1243 }
1244
1245 // Make select dynamic
1246 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1247 $out .= ajax_combobox('select'.$htmlname, array(), 0, 0, 'resolve');
1248
1249 return $out;
1250 }
1251
1268 public function showPhoneInput($phoneValue, $htmlname, $country_id_hint = 0, $picto = 'object_phoning', $morecss = 'maxwidth150', $maxlength = 0, $countrySelectorId = 'selectcountry_id')
1269 {
1270 global $mysoc;
1271
1272 include_once DOL_DOCUMENT_ROOT.'/core/lib/phone.lib.php';
1273
1274 $codename = $htmlname.'_code';
1275
1276 // Fallback country_id: use caller hint, else main company country
1277 if (empty($country_id_hint) && !empty($mysoc->country_id)) {
1278 $country_id_hint = $mysoc->country_id;
1279 }
1280
1281 // On POST re-display, read the hidden field (which contains the full phone string)
1282 if (GETPOSTISSET($htmlname)) {
1283 $fullPhone = (string) GETPOST($htmlname);
1284 } else {
1285 $fullPhone = (string) $phoneValue;
1286 }
1287
1288 // Split into code + number
1289 $parsed = dol_parse_phone($fullPhone);
1290
1291 // Resolve default phone code: parsed code if set, else from country hint
1292 $phonecode = !empty($parsed['code']) ? $parsed['code'] : dol_get_phone_code_from_country($this->db, $country_id_hint);
1293
1294 $selectedCode = $phonecode;
1295 $numberValue = $parsed['number'];
1296
1297 // Add back trunk prefix for display (e.g. "644986885" → "0644986885" for France)
1298 if ($numberValue !== '' && $selectedCode !== '') {
1299 $trunkPrefix = dol_get_trunk_prefix($this->db, $selectedCode);
1300 if ($trunkPrefix !== '' && strpos($numberValue, $trunkPrefix) !== 0) {
1301 $numberValue = $trunkPrefix.$numberValue;
1302 }
1303 }
1304
1305 // Build output: hidden field (POSTed value)
1306 $out = '<input type="hidden" name="'.dol_escape_htmltag($htmlname).'" id="'.dol_escape_htmltag($htmlname).'" value="'.dol_escape_htmltag($fullPhone).'">';
1307
1308 // Picto
1309 $out .= img_picto('', $picto, 'class="pictofixedwidth"');
1310
1311 // Phone code select (display-only name, not submitted as separate POST param)
1312 $out .= $this->selectPhoneCode($selectedCode, $codename, 'maxwidth75 phone_code_select', 0, $country_id_hint);
1313
1314 // Visible number input (no name — not POSTed)
1315 $out .= '<input type="tel" inputmode="numeric" pattern="[0-9]*" id="'.dol_escape_htmltag($htmlname).'_input" class="'.dol_escape_htmltag($morecss).'"';
1316 if ($maxlength > 0) {
1317 $out .= ' maxlength="'.$maxlength.'"';
1318 }
1319 $out .= ' value="'.dol_escape_htmltag($numberValue).'">';
1320
1321 // Per-field JS to sync hidden field
1322 $out .= $this->getPhoneInputFieldJs($htmlname, $codename);
1323
1324 // Shared JS for country-sync (output once per page)
1325 $out .= $this->getPhoneInputSharedJs($countrySelectorId);
1326
1327 return $out;
1328 }
1329
1340 private function getPhoneInputFieldJs($htmlname, $codename)
1341 {
1342 $hiddenId = dol_escape_js($htmlname);
1343 $inputId = dol_escape_js($htmlname).'_input';
1344 $selectId = 'select'.dol_escape_js($codename);
1345
1346 $out = "\n".'<script type="text/javascript">'."\n";
1347 $out .= 'jQuery(document).ready(function() {'."\n";
1348 $out .= ' function syncPhoneField_'.$hiddenId.'() {'."\n";
1349 $out .= ' var selectEl = jQuery("#'.$selectId.'");'."\n";
1350 $out .= ' var code = selectEl.val() || "";'."\n";
1351 $out .= ' var number = (jQuery("#'.$inputId.'").val() || "").replace(/[^0-9]/g, "");'."\n";
1352 $out .= ' if (code && number) {'."\n";
1353 $out .= ' var selOpt = selectEl[0] && selectEl[0].selectedOptions && selectEl[0].selectedOptions[0];'."\n";
1354 $out .= ' var trunkPrefix = selOpt ? (selOpt.getAttribute("data-trunk-prefix") || "") : "";'."\n";
1355 $out .= ' if (trunkPrefix !== "" && number.indexOf(trunkPrefix) === 0) {'."\n";
1356 $out .= ' number = number.substring(trunkPrefix.length);'."\n";
1357 $out .= ' }'."\n";
1358 $out .= ' jQuery("#'.$hiddenId.'").val(code + " " + number);'."\n";
1359 $out .= ' } else if (number) {'."\n";
1360 $out .= ' jQuery("#'.$hiddenId.'").val(number);'."\n";
1361 $out .= ' } else {'."\n";
1362 $out .= ' jQuery("#'.$hiddenId.'").val("");'."\n";
1363 $out .= ' }'."\n";
1364 $out .= ' }'."\n";
1365 $out .= ' jQuery("#'.$selectId.'").on("change", function() { syncPhoneField_'.$hiddenId.'(); });'."\n";
1366 $out .= ' jQuery("#'.$inputId.'").on("input change", function() { syncPhoneField_'.$hiddenId.'(); });'."\n";
1367 $out .= '});'."\n";
1368 $out .= '</script>'."\n";
1369
1370 return $out;
1371 }
1372
1382 private function getPhoneInputSharedJs($countrySelectorId)
1383 {
1384 if ($this->phoneInputSharedJsLoaded) {
1385 return '';
1386 }
1387 $this->phoneInputSharedJsLoaded = true;
1388
1389 $out = "\n".'<script type="text/javascript">'."\n";
1390 $out .= 'jQuery(document).ready(function() {'."\n";
1391 $out .= ' jQuery("#'.dol_escape_js($countrySelectorId).'").on("change", function() {'."\n";
1392 $out .= ' var country_id = jQuery(this).val();'."\n";
1393 $out .= ' if (country_id) {'."\n";
1394 $out .= ' jQuery.getJSON("'.DOL_URL_ROOT.'/core/ajax/getphonecode.php", {country_id: country_id, token: "'.currentToken().'"}, function(data) {'."\n";
1395 $out .= ' if (data.phone_code) {'."\n";
1396 $out .= ' jQuery(".phone_code_select").each(function() {'."\n";
1397 $out .= ' jQuery(this).val(data.phone_code).trigger("change");'."\n";
1398 $out .= ' });'."\n";
1399 $out .= ' }'."\n";
1400 $out .= ' });'."\n";
1401 $out .= ' }'."\n";
1402 $out .= ' });'."\n";
1403 $out .= '});'."\n";
1404 $out .= '</script>'."\n";
1405
1406 return $out;
1407 }
1408
1422 private function makeAddLinkToObject($object, $key, $possiblelink, $num, $resqllist)
1423 {
1424 dol_syslog(__METHOD__, LOG_DEBUG);
1425 global $langs, $form;
1426 if (empty($form)) {
1427 $form = new Form($this->db);
1428 }
1429 $htmltoenteralink = '';
1430 $i = 0;
1431
1432 // headers
1433 $htmltoenteralink .= '<tr class="liste_titre">';
1434 $htmltoenteralink .= '<td class="nowrap"></td>';
1435 $htmltoenteralink .= '<td>' . $langs->trans("Ref") . '</td>';
1436 $htmltoenteralink .= '<td>' . $langs->trans("RefCustomer") . '</td>';
1437 $htmltoenteralink .= '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
1438 $htmltoenteralink .= '<td>' . $langs->trans("Company") . '</td>';
1439 $htmltoenteralink .= '</tr>';
1440
1441 // rows with data
1442 while ($i < $num) {
1443 $objp = $this->db->fetch_object($resqllist);
1444 $alreadylinked = false;
1445 if (!empty($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key])) {
1446 if (in_array($objp->rowid, array_values($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key]))) {
1447 $alreadylinked = true;
1448 }
1449 }
1450 $htmltoenteralink .= '<tr class="oddeven">';
1451 $htmltoenteralink .= '<td>';
1452 if ($alreadylinked) {
1453 $htmltoenteralink .= img_picto('', 'link');
1454 } else {
1455 $htmltoenteralink .= '<input type="checkbox" name="idtolinkto[' . $key . '_' . $objp->rowid . ']" id="' . $key . '_' . $objp->rowid . '" value="' . $objp->rowid . '">';
1456 }
1457 $htmltoenteralink .= '</td>';
1458 $htmltoenteralink .= '<td>';
1459 if (!$alreadylinked) {
1460 $htmltoenteralink .= '<label for="' . $key . '_' . $objp->rowid . '">';
1461 }
1462 $htmltoenteralink .= $objp->ref;
1463 if (!$alreadylinked) {
1464 $htmltoenteralink .= '</label>';
1465 }
1466 $htmltoenteralink .= '</td>';
1467 $htmltoenteralink .= '<td>' . (!empty($objp->ref_client) ? $objp->ref_client : (!empty($objp->ref_supplier) ? $objp->ref_supplier : '')) . '</td>';
1468 $htmltoenteralink .= '<td class="right">';
1469 if ($possiblelink['label'] == 'LinkToContract') {
1470 $htmltoenteralink .= $form->textwithpicto('', $langs->trans("InformationOnLinkToContract")) . ' ';
1471 }
1472 $htmltoenteralink .= '<span class="amount">' . (isset($objp->total_ht) ? price($objp->total_ht) : '') . '</span>';
1473 $htmltoenteralink .= '</td>';
1474 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1475 $htmltoenteralink .= '</tr>';
1476 $i++;
1477 }
1478
1479 return $htmltoenteralink;
1480 }
1481
1496 private function makeAddLinkToAttendee($object, $key, $possiblelink, $num, $resqllist)
1497 {
1498 dol_syslog(__METHOD__, LOG_DEBUG);
1499 global $langs, $form;
1500 require_once DOL_DOCUMENT_ROOT . '/eventorganization/class/conferenceorboothattendee.class.php';
1501 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1502 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
1503 $attendeestatic = new ConferenceOrBoothAttendee($this->db);
1504 $companystatic = new Societe($this->db);
1505 $projectstatic = new Project($this->db);
1506 if (empty($form)) {
1507 $form = new Form($this->db);
1508 }
1509 $htmltoenteralink = '';
1510 $i = 0;
1511
1512 // headers
1513 $htmltoenteralink .= '<tr class="liste_titre">';
1514 $htmltoenteralink .= '<td class="nowrap"></td>';
1515 $htmltoenteralink .= '<td>' . $langs->trans("Ref") . '</td>';
1516 $htmltoenteralink .= '<td>' . $langs->trans("Name") . '</td>';
1517 $htmltoenteralink .= '<td>' . $langs->trans("Email") . '</td>';
1518 $htmltoenteralink .= '<td>' . $langs->trans("Company") . '</td>';
1519 $htmltoenteralink .= '<td>' . $langs->trans("Project") . '</td>';
1520 $htmltoenteralink .= '<td>' . $langs->trans("DateOfRegistration") . '</td>';
1521 $htmltoenteralink .= '</tr>';
1522
1523 // rows with data
1524 while ($i < $num) {
1525 $objp = $this->db->fetch_object($resqllist);
1526 $alreadylinked = false;
1527 if (!empty($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key])) {
1528 if (in_array($objp->rowid, array_values($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key]))) {
1529 $alreadylinked = true;
1530 }
1531 }
1532 $htmltoenteralink .= '<tr class="oddeven">';
1533 $htmltoenteralink .= '<td>';
1534 if ($alreadylinked) {
1535 $htmltoenteralink .= img_picto('', 'link');
1536 } else {
1537 $htmltoenteralink .= '<input type="checkbox" name="idtolinkto[' . $key . '_' . $objp->rowid . ']" id="' . $key . '_' . $objp->rowid . '" value="' . $objp->rowid . '">';
1538 }
1539 $htmltoenteralink .= '</td>';
1540 $fetchattendee = $attendeestatic->fetch($objp->rowid);
1541 if ($fetchattendee) {
1542 $htmltoenteralink .= '<td>' . $attendeestatic->getNomUrl(0). '</td>';
1543 } else {
1544 $htmltoenteralink .= '<td><label for="' . $key . '_' . $objp->rowid . '">' . $objp->ref . '</label></td>';
1545 }
1546 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1547 $htmltoenteralink .= '<td>' . $objp->email . '</td>';
1548 $fetchcompany = $companystatic->fetch($objp->socid);
1549 if ($fetchcompany) {
1550 $htmltoenteralink .= '<td>' . $companystatic->getNomUrl(0). '</td>';
1551 } else {
1552 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1553 }
1554 $fetchcproject = $projectstatic->fetch($objp->fk_project);
1555 if ($fetchcproject) {
1556 $htmltoenteralink .= '<td>' . $projectstatic->getNomUrl(0). '</td>';
1557 } else {
1558 $htmltoenteralink .= '<td>' . $objp->fk_project . '</td>';
1559 }
1560 $htmltoenteralink .= '<td>' . $objp->date_subscription . '</td>';
1561 $htmltoenteralink .= '</tr>';
1562 $i++;
1563 }
1564
1565 return $htmltoenteralink;
1566 }
1567
1568 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1569
1583 public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array(), $disableautocomplete = 0)
1584 {
1585 // phpcs:enable
1586 global $conf, $langs;
1587
1588 $langs->load("dict");
1589
1590 $out = '';
1591 //$moreattrib = '';
1592 $incotermArray = array();
1593
1594 $sql = "SELECT rowid, code";
1595 $sql .= " FROM " . $this->db->prefix() . "c_incoterms";
1596 $sql .= " WHERE active > 0";
1597 $sql .= " ORDER BY code ASC";
1598
1599 dol_syslog(get_class($this) . "::select_incoterm", LOG_DEBUG);
1600 $resql = $this->db->query($sql);
1601 if ($resql) {
1602 if ($conf->use_javascript_ajax && !$forcecombo) {
1603 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1604 $out .= ajax_combobox($htmlname, $events);
1605 }
1606
1607 if (!empty($page)) {
1608 $out .= '<form method="post" action="' . $page . '">';
1609 $out .= '<input type="hidden" name="action" value="set_incoterms">';
1610 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
1611 }
1612
1613 $out .= '<select id="' . $htmlname . '" class="flat selectincoterm width75" name="' . $htmlname . '" ' . $htmloption . '>';
1614 $out .= '<option value="0">&nbsp;</option>';
1615 $num = $this->db->num_rows($resql);
1616 $i = 0;
1617 if ($num) {
1618 while ($i < $num) {
1619 $obj = $this->db->fetch_object($resql);
1620 $incotermArray[$i]['rowid'] = $obj->rowid;
1621 $incotermArray[$i]['code'] = $obj->code;
1622 $i++;
1623 }
1624
1625 foreach ($incotermArray as $row) {
1626 if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1627 $out .= '<option value="' . $row['rowid'] . '" selected>';
1628 } else {
1629 $out .= '<option value="' . $row['rowid'] . '">';
1630 }
1631
1632 if ($row['code']) {
1633 $out .= $row['code'];
1634 }
1635
1636 $out .= '</option>';
1637 }
1638 }
1639 $out .= '</select>';
1640 $out .= ajax_combobox($htmlname);
1641
1642 if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
1643 $out .= ajax_multiautocompleter('location_incoterms', array(), DOL_URL_ROOT . '/core/ajax/locationincoterms.php') . "\n";
1644 //$moreattrib .= ' autocomplete="off"';
1645 }
1646 $out .= '<input id="location_incoterms" class="maxwidthonsmartphone heightofcombo" type="text" name="location_incoterms" value="' . $location_incoterms . '">' . "\n";
1647
1648 if (!empty($page)) {
1649 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="' . $langs->trans("Modify") . '"></form>';
1650 }
1651 } else {
1652 dol_print_error($this->db);
1653 }
1654
1655 return $out;
1656 }
1657
1658 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1659
1673 public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0, $morecss = "", $useajaxcombo = 1)
1674 {
1675 // phpcs:enable
1676 global $langs;
1677
1678 // If product & services are enabled or both disabled.
1679 if ($forceall == 1 || (empty($forceall) && isModEnabled("product") && isModEnabled("service"))
1680 || (empty($forceall) && !isModEnabled('product') && !isModEnabled('service'))) {
1681 if (empty($hidetext)) {
1682 print $langs->trans("Type").'...';
1683 }
1684
1685 print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $htmlname . '" name="' . $htmlname . '">';
1686 if ($showempty) {
1687 print '<option value="-1" class="opacitymedium"'.($useajaxcombo ? '' : ' disabled="disabled"');
1688 if ($selected == -1) {
1689 print ' selected';
1690 }
1691 print '>';
1692 if (is_numeric($showempty)) {
1693 print '&nbsp;';
1694 } else {
1695 print $showempty;
1696 }
1697 print '</option>';
1698 }
1699
1700 print '<option value="0"';
1701 if (0 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'product')) {
1702 print ' selected';
1703 }
1704 print '>' . $langs->trans("Product");
1705 print '</option>';
1706
1707 print '<option value="1"';
1708 if (1 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'service')) {
1709 print ' selected';
1710 }
1711 print '>' . $langs->trans("Service");
1712 print '</option>';
1713
1714 print '</select>';
1715
1716 if ($useajaxcombo) {
1717 print ajax_combobox('select_' . $htmlname);
1718 }
1719 //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1720 }
1721 if ((empty($forceall) && !isModEnabled('product') && isModEnabled("service")) || $forceall == 3) {
1722 print $langs->trans("Service");
1723 print '<input type="hidden" name="' . $htmlname . '" value="1">';
1724 }
1725 if ((empty($forceall) && isModEnabled("product") && !isModEnabled('service')) || $forceall == 2) {
1726 print $langs->trans("Product");
1727 print '<input type="hidden" name="' . $htmlname . '" value="0">';
1728 }
1729 if ($forceall < 0) { // This should happened only for contracts when both predefined product and service are disabled.
1730 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
1731 }
1732 }
1733
1734 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1735
1741 public function load_cache_types_fees()
1742 {
1743 // phpcs:enable
1744 global $langs;
1745
1746 $num = count($this->cache_types_fees);
1747 if ($num > 0) {
1748 return 0; // Cache already loaded
1749 }
1750
1751 dol_syslog(__METHOD__, LOG_DEBUG);
1752
1753 $langs->load("trips");
1754
1755 $sql = "SELECT c.code, c.label";
1756 $sql .= " FROM " . $this->db->prefix() . "c_type_fees as c";
1757 $sql .= " WHERE active > 0";
1758
1759 $resql = $this->db->query($sql);
1760 if ($resql) {
1761 $num = $this->db->num_rows($resql);
1762 $i = 0;
1763
1764 while ($i < $num) {
1765 $obj = $this->db->fetch_object($resql);
1766
1767 // If a translation exists, we use is, otherwise, we take the label by default
1768 $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1769 $this->cache_types_fees[$obj->code] = $label;
1770 $i++;
1771 }
1772
1773 asort($this->cache_types_fees);
1774
1775 return $num;
1776 } else {
1777 dol_print_error($this->db);
1778 return -1;
1779 }
1780 }
1781
1782 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1783
1792 public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1793 {
1794 // phpcs:enable
1795 global $user, $langs;
1796
1797 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
1798
1799 $this->load_cache_types_fees();
1800
1801 print '<select id="select_' . $htmlname . '" class="flat" name="' . $htmlname . '">';
1802 if ($showempty) {
1803 print '<option value="-1"';
1804 if ($selected == -1) {
1805 print ' selected';
1806 }
1807 print '>&nbsp;</option>';
1808 }
1809
1810 foreach ($this->cache_types_fees as $key => $value) {
1811 print '<option value="' . $key . '"';
1812 if ($key == $selected) {
1813 print ' selected';
1814 }
1815 print '>';
1816 print $value;
1817 print '</option>';
1818 }
1819
1820 print '</select>';
1821 if ($user->admin) {
1822 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1823 }
1824 }
1825
1826
1827 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1828
1851 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)
1852 {
1853 // phpcs:enable
1854 global $conf, $langs;
1855
1856 $out = '';
1857
1858 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1859 if (is_null($ajaxoptions)) {
1860 $ajaxoptions = array();
1861 }
1862
1863 require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1864
1865 // No immediate load of all database
1866 $placeholder = '';
1867 if ($selected && empty($selected_input_value)) {
1868 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1869 $societetmp = new Societe($this->db);
1870 $societetmp->fetch($selected);
1871 $selected_input_value = $societetmp->name;
1872 unset($societetmp);
1873 }
1874
1875 // mode 1
1876 $urloption = 'htmlname=' . urlencode((string) (str_replace('.', '_', $htmlname))) . '&outjson=1&filter=' . urlencode((string) ($filter)) . (empty($excludeids) ? '' : '&excludeids=' . implode(',', $excludeids)) . ($showtype ? '&showtype=' . urlencode((string) ($showtype)) : '') . ($showcode ? '&showcode=' . urlencode((string) ($showcode)) : '') . ($limit ? '&limit='.$limit : '');
1877
1878 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1879 if (empty($hidelabel)) {
1880 $out .= $langs->trans("RefOrLabel") . ' : ';
1881 } elseif ($hidelabel == 1 && !is_numeric($showempty)) {
1882 $placeholder = $langs->trans($showempty);
1883 } elseif ($hidelabel > 1) {
1884 $placeholder = $langs->trans("RefOrLabel");
1885 if ($hidelabel == 2) {
1886 $out .= img_picto($langs->trans("Search"), 'search');
1887 }
1888 }
1889 $out .= '<input type="text" class="' . $morecss . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '') . ' ' . (getDolGlobalString('THIRDPARTY_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' spellcheck="false" />';
1890 if ($hidelabel == 3) {
1891 $out .= img_picto($langs->trans("Search"), 'search');
1892 }
1893
1894 $out .= ajax_event($htmlname, $events);
1895
1896 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, getDolGlobalInt('COMPANY_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
1897 } else {
1898 // Immediate load of all database
1899 $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids, $showcode);
1900 }
1901
1902 return $out;
1903 }
1904
1905
1906 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1907
1933 public function select_contact($socid, $selected = '', $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $nokeyifsocid = true, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $selected_input_value = '', $filter = '')
1934 {
1935 // phpcs:enable
1936
1937 global $conf, $langs;
1938
1939 $out = '';
1940
1941 $sav = getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT');
1942 if ($nokeyifsocid && $socid > 0) {
1943 $conf->global->CONTACT_USE_SEARCH_TO_SELECT = 0;
1944 }
1945
1946 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1947 $ajaxoptions = array();
1948
1949 require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1950
1951 // No immediate load of all database
1952 $placeholder = '';
1953 if ($selected && empty($selected_input_value)) {
1954 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
1955 $contacttmp = new Contact($this->db);
1956 $contacttmp->fetch($selected);
1957 $selected_input_value = $contacttmp->getFullName($langs);
1958 unset($contacttmp);
1959 }
1960 if (!is_numeric($showempty)) {
1961 $placeholder = $showempty;
1962 }
1963
1964 // mode 1
1965 $urloption = 'htmlname=' . urlencode((string) (str_replace('.', '_', $htmlname))) . '&outjson=1&filter=' . urlencode((string) ($filter)) . (empty($exclude) ? '' : '&exclude=' . urlencode($exclude)) . ($showsoc ? '&showsoc=' . urlencode((string) ($showsoc)) : '');
1966
1967 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1968
1969 $out .= '<input type="text" class="' . $morecss . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '') . ' ' . (getDolGlobalString('CONTACT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' spellcheck="false" />';
1970
1971 $out .= ajax_event($htmlname, $events);
1972
1973 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/contact/ajax/contact.php', $urloption, getDolGlobalInt('CONTACT_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
1974 } else {
1975 // Immediate load of all database
1976 $multiple = false;
1977 $disableifempty = 0;
1978 $options_only = 0;
1979 $limitto = '';
1980
1981 $out .= $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $morecss, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid, $multiple, $disableifempty, $filter);
1982 }
1983
1984 $conf->global->CONTACT_USE_SEARCH_TO_SELECT = $sav;
1985
1986 return $out;
1987 }
1988
1989
1990 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1991
2015 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)
2016 {
2017 // phpcs:enable
2018 global $user, $langs;
2019 global $hookmanager;
2020
2021 $langs->loadLangs(array("companies", "suppliers"));
2022
2023 $out = '';
2024 $num = 0;
2025 $outarray = array();
2026
2027 if ($selected === '') {
2028 $selected = array();
2029 } elseif (!is_array($selected)) {
2030 $selected = array($selected);
2031 }
2032
2033 // Clean $filter that may contains sql conditions so sql code
2034 if (function_exists('testSqlAndScriptInject')) {
2035 if (testSqlAndScriptInject($filter, 3) > 0) {
2036 $filter = '';
2037 return 'SQLInjectionTryDetected';
2038 }
2039 }
2040
2041 if ($filter != '') { // If a filter was provided
2042 $errormsg = '';
2043 $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
2044
2045 // Redo clean $filter that may contains sql conditions so sql code
2046 if (function_exists('testSqlAndScriptInject')) {
2047 if (testSqlAndScriptInject($filter, 3) > 0) {
2048 $filter = '';
2049 return 'SQLInjectionTryDetected';
2050 }
2051 }
2052 }
2053
2054 // We search companies
2055 $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
2056 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2057 $sql .= ", s.address, s.zip, s.town";
2058 $sql .= ", dictp.code as country_code";
2059 }
2060 $sql .= " FROM " . $this->db->prefix() . "societe as s";
2061 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2062 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_country as dictp ON dictp.rowid = s.fk_pays";
2063 }
2064 if (!$user->hasRight('societe', 'client', 'voir')) {
2065 $sql .= ", " . $this->db->prefix() . "societe_commerciaux as sc";
2066 }
2067 $sql .= " WHERE s.entity IN (" . getEntity('societe') . ")";
2068 if (!empty($user->socid)) {
2069 $sql .= " AND s.rowid = " . ((int) $user->socid);
2070 }
2071 if ($filter) {
2072 // $filter is safe because, it has been tested by testSqlAndScriptInject() and sanitized by forgeSQLFromUniversalSearchCriteria()
2073 $sqlwhere = $filter; // @phan-suppress-current-line SqlInjection
2074 $sql .= " AND (" . $sqlwhere . ")";
2075 }
2076 if (!$user->hasRight('societe', 'client', 'voir')) {
2077 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id);
2078 }
2079 if (getDolGlobalString('COMPANY_HIDE_INACTIVE_IN_COMBOBOX')) {
2080 $sql .= " AND s.status <> 0";
2081 }
2082 if (!empty($excludeids)) {
2083 $sql .= " AND s.rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeids)) . ")";
2084 }
2085 // Add where from hooks
2086 $parameters = array();
2087 $reshook = $hookmanager->executeHooks('selectThirdpartyListWhere', $parameters); // Note that $action and $object may have been modified by hook
2088 $sql .= $hookmanager->resPrint;
2089 // Add criteria
2090 if ($filterkey && $filterkey != '') {
2091 $sql .= " AND (";
2092 $prefix = !getDolGlobalString('COMPANY_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
2093 // For natural search
2094 $search_crit = explode(' ', $filterkey);
2095 $i = 0;
2096 if (count($search_crit) > 1) {
2097 $sql .= "(";
2098 }
2099 foreach ($search_crit as $crit) {
2100 if ($i > 0) {
2101 $sql .= " AND ";
2102 }
2103 $sql .= "(s.nom LIKE '" . $this->db->escape($prefix . $crit) . "%')";
2104 $i++;
2105 }
2106 if (count($search_crit) > 1) {
2107 $sql .= ")";
2108 }
2109 if (isModEnabled('barcode')) {
2110 $sql .= " OR s.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2111 }
2112 $sql .= " OR s.code_client LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.code_fournisseur LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2113 $sql .= " OR s.name_alias LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.tva_intra LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2114 $sql .= ")";
2115 }
2116 $sql .= $this->db->order("nom", "ASC");
2117 $sql .= $this->db->plimit($limit, 0);
2118
2119 // Build output string
2120 dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
2121 $resql = $this->db->query($sql);
2122 if ($resql) {
2123 // Construct $out and $outarray
2124 $out .= '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($moreparam ? ' ' . $moreparam : '') . ' name="' . $htmlname . ($multiple ? '[]' : '') . '"' . ($multiple ? ' multiple' : '') . '>' . "\n";
2125
2126 $textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
2127 if (getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT')) {
2128 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2129 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2130 if ($showempty && !is_numeric($showempty)) {
2131 $textifempty = $langs->trans($showempty);
2132 } else {
2133 $textifempty .= $langs->trans("All");
2134 }
2135 }
2136 if ($showempty) {
2137 $out .= '<option value="-1" data-html="' . dol_escape_htmltag('<span class="opacitymedium">' . ($textifempty ? $textifempty : '&nbsp;') . '</span>') . '">' . $textifempty . '</option>' . "\n";
2138 }
2139
2140 $companytemp = new Societe($this->db);
2141
2142 $num = $this->db->num_rows($resql);
2143 $i = 0;
2144 if ($num) {
2145 while ($i < $num) {
2146 $obj = $this->db->fetch_object($resql);
2147 $label = '';
2148 if ($showcode || getDolGlobalString('SOCIETE_ADD_REF_IN_LIST')) {
2149 if (($obj->client) && (!empty($obj->code_client))) {
2150 $label = $obj->code_client . ' - ';
2151 }
2152 if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
2153 $label .= $obj->code_fournisseur . ' - ';
2154 }
2155 $label .= ' ' . $obj->name;
2156 } else {
2157 $label = $obj->name;
2158 }
2159
2160 if (!empty($obj->name_alias)) {
2161 $label .= ' (' . $obj->name_alias . ')';
2162 }
2163
2164 if (getDolGlobalString('SOCIETE_SHOW_VAT_IN_LIST') && !empty($obj->tva_intra)) {
2165 $label .= ' - '.$obj->tva_intra;
2166 }
2167
2168 $labelhtml = $label;
2169
2170 if ($showtype) {
2171 $companytemp->id = $obj->rowid;
2172 $companytemp->client = $obj->client;
2173 $companytemp->fournisseur = $obj->fournisseur;
2174 $tmptype = $companytemp->getTypeUrl(1, '', 0, 'span');
2175 if ($tmptype) {
2176 $labelhtml .= ' ' . $tmptype;
2177 }
2178
2179 if ($obj->client || $obj->fournisseur) {
2180 $label .= ' (';
2181 }
2182 if ($obj->client == 1 || $obj->client == 3) {
2183 $label .= $langs->trans("Customer");
2184 }
2185 if ($obj->client == 2 || $obj->client == 3) {
2186 $label .= ($obj->client == 3 ? ', ' : '') . $langs->trans("Prospect");
2187 }
2188 if ($obj->fournisseur) {
2189 $label .= ($obj->client ? ', ' : '') . $langs->trans("Supplier");
2190 }
2191 if ($obj->client || $obj->fournisseur) {
2192 $label .= ')';
2193 }
2194 }
2195
2196 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2197 $s = ($obj->address ? ' - ' . $obj->address : '') . ($obj->zip ? ' - ' . $obj->zip : '') . ($obj->town ? ' ' . $obj->town : '');
2198 if (!empty($obj->country_code)) {
2199 $s .= ', ' . $langs->trans('Country' . $obj->country_code);
2200 }
2201 $label .= $s;
2202 $labelhtml .= $s;
2203 }
2204
2205 if (empty($outputmode)) {
2206 if (in_array($obj->rowid, $selected)) {
2207 $out .= '<option value="' . $obj->rowid . '" selected data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
2208 } else {
2209 $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
2210 }
2211 } else {
2212 array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label, 'labelhtml' => $labelhtml));
2213 }
2214
2215 $i++;
2216 if (($i % 10) == 0) {
2217 $out .= "\n";
2218 }
2219 }
2220 }
2221 $out .= '</select>' . "\n";
2222 if (!$forcecombo) {
2223 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2224 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("COMPANY_USE_SEARCH_TO_SELECT"));
2225 }
2226 } else {
2227 dol_print_error($this->db);
2228 }
2229
2230 $this->result = array('nbofthirdparties' => $num);
2231
2232 if ($outputmode) {
2233 return $outarray;
2234 }
2235 return $out;
2236 }
2237
2238
2264 public function selectcontacts($socid, $selected = array(), $htmlname = 'contactid', $showempty = 0, $exclude = '', $limitto = '', $showfunction = 0, $morecss = '', $options_only = 0, $showsoc = 0, $forcecombo = 0, $events = array(), $moreparam = '', $htmlid = '', $multiple = false, $disableifempty = 0, $filter = '')
2265 {
2266 global $conf, $user, $langs, $hookmanager, $action;
2267
2268 $langs->load('companies');
2269
2270 if (empty($htmlid)) {
2271 $htmlid = $htmlname;
2272 }
2273 $num = 0;
2274 $out = '';
2275 $outarray = array();
2276
2277 if ($selected === '') {
2278 $selected = array();
2279 } elseif (!is_array($selected)) {
2280 $selected = array((int) $selected);
2281 }
2282
2283 // Clean $filter that may contains sql conditions so sql code
2284 if (function_exists('testSqlAndScriptInject')) {
2285 if (testSqlAndScriptInject($filter, 3) > 0) {
2286 $filter = '';
2287 return 'SQLInjectionTryDetected';
2288 }
2289 }
2290
2291 if ($filter != '') { // If a filter was provided
2292 if (preg_match('/[\‍(\‍)]/', $filter)) {
2293 // If there is one parenthesis inside the criteria, we assume it is an Universal Filter Syntax.
2294 $errormsg = '';
2295 $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
2296
2297 // Redo clean $filter that may contains sql conditions so sql code
2298 if (function_exists('testSqlAndScriptInject')) {
2299 if (testSqlAndScriptInject($filter, 3) > 0) {
2300 $filter = '';
2301 return 'SQLInjectionTryDetected';
2302 }
2303 }
2304 } else {
2305 // If not, we do nothing. We already know that there is no parenthesis
2306 // TODO Disallow this case in a future by returning an error here.
2307 dol_syslog("Warning, select_thirdparty_list was called with a filter criteria not using the Universal Search Filter Syntax.", LOG_WARNING);
2308 }
2309 }
2310
2311 if (!is_object($hookmanager)) {
2312 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
2313 $hookmanager = new HookManager($this->db);
2314 }
2315
2316 // We search third parties
2317 $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";
2318 if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2319 $sql .= ", s.nom as company, s.town AS company_town";
2320 }
2321 $sql .= " FROM " . $this->db->prefix() . "socpeople as sp";
2322 if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2323 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON s.rowid = sp.fk_soc";
2324 }
2325 $sql .= " WHERE sp.entity IN (" . getEntity('contact') . ")";
2326 $sql .= " AND ((sp.fk_user_creat = ".((int) $user->id)." AND sp.priv = 1) OR sp.priv = 0)"; // check if this is a private contact
2327 if ($socid > 0 || $socid == -1) {
2328 $sql .= " AND sp.fk_soc = " . ((int) $socid);
2329 }
2330 if (getDolGlobalString('CONTACT_HIDE_INACTIVE_IN_COMBOBOX')) {
2331 $sql .= " AND sp.statut <> 0";
2332 }
2333 // filter user access
2334 if (!$user->hasRight('societe', 'client', 'voir') && !$user->socid) {
2335 $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = sp.fk_soc AND sc.fk_user = ".(int) $user->id .")";
2336 }
2337 if ($user->socid > 0) {
2338 $sql .= " AND sp.fk_soc = ".((int) $user->socid);
2339 }
2340 if ($filter) {
2341 // $filter is safe because, if it contains '(' or ')', it has been sanitized by testSqlAndScriptInject() and forgeSQLFromUniversalSearchCriteria()
2342 // if not, by testSqlAndScriptInject() only.
2343 $sanitizedfilter = $filter; // @phan-suppress-current-line SqlInjection
2344 $sql .= " AND (" . $sanitizedfilter . ")";
2345 }
2346 // Add where from hooks
2347 $parameters = array();
2348 $reshook = $hookmanager->executeHooks('selectContactListWhere', $parameters); // Note that $action and $object may have been modified by hook
2349 $sql .= $hookmanager->resPrint;
2350 $sql .= " ORDER BY sp.lastname ASC";
2351
2352 dol_syslog(get_class($this) . "::selectcontacts", LOG_DEBUG);
2353 $resql = $this->db->query($sql);
2354 if ($resql) {
2355 $num = $this->db->num_rows($resql);
2356
2357 if ($htmlname != 'none' && !$options_only) {
2358 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlid . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . (($num || empty($disableifempty)) ? '' : ' disabled') . ($multiple ? 'multiple' : '') . ' ' . (!empty($moreparam) ? $moreparam : '') . '>';
2359 }
2360
2361 if ($showempty && !is_numeric($showempty)) {
2362 $textforempty = $showempty;
2363 $out .= '<option class="optiongrey" value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>' . dol_escape_htmltag($textforempty) . '</option>';
2364 } else {
2365 if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) {
2366 $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>&nbsp;</option>';
2367 }
2368 if ($showempty == 2) {
2369 $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>-- ' . $langs->trans("Internal") . ' --</option>';
2370 }
2371 }
2372
2373 $i = 0;
2374 if ($num) {
2375 include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
2376 $contactstatic = new Contact($this->db);
2377
2378 while ($i < $num) {
2379 $obj = $this->db->fetch_object($resql);
2380
2381 // Set email (or phones) and town extended infos
2382 $extendedInfos = '';
2383 if (getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2384 $extendedInfos = array();
2385 $email = trim($obj->email);
2386 if (!empty($email)) {
2387 $extendedInfos[] = $email;
2388 } else {
2389 $phone = trim($obj->phone);
2390 $phone_perso = trim($obj->phone_perso);
2391 $phone_mobile = trim($obj->phone_mobile);
2392 if (!empty($phone)) {
2393 $extendedInfos[] = $phone;
2394 }
2395 if (!empty($phone_perso)) {
2396 $extendedInfos[] = $phone_perso;
2397 }
2398 if (!empty($phone_mobile)) {
2399 $extendedInfos[] = $phone_mobile;
2400 }
2401 }
2402 $contact_town = trim($obj->contact_town);
2403 $company_town = trim($obj->company_town);
2404 if (!empty($contact_town)) {
2405 $extendedInfos[] = $contact_town;
2406 } elseif (!empty($company_town)) {
2407 $extendedInfos[] = $company_town;
2408 }
2409 $extendedInfos = implode(' - ', $extendedInfos);
2410 if (!empty($extendedInfos)) {
2411 $extendedInfos = ' - ' . $extendedInfos;
2412 }
2413 }
2414
2415 $contactstatic->id = $obj->rowid;
2416 $contactstatic->lastname = $obj->lastname;
2417 $contactstatic->firstname = $obj->firstname;
2418 if ($obj->statut == 1) {
2419 $tmplabel = '';
2420 if ($htmlname != 'none') {
2421 $disabled = 0;
2422 if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
2423 $disabled = 1;
2424 }
2425 if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
2426 $disabled = 1;
2427 }
2428 if (!empty($selected) && in_array($obj->rowid, $selected)) {
2429 $out .= '<option value="' . $obj->rowid . '"';
2430 if ($disabled) {
2431 $out .= ' disabled';
2432 }
2433 $out .= ' selected>';
2434
2435 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2436 if ($showfunction && $obj->poste) {
2437 $tmplabel .= ' (' . $obj->poste . ')';
2438 }
2439 if (($showsoc > 0) && $obj->company) {
2440 $tmplabel .= ' - (' . $obj->company . ')';
2441 }
2442
2443 $out .= $tmplabel;
2444 $out .= '</option>';
2445 } else {
2446 $out .= '<option value="' . $obj->rowid . '"';
2447 if ($disabled) {
2448 $out .= ' disabled';
2449 }
2450 $out .= '>';
2451
2452 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2453 if ($showfunction && $obj->poste) {
2454 $tmplabel .= ' (' . $obj->poste . ')';
2455 }
2456 if (($showsoc > 0) && $obj->company) {
2457 $tmplabel .= ' - (' . $obj->company . ')';
2458 }
2459
2460 $out .= $tmplabel;
2461 $out .= '</option>';
2462 }
2463 } else {
2464 if (in_array($obj->rowid, $selected)) {
2465 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2466 if ($showfunction && $obj->poste) {
2467 $tmplabel .= ' (' . $obj->poste . ')';
2468 }
2469 if (($showsoc > 0) && $obj->company) {
2470 $tmplabel .= ' - (' . $obj->company . ')';
2471 }
2472
2473 $out .= $tmplabel;
2474 }
2475 }
2476
2477 if ($tmplabel != '') {
2478 array_push($outarray, array('key' => $obj->rowid, 'value' => $tmplabel, 'label' => $tmplabel, 'labelhtml' => $tmplabel));
2479 }
2480 }
2481 $i++;
2482 }
2483 } else {
2484 $labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
2485 $out .= '<option class="disabled" value="-1"' . (($showempty == 2 || $multiple) ? '' : ' selected') . ' disabled="disabled">';
2486 $out .= $labeltoshow;
2487 $out .= '</option>';
2488 }
2489
2490 $parameters = array(
2491 'socid' => $socid,
2492 'htmlname' => $htmlname,
2493 'resql' => $resql,
2494 'out' => &$out,
2495 'showfunction' => $showfunction,
2496 'showsoc' => $showsoc,
2497 );
2498
2499 $reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2500
2501 if ($htmlname != 'none' && !$options_only) {
2502 $out .= '</select>';
2503 }
2504
2505 if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
2506 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2507 $out .= ajax_combobox($htmlid, $events, getDolGlobalInt("CONTACT_USE_SEARCH_TO_SELECT"));
2508 }
2509
2510 $this->num = $num;
2511
2512 if ($options_only === 2) {
2513 // Return array of options
2514 return $outarray;
2515 } else {
2516 return $out;
2517 }
2518 } else {
2519 dol_print_error($this->db);
2520 return -1;
2521 }
2522 }
2523
2524
2525 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2526
2537 public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
2538 {
2539 // phpcs:enable
2540 global $langs, $conf;
2541
2542 // Search for the discounts
2543 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
2544 $sql .= " re.description, re.fk_facture_source";
2545 $sql .= " FROM " . $this->db->prefix() . "societe_remise_except as re";
2546 $sql .= " WHERE re.fk_soc = " . (int) $socid;
2547 $sql .= " AND re.entity = " . ((int) $conf->entity);
2548 if ($filter) {
2549 $sanitizedfilter = $filter; // @phan-suppress-current-line SqlInjection
2550 $sql .= " AND " . $sanitizedfilter;
2551 }
2552 $sql .= " ORDER BY re.description ASC";
2553
2554 dol_syslog(get_class($this) . "::select_remises", LOG_DEBUG);
2555 $resql = $this->db->query($sql);
2556 if ($resql) {
2557 print '<select id="select_' . $htmlname . '" class="flat maxwidth200onsmartphone" name="' . $htmlname . '">';
2558 $num = $this->db->num_rows($resql);
2559
2560 $qualifiedlines = $num;
2561
2562 $i = 0;
2563 if ($num) {
2564 print '<option value="0">&nbsp;</option>';
2565 while ($i < $num) {
2566 $obj = $this->db->fetch_object($resql);
2567 $desc = dol_trunc($obj->description, 40);
2568 if (preg_match('/\‍(CREDIT_NOTE\‍)/', $desc)) {
2569 $desc = preg_replace('/\‍(CREDIT_NOTE\‍)/', $langs->trans("CreditNote"), $desc);
2570 }
2571 if (preg_match('/\‍(DEPOSIT\‍)/', $desc)) {
2572 $desc = preg_replace('/\‍(DEPOSIT\‍)/', $langs->trans("Deposit"), $desc);
2573 }
2574 if (preg_match('/\‍(EXCESS RECEIVED\‍)/', $desc)) {
2575 $desc = preg_replace('/\‍(EXCESS RECEIVED\‍)/', $langs->trans("ExcessReceived"), $desc);
2576 }
2577 if (preg_match('/\‍(EXCESS PAID\‍)/', $desc)) {
2578 $desc = preg_replace('/\‍(EXCESS PAID\‍)/', $langs->trans("ExcessPaid"), $desc);
2579 }
2580
2581 $selectstring = '';
2582 if ($selected > 0 && $selected == $obj->rowid) {
2583 $selectstring = ' selected';
2584 }
2585
2586 $disabled = '';
2587 if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
2588 $qualifiedlines--;
2589 $disabled = ' disabled';
2590 }
2591
2592 if (getDolGlobalString('MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST') && !empty($obj->fk_facture_source)) {
2593 $tmpfac = new Facture($this->db);
2594 if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
2595 $desc = $desc . ' - ' . $tmpfac->ref;
2596 }
2597 }
2598
2599 print '<option value="' . $obj->rowid . '"' . $selectstring . $disabled . '>' . $desc . ' (' . price($obj->amount_ht) . ' ' . $langs->trans("HT") . ' - ' . price($obj->amount_ttc) . ' ' . $langs->trans("TTC") . ')</option>';
2600 $i++;
2601 }
2602 }
2603 print '</select>';
2604 print ajax_combobox('select_' . $htmlname);
2605
2606 return $qualifiedlines;
2607 } else {
2608 dol_print_error($this->db);
2609 return -1;
2610 }
2611 }
2612
2613
2614 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2615
2631 public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0')
2632 {
2633 // phpcs:enable
2634 print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
2635 }
2636
2637 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2638
2663 public function select_dolusers($userselected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = '', $force_entity = '', $maxlength = 0, $showstatus = 0, $morefilter = '', $showalso = 0, $enableonlytext = '', $morecss = '', $notdisabled = 0, $outputmode = 0, $multiple = false, $forcecombo = 0)
2664 {
2665 // phpcs:enable
2666 global $conf, $user, $langs, $hookmanager;
2667 global $action;
2668
2669 // Convert $selected into an int (in case it is an object)
2670 if (is_object($userselected)) {
2671 $selected = (int) $userselected->id;
2672 } elseif (is_numeric($userselected)) {
2673 $selected = (int) $userselected;
2674 } elseif (is_array($userselected)) {
2675 $selected = $userselected;
2676 } else {
2677 $selected = -1;
2678 }
2679
2680 // If no preselected user defined, we take current user
2681 if ((is_numeric($selected) && ((int) $selected < -4 || empty($selected))) && !getDolGlobalString('SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE')) {
2682 $selected = $user->id;
2683 }
2684
2685 // Convert selected int into an array
2686 if (!is_array($selected)) {
2687 if ($selected === -1 || $selected === '') {
2688 $selected = array();
2689 } else {
2690 $selected = array($selected);
2691 }
2692 }
2693
2694 // Exclude some users in $excludeUsers string
2695 $excludeUsers = null;
2696 if (is_array($exclude)) {
2697 $excludeUsers = implode(",", $exclude);
2698 }
2699
2700 // Include some users in $includeUsers string
2701 $includeUsers = null;
2702 $includeUsersArray = array();
2703 if (is_array($include)) {
2704 $includeUsersArray = $include;
2705 } elseif ($include == 'hierarchy') {
2706 // Build list includeUsersArray to have only hierarchy
2707 $includeUsersArray = $user->getAllChildIds(0);
2708 } elseif ($include == 'hierarchyme') {
2709 // Build list includeUsersArray to have only hierarchy and current user
2710 $includeUsersArray = $user->getAllChildIds(1);
2711 }
2712 // Get list of allowed users
2713 /* We do not limit list of users. Because we should limit this only for combo list into HR features where we may be allowed to
2714 * see all other users and element in other. For example in agenda, we can have permission to read all event of otherusers.
2715 * So we disable this.
2716 if (!$user->hasRight('user', 'user', 'lire')) {
2717 if (empty($includeUsersArray)) {
2718 $includeUsers = implode(",", $user->getAllChildIds(1));
2719 } else {
2720 $includeUsers = implode(",", array_intersect($includeUsersArray, $user->getAllChildIds(1)));
2721 }
2722 } else {
2723 $includeUsers = implode(",", $includeUsersArray);
2724 } */
2725 $includeUsers = implode(",", $includeUsersArray);
2726
2727 $num = 0;
2728
2729 $out = '';
2730 $outarray = array();
2731 $outarray2 = array();
2732
2733 // Do we want to show the label of entity into the combo list ?
2734 $showlabelofentity = isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1 && !empty($user->admin) && empty($user->entity) && !preg_match('/^search_/', $htmlname);
2735 $userissuperadminentityone = isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && empty($user->entity);
2736
2737 // Forge request to select users
2738 $sql = "SELECT DISTINCT u.rowid, u.lastname as lastname, u.firstname, u.statut as status, u.login, u.admin, u.entity, u.gender, u.photo";
2739 if ($showlabelofentity) {
2740 $sql .= ", e.label";
2741 }
2742 $sql .= " FROM " . $this->db->prefix() . "user as u";
2743 if ($showlabelofentity) {
2744 $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid = u.entity";
2745 }
2746 // Condition here should be the same than into societe->getSalesRepresentatives().
2747 if ($userissuperadminentityone && $force_entity !== 'default') {
2748 if (!empty($force_entity)) {
2749 $sql .= " WHERE u.entity IN (0, " . $this->db->sanitize($force_entity) . ")";
2750 } else {
2751 $sql .= " WHERE u.entity IS NOT NULL";
2752 }
2753 } else {
2754 if (isModEnabled('multicompany') && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2755 $sql .= " WHERE u.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
2756 } else {
2757 $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
2758 }
2759 }
2760
2761 if (!empty($user->socid)) {
2762 $sql .= " AND u.fk_soc = " . ((int) $user->socid);
2763 }
2764 if (is_array($exclude) && $excludeUsers) {
2765 $sql .= " AND u.rowid NOT IN (" . $this->db->sanitize($excludeUsers) . ")";
2766 }
2767 if ($includeUsers) {
2768 $sql .= " AND u.rowid IN (" . $this->db->sanitize($includeUsers) . ")";
2769 }
2770 if (getDolGlobalString('USER_HIDE_INACTIVE_IN_COMBOBOX') || $notdisabled) {
2771 $sql .= " AND (u.statut <> 0";
2772 if (!empty($selected)) {
2773 $sql .= " OR u.rowid IN (".$this->db->sanitize(implode(',', $selected)).")"; // We must always keep the selected users to avoid to loose it/them when updating
2774 }
2775 $sql .= ")";
2776 }
2777 if (getDolGlobalString('USER_HIDE_NONEMPLOYEE_IN_COMBOBOX')) {
2778 $sql .= " AND u.employee <> 0";
2779 }
2780 if (getDolGlobalString('USER_HIDE_EXTERNAL_IN_COMBOBOX')) {
2781 $sql .= " AND u.fk_soc IS NULL";
2782 }
2783 if (!empty($morefilter)) {
2784 $errormessage = '';
2785 $sql .= forgeSQLFromUniversalSearchCriteria($morefilter, $errormessage);
2786 if ($errormessage) {
2787 $this->errors[] = $errormessage;
2788 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2789 if ($outputmode == 0) {
2790 return 'Error bad param $morefilter';
2791 } else {
2792 return array();
2793 }
2794 }
2795 }
2796
2797 //Add hook to filter on user (for example on usergroup define in custom modules)
2798 $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
2799 if (!empty($reshook)) {
2800 $sql .= $hookmanager->resPrint;
2801 }
2802
2803 if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) { // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
2804 $sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
2805 } else {
2806 $sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
2807 }
2808
2809 dol_syslog(get_class($this) . "::select_dolusers", LOG_DEBUG);
2810
2811 $resql = $this->db->query($sql);
2812 if ($resql) {
2813 $num = $this->db->num_rows($resql);
2814 $i = 0;
2815 if ($num) {
2816 // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
2817 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : ' minwidth200') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
2818 if ($show_empty && !$multiple) {
2819 $textforempty = ' ';
2820 if (!empty($conf->use_javascript_ajax)) {
2821 $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
2822 }
2823 if (!is_numeric($show_empty)) {
2824 $textforempty = $show_empty;
2825 }
2826 $out .= '<option class="optiongrey" value="' . ($show_empty < 0 ? $show_empty : -1) . '"' . ((empty($selected) || in_array(-1, $selected)) ? ' selected' : '') . '>' . dol_escape_htmltag($textforempty) . '</option>' . "\n";
2827
2828 $outarray[($show_empty < 0 ? $show_empty : -1)] = $textforempty;
2829 $outarray2[($show_empty < 0 ? $show_empty : -1)] = array(
2830 'id' => ($show_empty < 0 ? $show_empty : -1),
2831 'label' => $textforempty,
2832 'labelhtml' => $textforempty,
2833 'color' => '',
2834 'picto' => ''
2835 );
2836 }
2837 if ($showalso == 2 || $showalso == 3) {
2838 $out .= '<option value="-3"' . ((in_array(-3, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("MyTeam") . ' --</option>' . "\n";
2839
2840 $hasAtLeastOneSubordinate = (count($user->getAllChildIds(1)) > 1);
2841 if ($hasAtLeastOneSubordinate) {
2842 //$sql = "SELECT rowid FROM".MAIN_DB_PREFIX."user "
2843 $outarray[-3] = '-- ' . $langs->trans("MyTeam") . ' --';
2844 $outarray2[-3] = array(
2845 'id' => -3,
2846 'label' => '-- ' . $langs->trans("MyTeam") . ' --',
2847 'labelhtml' => '-- ' . $langs->trans("MyTeam") . ' --',
2848 'color' => '',
2849 'picto' => ''
2850 );
2851 }
2852 }
2853 if ($showalso == 1 || $showalso == 3) {
2854 $out .= '<option value="-2"' . ((in_array(-2, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("Everybody") . ' --</option>' . "\n";
2855
2856 $outarray[-2] = '-- ' . $langs->trans("Everybody") . ' --';
2857 $outarray2[-2] = array(
2858 'id' => -2,
2859 'label' => '-- ' . $langs->trans("Everybody") . ' --',
2860 'labelhtml' => '-- ' . $langs->trans("Everybody") . ' --',
2861 'color' => '',
2862 'picto' => ''
2863 );
2864 }
2865 if ($showalso == 4) {
2866 $out .= '<option value="-4"' . ((in_array(-4, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("AllProjectContacts") . ' --</option>' . "\n";
2867
2868 $outarray[-4] = '-- ' . $langs->trans("AllProjectContacts") . ' --';
2869 $outarray2[-4] = array(
2870 'id' => -4,
2871 'label' => '-- ' . $langs->trans("AllProjectContacts") . ' --',
2872 'labelhtml' => '-- ' . $langs->trans("AllProjectContacts") . ' --',
2873 'color' => '',
2874 'picto' => ''
2875 );
2876 }
2877
2878 $userstatic = new User($this->db);
2879
2880 while ($i < $num) {
2881 $obj = $this->db->fetch_object($resql);
2882
2883 $userstatic->id = $obj->rowid;
2884 $userstatic->lastname = $obj->lastname;
2885 $userstatic->firstname = $obj->firstname;
2886 $userstatic->photo = $obj->photo;
2887 $userstatic->status = $obj->status;
2888 $userstatic->entity = $obj->entity;
2889 $userstatic->admin = $obj->admin;
2890 $userstatic->gender = $obj->gender;
2891
2892 $disableline = '';
2893 if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
2894 $disableline = ($enableonlytext ? $enableonlytext : '1');
2895 }
2896
2897 $labeltoshow = '';
2898 $labeltoshowhtml = '';
2899
2900 // $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2901 $fullNameMode = 0;
2902 if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) {
2903 $fullNameMode = 1; //Firstname+lastname
2904 }
2905 $labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2906 $labeltoshowhtml .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2907 if (empty($obj->firstname) && empty($obj->lastname)) {
2908 $labeltoshow .= $obj->login;
2909 $labeltoshowhtml .= $obj->login;
2910 }
2911
2912 // Complete name with a more info string like: ' (info1 - info2 - ...)'
2913 $moreinfo = '';
2914 $moreinfohtml = '';
2915 if (getDolGlobalString('MAIN_SHOW_LOGIN')) {
2916 $moreinfo .= ($moreinfo ? ' - ' : ' (');
2917 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(');
2918 $moreinfo .= $obj->login;
2919 $moreinfohtml .= $obj->login;
2920 }
2921 if ($showstatus >= 0) {
2922 if ($obj->status == 1 && $showstatus == 1) {
2923 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Enabled');
2924 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Enabled');
2925 }
2926 if ($obj->status == 0 && $showstatus == 1) {
2927 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Disabled');
2928 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Disabled');
2929 }
2930 }
2931 if ($showlabelofentity) {
2932 if (empty($obj->entity)) {
2933 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans("AllEntities");
2934 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans("AllEntities");
2935 } else {
2936 if ($obj->entity != $conf->entity) {
2937 $moreinfo .= ($moreinfo ? ' - ' : ' (') . ($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2938 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2939 }
2940 }
2941 }
2942 $moreinfo .= (!empty($moreinfo) ? ')' : '');
2943 $moreinfohtml .= (!empty($moreinfohtml) ? ')</span>' : '');
2944 if (!empty($disableline) && $disableline != '1') {
2945 // Add text from $enableonlytext parameter
2946 $moreinfo .= ' - ' . $disableline;
2947 $moreinfohtml .= ' - ' . $disableline;
2948 }
2949 $labeltoshow .= $moreinfo;
2950 $labeltoshowhtml .= $moreinfohtml;
2951
2952 $out .= '<option value="' . $obj->rowid . '"';
2953 if (!empty($disableline)) {
2954 $out .= ' disabled';
2955 }
2956 if (in_array($obj->rowid, $selected)) {
2957 $out .= ' selected';
2958 }
2959 $out .= ' data-html="';
2960
2961 $outhtml = $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1) . ' ';
2962 if ($showstatus >= 0 && $obj->status == 0) {
2963 $outhtml .= '<strike class="opacitymediumxxx">';
2964 }
2965 $outhtml .= $labeltoshowhtml;
2966 if ($showstatus >= 0 && $obj->status == 0) {
2967 $outhtml .= '</strike>';
2968 }
2969 $labeltoshowhtml = $outhtml;
2970
2971 $out .= dol_escape_htmltag($outhtml);
2972 $out .= '">';
2973 $out .= $labeltoshow;
2974 $out .= '</option>';
2975
2976 $outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength) . $moreinfo;
2977 $outarray2[$userstatic->id] = array(
2978 'id' => $userstatic->id,
2979 'label' => $labeltoshow,
2980 'labelhtml' => $labeltoshowhtml,
2981 'color' => '',
2982 'picto' => ''
2983 );
2984
2985 $i++;
2986 }
2987 } else {
2988 $out .= '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '" disabled>';
2989 $out .= '<option value="">' . $langs->trans("None") . '</option>';
2990 }
2991 $out .= '</select>';
2992
2993 if ($num && !$forcecombo) {
2994 // Enhance with select2
2995 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2996 $out .= ajax_combobox($htmlname);
2997 }
2998 } else {
2999 dol_print_error($this->db);
3000 }
3001
3002 $this->num = $num;
3003
3004 if ($outputmode == 2) {
3005 return $outarray2;
3006 } elseif ($outputmode) {
3007 return $outarray;
3008 }
3009
3010 return $out;
3011 }
3012
3013
3014 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3038 public function select_dolusers_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = array(), $enableonly = array(), $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofuserid = array(), $listofcontactid = array(), $listofotherid = array(), $canremoveowner = 1)
3039 {
3040 // phpcs:enable
3041 global $langs, $user;
3042
3043 $userstatic = new User($this->db);
3044 $out = '';
3045
3046 if (!empty($_SESSION['assignedtouser'])) {
3047 $assignedtouser = json_decode($_SESSION['assignedtouser'], true);
3048 if (!is_array($assignedtouser)) {
3049 $assignedtouser = array();
3050 }
3051 } else {
3052 $assignedtouser = array();
3053 }
3054 $nbassignetouser = count($assignedtouser);
3055
3056 //if ($nbassignetouser && $action != 'view') $out .= '<br>';
3057 if ($nbassignetouser) {
3058 $out .= '<ul class="attendees">';
3059 }
3060 $i = 0;
3061 $ownerid = 0;
3062 foreach ($assignedtouser as $key => $value) {
3063 if ($value['id'] == $ownerid) {
3064 continue;
3065 }
3066
3067 $out .= '<li>';
3068
3069 $userstatic->fetch($value['id']);
3070 $out .= $userstatic->getNomUrl(-4);
3071
3072 if ($i == 0) {
3073 $ownerid = $value['id'];
3074 $out .= ' (' . $langs->trans("Owner") . ')';
3075 }
3076 // Add picto to delete owner/assignee
3077 if ($nbassignetouser > 1 && $action != 'view') {
3078 $canremoveassignee = 1;
3079 if ($i == 0) {
3080 // We are on the owner of the event
3081 if (!$canremoveowner) {
3082 $canremoveassignee = 0;
3083 }
3084 if (!$user->hasRight('agenda', 'allactions', 'create')) {
3085 $canremoveassignee = 0; // Can't remove the owner
3086 }
3087 } else {
3088 // We are not on the owner of the event but on a secondary assignee
3089 }
3090 if ($canremoveassignee) {
3091 // If user has all permission, he should be ableto remove a assignee.
3092 // If user has not all permission, he can onlyremove assignee of other (he can't remove itself)
3093 $out .= ' <input type="image" style="border: 0px;" src="' . img_picto($langs->trans("Remove"), 'delete', '', 0, 1) . '" value="' . $userstatic->id . '" class="noborderfocus removedassigned reposition" id="removedassigned_' . $userstatic->id . '" name="removedassigned_' . $userstatic->id . '">';
3094 }
3095 }
3096 // Show my availability
3097 if ($showproperties) {
3098 if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
3099 $out .= '<div class="myavailability inline-block">';
3100 $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;';
3101 //$out .= '<span class="opacitymedium">' . $langs->trans("Availability") . ':</span>';
3102 $out .= '</span>';
3103 $out .= ' <input title="'.$langs->trans("Availability").'" id="transparency" class="paddingrightonly" ' . ($action == 'view' ? 'disabled' : '') . ' type="checkbox" name="transparency"' . ($listofuserid[$ownerid]['transparency'] ? ' checked' : '') . '><label for="transparency">' . $langs->trans("Busy") . '</label>';
3104 $out .= '</div>';
3105 }
3106 }
3107 //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
3108 //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
3109
3110 $out .= '</li>';
3111 $i++;
3112 }
3113 if ($nbassignetouser) {
3114 $out .= '</ul>';
3115 }
3116
3117 // Method with no ajax
3118 if ($action != 'view') {
3119 // Section to add another user
3120 $out .= '<div class="divadduser'.$htmlname.'">';
3121 $out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
3122 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
3123 $out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
3124 $out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
3125 $out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#' . $action . 'assignedtouser").attr("disabled", false); }';
3126 $out .= ' else { jQuery("#' . $action . 'assignedtouser").attr("disabled", true); }';
3127 $out .= '});';
3128 $out .= '})</script>';
3129 $out .= img_picto('', 'user', 'class="pictofixedwidth"');
3130 $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter, 0, '', 'minwidth200');
3131 $out .= ' <button type="submit" disabled class="button valignmiddle smallpaddingimp reposition butActionAdd" id="' . $action . 'assignedtouser" name="' . $action . 'assignedtouser" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
3132 $out .= $langs->trans("Add").'</button>';
3133 $out .= '</div>';
3134 //$out .= '<br>';
3135 }
3136
3137 return $out;
3138 }
3139
3140 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3160 public function select_dolresources_forevent($action = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = array(), $enableonly = array(), $force_entity = '0', $maxlength = 0, $showstatus = 0, $morefilter = '', $showproperties = 0, $listofresourceid = array())
3161 {
3162 // phpcs:enable
3163 global $langs;
3164
3165 require_once DOL_DOCUMENT_ROOT.'/resource/class/html.formresource.class.php';
3166 require_once DOL_DOCUMENT_ROOT.'/resource/class/dolresource.class.php';
3167 $formresources = new FormResource($this->db);
3168 $resourcestatic = new Dolresource($this->db);
3169
3170 $out = '';
3171 if (!empty($_SESSION['assignedtoresource'])) {
3172 $assignedtoresource = json_decode($_SESSION['assignedtoresource'], true);
3173 if (!is_array($assignedtoresource)) {
3174 $assignedtoresource = array();
3175 }
3176 } else {
3177 $assignedtoresource = array();
3178 }
3179 $nbassignetoresource = count($assignedtoresource);
3180
3181 //if ($nbassignetoresource && $action != 'view') $out .= '<br>';
3182 if ($nbassignetoresource) {
3183 $out .= '<ul class="attendees">';
3184 }
3185 $i = 0;
3186
3187 foreach ($assignedtoresource as $key => $value) {
3188 $out .= '<li>';
3189 $resourcestatic->fetch($value['id']);
3190 $out .= $resourcestatic->getNomUrl(-1);
3191 if ($nbassignetoresource >= 1 && $action != 'view') {
3192 $out .= ' <input type="image" style="border: 0px;" src="' . img_picto($langs->trans("Remove"), 'delete', '', 0, 1) . '" value="' . $resourcestatic->id . '" class="removedassignedresource reposition" id="removedassignedresource_' . $resourcestatic->id . '" name="removedassignedresource_' . $resourcestatic->id . '">';
3193 }
3194 // Show my availability
3195 if ($showproperties) {
3196 if (is_array($listofresourceid) && count($listofresourceid)) {
3197 $out .= '<div class="myavailability inline-block">';
3198 $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;';
3199 //$out .= '<span class="opacitymedium">' . $langs->trans("Availability") . ': </span>';
3200 $out .= '</span>';
3201 $out .= ' <input title="'.$langs->trans("Availability").'" id="transparencyresource'.$value['id'].'" class="paddingrightonly" ' . ($action == 'view' ? 'disabled' : '') . ' type="checkbox" name="transparency"' . ($listofresourceid[$value['id']]['transparency'] ? ' checked' : '') . '><label for="transparencyresource'.$value['id'].'">' . $langs->trans("Busy") . '</label>';
3202 $out .= '</div>';
3203 }
3204 }
3205 //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
3206 //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
3207
3208 $out .= '</li>';
3209 $i++;
3210 }
3211 if ($nbassignetoresource) {
3212 $out .= '</ul>';
3213 }
3214
3215 // Method with no ajax
3216 if ($action != 'view') {
3217 $out .= '<input type="hidden" class="removedassignedresourcehidden" name="removedassignedresource" value="">';
3218 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
3219 $out .= 'jQuery(".removedassignedresource").click(function() { jQuery(".removedassignedresourcehidden").val(jQuery(this).val()); });';
3220 $out .= 'jQuery(".assignedtoresource").change(function() { console.log(jQuery(".assignedtoresource option:selected").val());';
3221 $out .= ' if (jQuery(".assignedtoresource option:selected").val() > 0) { jQuery("#' . $action . 'assignedtoresource").attr("disabled", false); }';
3222 $out .= ' else { jQuery("#' . $action . 'assignedtoresource").attr("disabled", true); }';
3223 $out .= '});';
3224 $out .= '})</script>';
3225
3226 $events = array();
3227 if ($nbassignetoresource) {
3228 //$out .= img_picto('', 'add', 'class="pictofixedwidth"');
3229 } else {
3230 $out .= img_picto('', 'resource', 'class="pictofixedwidth"');
3231 }
3232 $out .= $formresources->select_resource_list(0, $htmlname, '', 1, 1, 0, $events, '', 2, 0, 'minwidth200');
3233 //$out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
3234 $out .= ' <button type="submit" disabled class="button valignmiddle smallpaddingimp reposition butActionAdd" id="' . $action . 'assignedtoresource" name="' . $action . 'assignedtoresource" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
3235 $out .= $langs->trans("Add");
3236 $out .= '</button>';
3237 $out .= '<br>';
3238 }
3239
3240 return $out;
3241 }
3242
3243 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3244
3274 public function select_produits($selected = 0, $htmlname = 'productid', $filtertype = '', $limit = 0, $price_level = 0, $status = 1, $finished = 2, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $hidepriceinlabel = 0, $warehouseStatus = '', $selected_combinations = null, $nooutput = 0, $status_purchase = -1, $warehouseId = 0)
3275 {
3276 // phpcs:enable
3277 global $langs, $conf;
3278
3279 $out = '';
3280
3281 // check parameters
3282 $price_level = (!empty($price_level) ? $price_level : 0);
3283 if (is_null($ajaxoptions)) {
3284 $ajaxoptions = array();
3285 }
3286
3287 if (strval($filtertype) === '' && (isModEnabled("product") || isModEnabled("service"))) {
3288 if (isModEnabled("product") && !isModEnabled('service')) {
3289 $filtertype = '0';
3290 } elseif (!isModEnabled('product') && isModEnabled("service")) {
3291 $filtertype = '1';
3292 }
3293 }
3294
3295 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3296 $placeholder = (is_numeric($showempty) ? '' : 'placeholder="'.dolPrintHTML($showempty).'"');
3297
3298 if ($selected && empty($selected_input_value)) {
3299 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3300 $producttmpselect = new Product($this->db);
3301 $producttmpselect->fetch($selected);
3302 $selected_input_value = $producttmpselect->ref;
3303 unset($producttmpselect);
3304 }
3305 // handle case where product or service module is disabled + no filter specified
3306 if ($filtertype == '') {
3307 if (!isModEnabled('product')) { // when product module is disabled, show services only
3308 $filtertype = 1;
3309 } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
3310 $filtertype = 0;
3311 }
3312 }
3313 // mode=1 means customers products
3314 $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;
3315 if ((int) $warehouseId > 0) {
3316 $urloption .= '&warehouseid=' . (int) $warehouseId;
3317 }
3318
3319 if (isModEnabled('variants') && is_array($selected_combinations)) {
3320 // Code to automatically insert with javascript the select of attributes under the select of product
3321 // when a parent of variant has been selected.
3322 // Note: Samecode than for product input using select
3323 $htmltag = 'input';
3324 $out .= '
3325 <!-- script to auto show attributes select tags if a variant was selected -->
3326 <script nonce="' . getNonce() . '">
3327 // auto show attributes fields
3328 selected = ' . json_encode($selected_combinations) . ';
3329 combvalues = {};
3330
3331 jQuery(document).ready(function () {
3332
3333 jQuery("input[name=\'prod_entry_mode\']").change(function () {
3334 if (jQuery(this).val() == \'free\') {
3335 jQuery(\'div#attributes_box\').empty();
3336 }
3337 });
3338
3339 jQuery("'.$htmltag.'#' . $htmlname . '").change(function () {
3340
3341 if (!jQuery(this).val()) {
3342 jQuery(\'div#attributes_box\').empty();
3343 return;
3344 }
3345
3346 console.log("A change has started. We get variants fields to inject html select");
3347
3348 jQuery.getJSON("' . DOL_URL_ROOT . '/variants/ajax/getCombinations.php", {
3349 id: jQuery(this).val()
3350 }, function (data) {
3351 jQuery(\'div#attributes_box\').empty();
3352
3353 jQuery.each(data, function (key, val) {
3354
3355 combvalues[val.id] = val.values;
3356
3357 var span = jQuery(document.createElement(\'div\')).css({
3358 \'display\': \'table-row\'
3359 });
3360
3361 span.append(
3362 jQuery(document.createElement(\'div\')).text(val.label).css({
3363 \'font-weight\': \'bold\',
3364 \'display\': \'table-cell\'
3365 })
3366 );
3367
3368 var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
3369 \'margin-left\': \'15px\',
3370 \'white-space\': \'pre\'
3371 }).append(
3372 jQuery(document.createElement(\'option\')).val(\'\')
3373 );
3374
3375 jQuery.each(combvalues[val.id], function (key, val) {
3376 var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
3377
3378 if (selected[val.fk_product_attribute] == val.id) {
3379 tag.attr(\'selected\', \'selected\');
3380 }
3381
3382 html.append(tag);
3383 });
3384
3385 span.append(html);
3386 jQuery(\'div#attributes_box\').append(span);
3387 });
3388 })
3389 });
3390
3391 ' . ($selected ? 'jQuery("'.$htmltag.'#' . $htmlname . '").change();' : '') . '
3392 });
3393 </script>
3394 ';
3395 }
3396
3397 if (empty($hidelabel)) {
3398 $placeholder = ' placeholder="' . dolPrintHTMLForAttribute($langs->trans("RefOrLabel")) . '"';
3399 } elseif ($hidelabel > 1) {
3400 $placeholder = ' placeholder="' . dolPrintHTMLForAttribute($langs->trans("RefOrLabel")) . '"';
3401 if ($hidelabel == 2) {
3402 $out .= img_picto($langs->trans("Search"), 'search');
3403 }
3404 }
3405
3406 $out .= '<input type="text" class="minwidth100' . ($morecss ? ' ' . $morecss : '') . '" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' spellcheck="false" />';
3407 if ($hidelabel == 3) {
3408 $out .= img_picto($langs->trans("Search"), 'search');
3409 }
3410
3411 $out .= ajax_autocompleter((string) $selected, $htmlname, DOL_URL_ROOT . '/product/ajax/products.php', $urloption, getDolGlobalInt('PRODUIT_USE_SEARCH_TO_SELECT'), getDolGlobalInt('PRODUCT_SEARCH_AUTO_SELECT_IF_ONLY_ONE', 1), $ajaxoptions);
3412 } else {
3413 $out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus, $status_purchase, $warehouseId);
3414
3415 if (isModEnabled('variants') && is_array($selected_combinations)) {
3416 // Code to automatically insert with javascript the select of attributes under the select of product
3417 // when a parent of variant has been selected.
3418 // Note: Samecode than for product input using Ajax
3419 $htmltag = 'select';
3420 $out .= '
3421 <!-- script to auto show attributes select tags if a variant was selected -->
3422 <script nonce="' . getNonce() . '">
3423 // auto show attributes fields
3424 selected = ' . json_encode($selected_combinations) . ';
3425 combvalues = {};
3426
3427 jQuery(document).ready(function () {
3428
3429 jQuery("input[name=\'prod_entry_mode\']").change(function () {
3430 if (jQuery(this).val() == \'free\') {
3431 jQuery(\'div#attributes_box\').empty();
3432 }
3433 });
3434
3435 jQuery("'.$htmltag.'#' . $htmlname . '").change(function () {
3436
3437 if (!jQuery(this).val()) {
3438 jQuery(\'div#attributes_box\').empty();
3439 return;
3440 }
3441
3442 console.log("A change has started. We get variants fields to inject html select");
3443
3444 jQuery.getJSON("' . DOL_URL_ROOT . '/variants/ajax/getCombinations.php", {
3445 id: jQuery(this).val()
3446 }, function (data) {
3447 jQuery(\'div#attributes_box\').empty();
3448
3449 jQuery.each(data, function (key, val) {
3450
3451 combvalues[val.id] = val.values;
3452
3453 var span = jQuery(document.createElement(\'div\')).css({
3454 \'display\': \'table-row\'
3455 });
3456
3457 span.append(
3458 jQuery(document.createElement(\'div\')).text(val.label).css({
3459 \'font-weight\': \'bold\',
3460 \'display\': \'table-cell\'
3461 })
3462 );
3463
3464 var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
3465 \'margin-left\': \'15px\',
3466 \'white-space\': \'pre\'
3467 }).append(
3468 jQuery(document.createElement(\'option\')).val(\'\')
3469 );
3470
3471 jQuery.each(combvalues[val.id], function (key, val) {
3472 var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
3473
3474 if (selected[val.fk_product_attribute] == val.id) {
3475 tag.attr(\'selected\', \'selected\');
3476 }
3477
3478 html.append(tag);
3479 });
3480
3481 span.append(html);
3482 jQuery(\'div#attributes_box\').append(span);
3483 });
3484 })
3485 });
3486
3487 ' . ($selected ? 'jQuery("'.$htmltag.'#' . $htmlname . '").change();' : '') . '
3488 });
3489 </script>
3490 ';
3491 }
3492 }
3493
3494 if (empty($nooutput)) {
3495 print $out;
3496 } else {
3497 return $out;
3498 }
3499 }
3500
3501 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3502
3518 public function select_bom($selected = '', $htmlname = 'bom_id', $limit = 0, $status = 1, $type = 0, $showempty = '1', $morecss = '', $nooutput = '', $forcecombo = 0, $TProducts = [])
3519 {
3520 // phpcs:enable
3521
3522 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3523
3524 $error = 0;
3525 $out = '';
3526
3527 if (!$forcecombo) {
3528 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3529 $events = array();
3530 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("BOM_USE_SEARCH_TO_SELECT"));
3531 }
3532
3533 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
3534
3535 $sql = 'SELECT b.rowid, b.ref, b.label as bomLabel, p.label as productLabel';
3536 $sql .= ' FROM ' . $this->db->prefix() . 'bom_bom as b';
3537 $sql .= ' INNER JOIN ' . $this->db->prefix() . 'product as p ON b.fk_product = p.rowid';
3538 $sql .= ' WHERE b.entity IN (' . getEntity('bom') . ')';
3539 if (!empty($status)) {
3540 $sql .= ' AND status = ' . (int) $status;
3541 }
3542 if (!empty($type)) {
3543 $sql .= ' AND bomtype = ' . (int) $type;
3544 }
3545 if (!empty($TProducts)) {
3546 $sql .= ' AND fk_product IN (' . $this->db->sanitize(implode(',', $TProducts)) . ')';
3547 }
3548 if (!empty($limit)) {
3549 $sql .= ' LIMIT ' . (int) $limit;
3550 }
3551 $resql = $this->db->query($sql);
3552 if ($resql) {
3553 if ($showempty) {
3554 $out .= '<option value="-1"';
3555 if (empty($selected)) {
3556 $out .= ' selected';
3557 }
3558 $out .= '>&nbsp;</option>';
3559 }
3560 while ($obj = $this->db->fetch_object($resql)) {
3561 $out .= '<option value="' . $obj->rowid . '"';
3562 if ($obj->rowid == $selected) {
3563 $out .= 'selected';
3564 }
3565 $out .= '>' . $obj->ref . ' - ' . $obj->productLabel . ' - ' . $obj->bomLabel . '</option>';
3566 }
3567 } else {
3568 $error++;
3569 dol_print_error($this->db);
3570 }
3571 $out .= '</select>';
3572 if (empty($nooutput)) {
3573 print $out;
3574 } else {
3575 return $out;
3576 }
3577 }
3578
3579 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3580
3607 public function select_produits_list($selected = 0, $htmlname = 'productid', $filtertype = '', $limit = 1000, $price_level = 0, $filterkey = '', $status = 1, $finished = 2, $outputmode = 0, $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = 'maxwidth500', $hidepriceinlabel = 0, $warehouseStatus = '', $status_purchase = -1, $warehouseId = 0)
3608 {
3609 // phpcs:enable
3610 global $langs;
3611 global $hookmanager;
3612
3613 $out = '';
3614 $outarray = array();
3615
3616 // Units
3617 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3618 $langs->load('other');
3619 }
3620
3621 $warehouseStatusArray = array();
3622 if (!empty($warehouseStatus)) {
3623 require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
3624 if (preg_match('/warehouseclosed/', $warehouseStatus)) {
3625 $warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
3626 }
3627 if (preg_match('/warehouseopen/', $warehouseStatus)) {
3628 $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
3629 }
3630 if (preg_match('/warehouseinternal/', $warehouseStatus)) {
3631 $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
3632 }
3633 }
3634
3635 $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";
3636 if (count($warehouseStatusArray)) {
3637 $selectFieldsGrouped = ", SUM(" . $this->db->ifsql("e.statut IS NULL", "0", "ps.reel") . ") as stock"; // e.statut is null if there is no record in a qualified stock
3638 } else {
3639 $selectFieldsGrouped = ", " . $this->db->ifsql("p.stock IS NULL", '0', "p.stock") . " AS stock";
3640 }
3641
3642 $sql = "SELECT ";
3643
3644 // Add select from hooks
3645 $parameters = array();
3646 $reshook = $hookmanager->executeHooks('selectProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
3647 if (empty($reshook)) {
3648 $sql .= $selectFields.$selectFieldsGrouped.$hookmanager->resPrint;
3649 } else {
3650 $sql .= $hookmanager->resPrint;
3651 }
3652
3653 if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
3654 // Take randomly the first category of product to allow a sort on it. Bugged feature !
3655 $sql .= ", (SELECT " . $this->db->prefix() . "categorie_product.fk_categorie
3656 FROM " . $this->db->prefix() . "categorie_product
3657 WHERE " . $this->db->prefix() . "categorie_product.fk_product = p.rowid
3658 LIMIT 1
3659 ) AS categorie_product_id";
3660 }
3661
3662 // Price by customer
3663 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3664 $sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
3665 $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, pcp.discount_percent as custdiscount_percent';
3666 $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custdefault_vat_code, custref, custdiscount_percent";
3667 }
3668 // Units
3669 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3670 $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";
3671 $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';
3672 }
3673
3674 // Multilang : we add translation
3675 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3676 $sql .= ", pl.label as label_translated";
3677 $sql .= ", pl.description as description_translated";
3678 $selectFields .= ", label_translated";
3679 $selectFields .= ", description_translated";
3680 }
3681 // Price by quantity
3682 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3683 $sql .= ", (SELECT pp.rowid FROM " . $this->db->prefix() . "product_price as pp WHERE pp.fk_product = p.rowid";
3684 if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3685 $sql .= " AND price_level = " . ((int) $price_level);
3686 }
3687 $sql .= " ORDER BY date_price";
3688 $sql .= " DESC LIMIT 1) as price_rowid";
3689 $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
3690 if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3691 $sql .= " AND price_level = " . ((int) $price_level);
3692 }
3693 $sql .= " ORDER BY date_price";
3694 $sql .= " DESC LIMIT 1) as price_by_qty";
3695 $selectFields .= ", price_rowid, price_by_qty";
3696 }
3697
3698 //$sqlfields = $sql; // $sql fields to remove for count total
3699
3700 $sql .= " FROM ".$this->db->prefix()."product as p";
3701
3702 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_FORCE_INDEX')) {
3703 $sql .= " USE INDEX (" . $this->db->sanitize(getDolGlobalString('MAIN_PRODUCT_FORCE_INDEX')) . ")";
3704 }
3705
3706 // Add from (left join) from hooks
3707 $parameters = array(
3708 'socid' => $socid,
3709 );
3710 $reshook = $hookmanager->executeHooks('selectProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
3711 $sql .= $hookmanager->resPrint;
3712
3713 if (count($warehouseStatusArray)) {
3714 // Return line if product is inside the selected stock. If not, e.* and p.* will be null so we will count 0.
3715 // Replace this with a AND EXISTS ? Not possible as we need the ps.reel field for the SUM or 0 if no link.
3716 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_stock as ps ON ps.fk_product = p.rowid";
3717 $sql .= " LEFT JOIN " . $this->db->prefix() . "entrepot as e ON ps.fk_entrepot = e.rowid AND e.entity IN (" . getEntity('stock') . ")";
3718 $sql .= ' AND e.statut IN (' . $this->db->sanitize($this->db->escape(implode(',', $warehouseStatusArray))) . ')';
3719 }
3720
3721 // Price by customer (Add field pcp for the older price for couple product/thirdparty.
3722 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3723 $now = dol_now();
3724 $sql .= " LEFT JOIN (";
3725 $sql .= " SELECT pcp1.*";
3726 $sql .= " FROM " . $this->db->prefix() . "product_customer_price AS pcp1";
3727 $sql .= " LEFT JOIN (";
3728 $sql .= " SELECT fk_soc, fk_product, MIN(date_begin) AS date_begin";
3729 $sql .= " FROM " . $this->db->prefix() . "product_customer_price";
3730 $sql .= " WHERE fk_soc = " . ((int) $socid);
3731 $sql .= " AND date_begin <= '" . $this->db->idate($now) . "'";
3732 $sql .= " AND (date_end IS NULL OR '" . $this->db->idate($now) . "' <= date_end)";
3733 $sql .= " GROUP BY fk_soc, fk_product";
3734 $sql .= " ) AS pcp2 ON pcp1.fk_soc = pcp2.fk_soc AND pcp1.fk_product = pcp2.fk_product AND pcp1.date_begin = pcp2.date_begin";
3735 $sql .= " WHERE pcp2.fk_soc IS NOT NULL";
3736 $sql .= " ) AS pcp ON pcp.fk_soc = " . ((int) $socid) . " AND pcp.fk_product = p.rowid";
3737 }
3738 // Units : we add unit properties with a link on the primary key of unit
3739 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3740 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units as u ON u.rowid = p.fk_unit";
3741 }
3742 // Multilang : we add translation fields with a link on unique key fk_product/lang.
3743 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3744 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_lang as pl ON pl.fk_product = p.rowid";
3745 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && !empty($socid)) {
3746 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
3747 $soc = new Societe($this->db);
3748 $result = $soc->fetch($socid);
3749 if ($result > 0 && !empty($soc->default_lang)) {
3750 $sql .= " AND pl.lang = '" . $this->db->escape($soc->default_lang) . "'";
3751 } else {
3752 $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3753 }
3754 } else {
3755 $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3756 }
3757 }
3758
3759 // Add WHERE conditions
3760 $sql .= ' WHERE p.entity IN (' . getEntity('product') . ')';
3761 if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD')) {
3762 if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD_BUT_ALLOW_SEARCH_IN_EAN13')) {
3763 if (strlen($filterkey) != 13) {
3764 $sql .= " AND NOT EXISTS (SELECT pac.rowid FROM ".$this->db->prefix()."product_attribute_combination as pac WHERE pac.fk_product_child = p.rowid)";
3765 }
3766 } else {
3767 $sql .= " AND NOT EXISTS (SELECT pac.rowid FROM ".$this->db->prefix()."product_attribute_combination as pac WHERE pac.fk_product_child = p.rowid)";
3768 }
3769 }
3770 if ($finished == 0) {
3771 $sql .= " AND p.finished = " . ((int) $finished);
3772 } elseif ($finished == 1) {
3773 $sql .= " AND p.finished = ".((int) $finished);
3774 }
3775 if ($status >= 0) {
3776 $sql .= " AND p.tosell = ".((int) $status);
3777 }
3778 if ($status_purchase >= 0) {
3779 $sql .= " AND p.tobuy = " . ((int) $status_purchase);
3780 }
3781 // Filter by product type
3782 if (strval($filtertype) != '') {
3783 $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
3784 } elseif (!isModEnabled('product')) { // when product module is disabled, show services only
3785 $sql .= " AND p.fk_product_type = 1";
3786 } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
3787 $sql .= " AND p.fk_product_type = 0";
3788 }
3789
3790 if ((int) $warehouseId > 0) {
3791 $sql .= " AND EXISTS (SELECT psw.fk_product FROM " . $this->db->prefix() . "product_stock as psw WHERE psw.reel > 0 AND psw.fk_entrepot = ".(int) $warehouseId." AND psw.fk_product = p.rowid)";
3792 }
3793
3794 // Add where from hooks
3795 $parameters = array(
3796 'filterkey' => &$filterkey,
3797 'socid' => $socid,
3798 );
3799 $reshook = $hookmanager->executeHooks('selectProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3800 $sql .= $hookmanager->resPrint;
3801 // Add criteria on ref/label
3802 if ($filterkey != '') {
3803 $sqlSupplierSearch = '';
3804
3805 $sql .= ' AND (';
3806 $prefix = getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3807 // For natural search
3808 $search_crit = explode(' ', $filterkey);
3809 $i = 0;
3810 if (count($search_crit) > 1) {
3811 $sql .= "(";
3812 }
3813 foreach ($search_crit as $crit) {
3814 if ($i > 0) {
3815 $sql .= " AND ";
3816 }
3817 $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3818 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3819 $sql .= " OR pl.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3820 }
3821 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3822 $sql .= " OR pcp.ref_customer LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3823 }
3824 if (getDolGlobalString('PRODUCT_AJAX_SEARCH_ON_DESCRIPTION')) {
3825 $sql .= " OR p.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3826 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3827 $sql .= " OR pl.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3828 }
3829 }
3830
3831 // include search in supplier ref
3832 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3833 $sqlSupplierSearch .= !empty($sqlSupplierSearch) ? ' AND ' : '';
3834 $sqlSupplierSearch .= " pfp.ref_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3835 }
3836 $sql .= ")";
3837 $i++;
3838 }
3839 if (count($search_crit) > 1) {
3840 $sql .= ")";
3841 }
3842 if (isModEnabled('barcode')) {
3843 $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3844 }
3845
3846 // include search in supplier ref
3847 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3848 $sql .= " OR EXISTS (SELECT pfp.fk_product FROM " . $this->db->prefix() . "product_fournisseur_price as pfp WHERE p.rowid = pfp.fk_product";
3849 $sql .= " AND (";
3850 $sql .= $sqlSupplierSearch;
3851 $sql .= "))";
3852 }
3853
3854 $sql .= ')';
3855 }
3856 if (count($warehouseStatusArray)) {
3857 $sql .= " GROUP BY " . $this->db->sanitize($selectFields, 0, 0, 1); // To have the SUM on ps.reel working in the select.
3858 }
3859
3860 // Sort by category
3861 if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
3862 $sql .= " ORDER BY categorie_product_id ".(getDolGlobalInt('PRODUCT_SORT_BY_CATEGORY') == 1 ? "ASC" : "DESC");
3863 } else {
3864 $sql .= $this->db->order("p.ref");
3865 }
3866
3867 $limit = getDolGlobalInt('SEARCH_LIMIT_AJAX') ?: $limit; // SEARCH_LIMIT_AJAX is a hidden option that has priority on visible option PRODUIT_LIMIT_SIZE if set.
3868 $sql .= $this->db->plimit($limit, 0);
3869
3870 /* The fast and low memory method to get and count full list converts the sql into a sql count */
3871 /*
3872 $nbtotalofrecords = 0;
3873 $sqlforcount = preg_replace('/^'.preg_quote($sqlfields, '/').'/', 'SELECT COUNT(*) as nbtotalofrecords', $sql);
3874 $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount);
3875
3876 $resql = $this->db->query($sqlforcount);
3877 if ($resql) {
3878 $objforcount = $this->db->fetch_object($resql);
3879 $nbtotalofrecords = $objforcount->nbtotalofrecords;
3880 } else {
3881 dol_print_error($this->db);
3882 }
3883 */
3884
3885 // Build output string
3886 dol_syslog(get_class($this) . "::select_produits_list search products", LOG_DEBUG);
3887
3888 // If we have no $limit parameter, this request may hang dur to high number of lines returned.
3889 // This should not happen because this method should not be called directly, iIt is called by select_produit() that always add a $limit parameter.
3890 $result = $this->db->query($sql);
3891
3892 if ($result) {
3893 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3894 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3895 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
3896
3897 $num = $this->db->num_rows($result);
3898
3899 $events = array();
3900
3901 if (!$forcecombo) {
3902 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3903 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
3904 }
3905
3906 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
3907
3908 $textifempty = '';
3909 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
3910 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
3911 if (getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3912 if ($showempty && !is_numeric($showempty)) {
3913 $textifempty = $langs->trans($showempty);
3914 } else {
3915 $textifempty .= $langs->trans("All");
3916 }
3917 } else {
3918 if ($showempty && !is_numeric($showempty)) {
3919 $textifempty = $langs->trans($showempty);
3920 }
3921 }
3922 if ($showempty) {
3923 $out .= '<option value="-1" selected>' . ($textifempty ? $textifempty : '&nbsp;') . '</option>';
3924 }
3925
3926 $i = 0;
3927 while ($num && $i < $num) {
3928 $opt = '';
3929 $optJson = array();
3930 $objp = $this->db->fetch_object($result);
3931
3932 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) && !empty($objp->price_by_qty) && $objp->price_by_qty == 1) { // Price by quantity will return many prices for the same product
3933 $sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
3934 $sql .= " FROM " . $this->db->prefix() . "product_price_by_qty";
3935 $sql .= " WHERE fk_product_price = " . ((int) $objp->price_rowid);
3936 $sql .= " ORDER BY quantity ASC";
3937
3938 dol_syslog(get_class($this) . "::select_produits_list search prices by qty", LOG_DEBUG);
3939 $result2 = $this->db->query($sql);
3940 if ($result2) {
3941 $nb_prices = $this->db->num_rows($result2);
3942 $j = 0;
3943 while ($nb_prices && $j < $nb_prices) {
3944 $objp2 = $this->db->fetch_object($result2);
3945
3946 $objp->price_by_qty_rowid = $objp2->rowid;
3947 $objp->price_by_qty_price_base_type = $objp2->price_base_type;
3948 $objp->price_by_qty_quantity = $objp2->quantity;
3949 $objp->price_by_qty_unitprice = $objp2->unitprice;
3950 $objp->price_by_qty_remise_percent = $objp2->remise_percent;
3951 // For backward compatibility
3952 $objp->quantity = $objp2->quantity;
3953 $objp->price = $objp2->price;
3954 $objp->unitprice = $objp2->unitprice;
3955 $objp->remise_percent = $objp2->remise_percent;
3956
3957 //$objp->tva_tx is not overwritten by $objp2 value
3958 //$objp->default_vat_code is not overwritten by $objp2 value
3959
3960 $this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
3961 '@phan-var-force array{key:string,value:string,label:string,label2:string,desc:string,type:string,price_ht:string,price_ttc:string,price_ht_locale:string,price_ttc_locale:string,pricebasetype:string,tva_tx:string,default_vat_code:string,qty:string,discount:string,duration_value:string,duration_unit:string,pbq:string,labeltrans:string,desctrans:string,ref_customer:string} $optJson';
3962 $j++;
3963
3964 // Add new entry
3965 // "key" value of json key array is used by jQuery automatically as selected value
3966 // "label" value of json key array is used by jQuery automatically as text for combo box
3967 $out .= $opt;
3968 array_push($outarray, $optJson);
3969 }
3970 }
3971 } else {
3972 if (isModEnabled('dynamicprices') && !empty($objp->fk_price_expression)) {
3973 $price_product = new Product($this->db);
3974 $price_product->fetch($objp->rowid, '', '', '1');
3975
3976 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3977 $priceparser = new PriceParser($this->db);
3978 $price_result = $priceparser->parseProduct($price_product);
3979 if ($price_result >= 0) {
3980 $objp->price = $price_result;
3981 $objp->unitprice = $price_result;
3982 //Calculate the VAT
3983 $objp->price_ttc = (float) price2num($objp->price) * (1 + ($objp->tva_tx / 100));
3984 $objp->price_ttc = price2num($objp->price_ttc, 'MU');
3985 }
3986 }
3987 if (getDolGlobalInt('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES') && !empty($objp->custprice)) {
3988 $price_level = '';
3989 }
3990 $this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
3991 // Add new entry
3992 // "key" value of json key array is used by jQuery automatically as selected value
3993 // "label" value of json key array is used by jQuery automatically as text for combo box
3994 $out .= $opt;
3995 array_push($outarray, $optJson);
3996 }
3997
3998 $i++;
3999 }
4000
4001 $out .= '</select>';
4002
4003 $this->db->free($result);
4004
4005 if (empty($outputmode)) {
4006 return $out;
4007 }
4008
4009 return $outarray;
4010 } else {
4011 dol_print_error($this->db);
4012 }
4013
4014 return '';
4015 }
4016
4032 protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
4033 {
4034 global $langs, $conf, $user;
4035 global $hookmanager;
4036
4037 $outkey = '';
4038 $outval = '';
4039 $outref = '';
4040 $outlabel = '';
4041 $outlabel_translated = '';
4042 $outdesc = '';
4043 $outdesc_translated = '';
4044 $outbarcode = '';
4045 $outorigin = '';
4046 $outtype = '';
4047 $outprice_ht = '';
4048 $outprice_ttc = '';
4049 $outpricebasetype = '';
4050 $outtva_tx = '';
4051 $outdefault_vat_code = '';
4052 $outqty = 1;
4053 $outdiscount = '0';
4054
4055 $maxlengtharticle = getDolGlobalInt('PRODUCT_MAX_LENGTH_COMBO', 48);
4056
4057 $productlabel = $objp->label;
4058 if (!empty($objp->label_translated)) {
4059 $productlabel = $objp->label_translated;
4060 }
4061 $label = $productlabel;
4062 if (!empty($filterkey) && $filterkey != '') {
4063 $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
4064 }
4065
4066 $outkey = $objp->rowid;
4067 $outref = $objp->ref;
4068 $outrefcust = empty($objp->custref) ? '' : $objp->custref;
4069 $outlabel = $objp->label;
4070 $outdesc = $objp->description;
4071 if (getDolGlobalInt('MAIN_MULTILANGS')) {
4072 $outlabel_translated = $objp->label_translated;
4073 $outdesc_translated = $objp->description_translated;
4074 }
4075 $outbarcode = $objp->barcode;
4076 $outorigin = $objp->fk_country;
4077 $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
4078
4079 $outtype = $objp->fk_product_type;
4080 $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
4081 $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
4082
4083 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4084 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
4085 }
4086
4087 // Units
4088 $outvalUnits = '';
4089 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4090 if (!empty($objp->unit_short)) {
4091 $outvalUnits .= ' - ' . $objp->unit_short;
4092 }
4093 }
4094 if (getDolGlobalString('PRODUCT_SHOW_DIMENSIONS_IN_COMBO')) {
4095 if (!empty($objp->weight) && $objp->weight_units !== null) {
4096 $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
4097 $outvalUnits .= ' - ' . $unitToShow;
4098 }
4099 if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
4100 $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
4101 $outvalUnits .= ' - ' . $unitToShow;
4102 }
4103 if (!empty($objp->surface) && $objp->surface_units !== null) {
4104 $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
4105 $outvalUnits .= ' - ' . $unitToShow;
4106 }
4107 if (!empty($objp->volume) && $objp->volume_units !== null) {
4108 $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
4109 $outvalUnits .= ' - ' . $unitToShow;
4110 }
4111 }
4112 if ($outdurationvalue && $outdurationunit) {
4113 $da = array(
4114 'h' => $langs->trans('Hour'),
4115 'd' => $langs->trans('Day'),
4116 'w' => $langs->trans('Week'),
4117 'm' => $langs->trans('Month'),
4118 'y' => $langs->trans('Year')
4119 );
4120 if (isset($da[$outdurationunit])) {
4121 $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
4122 }
4123 }
4124
4125 // Set stocktag (stock too low or not or unknown)
4126 $stocktag = 0;
4127 if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4128 if ($user->hasRight('stock', 'lire')) {
4129 if ($objp->stock > 0) {
4130 $stocktag = 1;
4131 } elseif ($objp->stock <= 0) {
4132 $stocktag = -1;
4133 }
4134 }
4135 }
4136
4137 // Set full plain label for the native <option> text. Select2 uses this text
4138 // as its search corpus, while data-html below keeps the visible label short.
4139 $labeltosearch = '';
4140 $labeltosearch .= $objp->ref;
4141 if (!empty($objp->custref)) {
4142 $labeltosearch .= ' (' . $objp->custref . ')';
4143 }
4144 if ($outbarcode) {
4145 $labeltosearch .= ' (' . $outbarcode . ')';
4146 }
4147 $labeltosearch .= ' - ' . $productlabel;
4148 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4149 $labeltosearch .= ' (' . getCountry($outorigin, '1') . ')';
4150 }
4151
4152 // Set $labltoshowhtml
4153 $labeltoshowhtml = '';
4154 $labeltoshowhtml .= $objp->ref;
4155 if (!empty($objp->custref)) {
4156 $labeltoshowhtml .= ' (' . $objp->custref . ')';
4157 }
4158 if (!empty($filterkey) && $filterkey != '') {
4159 $labeltoshowhtml = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $labeltoshowhtml, 1);
4160 }
4161 if ($outbarcode) {
4162 $labeltoshowhtml .= ' (' . $outbarcode . ')';
4163 }
4164 $labeltoshowhtml .= ' - ' . dol_trunc($label, $maxlengtharticle);
4165 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4166 $labeltoshowhtml .= ' (' . getCountry($outorigin, '1') . ')';
4167 }
4168
4169 // Stock
4170 $labeltoshowstock = '';
4171 $labeltoshowhtmlstock = '';
4172 if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4173 if ($user->hasRight('stock', 'lire')) {
4174 $labeltoshowstock .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4175
4176 if ($objp->stock > 0) {
4177 $labeltoshowhtmlstock .= ' - <span class="product_line_stock_ok">';
4178 } elseif ($objp->stock <= 0) {
4179 $labeltoshowhtmlstock .= ' - <span class="product_line_stock_too_low">';
4180 }
4181 $labeltoshowhtmlstock .= $langs->transnoentities("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4182 $labeltoshowhtmlstock .= '</span>';
4183
4184 if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) { // Warning, this option may slow down combo list generation
4185 $langs->load("stocks");
4186
4187 $tmpproduct = new Product($this->db);
4188 $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
4189 $tmpproduct->load_virtual_stock();
4190 $virtualstock = $tmpproduct->stock_theorique;
4191
4192 $labeltoshowstock .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
4193
4194 $labeltoshowhtmlstock .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
4195 if ($virtualstock > 0) {
4196 $labeltoshowhtmlstock .= '<span class="product_line_stock_ok">';
4197 } elseif ($virtualstock <= 0) {
4198 $labeltoshowhtmlstock .= '<span class="product_line_stock_too_low">';
4199 }
4200 $labeltoshowhtmlstock .= $virtualstock;
4201 $labeltoshowhtmlstock .= '</span>';
4202
4203 unset($tmpproduct);
4204 }
4205 }
4206 }
4207
4208 // Price
4209 $found = 0;
4210 $labeltoshowprice = '';
4211 $labeltoshowhtmlprice = '';
4212 // If we need a particular price level (from 1 to n)
4213 if (empty($hidepriceinlabel) && $price_level >= 1 && (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES'))) {
4214 $sql = "SELECT price, price_ttc, price_base_type, tva_tx, default_vat_code";
4215 $sql .= " FROM " . $this->db->prefix() . "product_price";
4216 $sql .= " WHERE fk_product = " . ((int) $objp->rowid);
4217 $sql .= " AND entity IN (" . getEntity('productprice') . ")";
4218 $sql .= " AND price_level = " . ((int) $price_level);
4219 $sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
4220 $sql .= " LIMIT 1";
4221
4222 dol_syslog(get_class($this) . '::constructProductListOption search price for product ' . $objp->rowid . ' AND level ' . $price_level, LOG_DEBUG);
4223 $result2 = $this->db->query($sql);
4224 if ($result2) {
4225 $objp2 = $this->db->fetch_object($result2);
4226 if ($objp2) {
4227 $found = 1;
4228 if ($objp2->price_base_type == 'HT') {
4229 $labeltoshowprice .= ' - ' . price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4230 $labeltoshowhtmlprice .= ' - ' . price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4231 } else {
4232 $labeltoshowprice .= ' - ' . price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4233 $labeltoshowhtmlprice .= ' - ' . price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4234 }
4235 $outprice_ht = price($objp2->price);
4236 $outprice_ttc = price($objp2->price_ttc);
4237 $outpricebasetype = $objp2->price_base_type;
4238 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
4239 $outtva_tx = $objp2->tva_tx; // We use the vat rate on line of multiprice
4240 $outdefault_vat_code = $objp2->default_vat_code; // We use the vat code on line of multiprice
4241 } else {
4242 $outtva_tx = $objp->tva_tx; // We use the vat rate of product, not the one on line of multiprice
4243 $outdefault_vat_code = $objp->default_vat_code; // We use the vat code or product, not the one on line of multiprice
4244 }
4245 }
4246 } else {
4247 dol_print_error($this->db);
4248 }
4249 }
4250
4251 // Price by quantity
4252 if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1 && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES'))) {
4253 $found = 1;
4254 $outqty = $objp->quantity;
4255 $outdiscount = $objp->remise_percent;
4256 if ($objp->quantity == 1) {
4257 $labeltoshowprice .= ' - ' . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/";
4258 $labeltoshowhtmlprice .= ' - ' . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/";
4259 $labeltoshowprice .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
4260 $labeltoshowhtmlprice .= $langs->transnoentities("Unit");
4261 } else {
4262 $labeltoshowprice .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4263 $labeltoshowhtmlprice .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4264 $labeltoshowprice .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
4265 $labeltoshowhtmlprice .= $langs->transnoentities("Units");
4266 }
4267
4268 $outprice_ht = price($objp->unitprice);
4269 $outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
4270 $outpricebasetype = $objp->price_base_type;
4271 $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
4272 $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
4273 }
4274 if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
4275 $labeltoshowprice .= " (" . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
4276 $labeltoshowhtmlprice .= " (" . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->transnoentities("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
4277 }
4278 if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
4279 $labeltoshowprice .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4280 $labeltoshowhtmlprice .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4281 }
4282
4283 // Price by customer
4284 if (empty($hidepriceinlabel) && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES'))) {
4285 if (!empty($objp->idprodcustprice)) {
4286 $found = 1;
4287
4288 if ($objp->custprice_base_type == 'HT') {
4289 $labeltoshowprice .= ' - ' . price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4290 $labeltoshowhtmlprice .= ' - ' . price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4291 } else {
4292 $labeltoshowprice .= ' - ' . price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4293 $labeltoshowhtmlprice .= ' - ' . price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4294 }
4295
4296 $outprice_ht = price($objp->custprice);
4297 $outprice_ttc = price($objp->custprice_ttc);
4298 $outpricebasetype = $objp->custprice_base_type;
4299 $outtva_tx = $objp->custtva_tx;
4300 $outdefault_vat_code = $objp->custdefault_vat_code;
4301 $outdiscount = $objp->custdiscount_percent;
4302 }
4303 }
4304
4305 // If level no defined or multiprice not found, we used the default price
4306 if (empty($hidepriceinlabel) && !$found) {
4307 if ($objp->price_base_type == 'HT') {
4308 $labeltoshowprice .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4309 $labeltoshowhtmlprice .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4310 } else {
4311 $labeltoshowprice .= ' - ' . price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4312 $labeltoshowhtmlprice .= ' - ' . price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4313 }
4314 $outprice_ht = price($objp->price);
4315 $outprice_ttc = price($objp->price_ttc);
4316 $outpricebasetype = $objp->price_base_type;
4317 $outtva_tx = $objp->tva_tx;
4318 $outdefault_vat_code = $objp->default_vat_code;
4319 }
4320
4321 $optiontext = $labeltosearch.$outvalUnits.$labeltoshowprice.$labeltoshowstock;
4322 $optionhtml = $labeltoshowhtml.$outvalUnits.$labeltoshowhtmlprice.$labeltoshowhtmlstock;
4323 $optionhtmlforattribute = dol_escape_htmltag($optionhtml, 0, 0, '', 0, 1);
4324
4325 // Build options
4326 $opt = '<option value="' . $objp->rowid . '"';
4327 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
4328 if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
4329 $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 . '"';
4330 }
4331 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
4332 $opt .= ' data-labeltrans="' . dol_escape_htmltag($outlabel_translated, 0, 0, '', 0, 1) . '"';
4333 $opt .= ' data-desctrans="' . dol_escape_htmltag($outdesc_translated) . '"';
4334 }
4335
4336 if ($stocktag == 1) {
4337 $opt .= ' class="product_line_stock_ok" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml, 0, array('strong')).dolPrintHTMLForAttribute($outvalUnits).$labeltoshowhtmlprice.dolPrintHTMLForAttribute($labeltoshowhtmlstock).'"';
4338 //$opt .= ' class="product_line_stock_ok"';
4339 }
4340 if ($stocktag == -1) {
4341 $opt .= ' class="product_line_stock_too_low" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml, 0, array('strong')).dolPrintHTMLForAttribute($outvalUnits).$labeltoshowhtmlprice.dolPrintHTMLForAttribute($labeltoshowhtmlstock).'"';
4342 //$opt .= ' class="product_line_stock_too_low"';
4343 }
4344 $opt .= ' data-html="'.$optionhtmlforattribute.'" data-select-html="'.$optionhtmlforattribute.'"';
4345
4346 $opt .= '>';
4347
4348 // Ref, barcode, country
4349 $opt .= dol_escape_htmltag($optiontext, 0, 0, '', 0, 1);
4350 $outval .= $labeltoshowhtml;
4351
4352 // Units
4353 $outval .= $outvalUnits;
4354
4355 // Price
4356 $outval .= $labeltoshowhtmlprice;
4357
4358 // Stock
4359 $outval .= $labeltoshowhtmlstock;
4360
4361
4362 $parameters = array('objp' => $objp);
4363 $reshook = $hookmanager->executeHooks('constructProductListOption', $parameters); // Note that $action and $object may have been modified by hook
4364 if (empty($reshook)) {
4365 $opt .= $hookmanager->resPrint;
4366 } else {
4367 $opt = $hookmanager->resPrint;
4368 }
4369
4370 $opt .= "</option>\n";
4371 $optJson = array(
4372 'key' => $outkey,
4373 'value' => $outref,
4374 'label' => $outval,
4375 'label2' => $outlabel,
4376 'desc' => $outdesc,
4377 'type' => $outtype,
4378 'price_ht' => price2num($outprice_ht),
4379 'price_ttc' => price2num($outprice_ttc),
4380 'price_ht_locale' => price(price2num($outprice_ht)),
4381 'price_ttc_locale' => price(price2num($outprice_ttc)),
4382 'pricebasetype' => $outpricebasetype,
4383 'tva_tx' => $outtva_tx,
4384 'default_vat_code' => $outdefault_vat_code,
4385 'qty' => $outqty,
4386 'discount' => $outdiscount,
4387 'duration_value' => $outdurationvalue,
4388 'duration_unit' => $outdurationunit,
4389 'pbq' => $outpbq,
4390 'labeltrans' => $outlabel_translated,
4391 'desctrans' => $outdesc_translated,
4392 'ref_customer' => $outrefcust
4393 );
4394 }
4395
4396 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4397
4414 public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $notused = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '', $nooutput = 0)
4415 {
4416 // phpcs:enable
4417 global $langs, $conf;
4418 global $price_level, $status, $finished;
4419
4420 if (!isset($status)) {
4421 $status = 1;
4422 }
4423
4424 $selected_input_value = '';
4425 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
4426 $regtmpsel = array();
4427 if ((int) $selected > 0) {
4428 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4429 $producttmpselect = new Product($this->db);
4430 $producttmpselect->fetch((int) $selected);
4431 $selected_input_value = $producttmpselect->ref;
4432 unset($producttmpselect);
4433 } elseif (preg_match('/^idprod_([0-9]+)$/', (string) $selected, $regtmpsel)) {
4434 // Preselect when a product without supplier price was just created ('idprod_ID' value, used by backtopage of creation popup)
4435 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
4436 $producttmpselect = new Product($this->db);
4437 $producttmpselect->fetch((int) $regtmpsel[1]);
4438 $selected_input_value = $producttmpselect->ref;
4439 unset($producttmpselect);
4440 }
4441
4442 // mode=2 means suppliers products
4443 $urloption = ($socid > 0 ? 'socid=' . $socid . '&' : '') . 'htmlname=' . $htmlname . '&outjson=1&price_level=' . $price_level . '&type=' . $filtertype . '&mode=2&status=' . $status . '&finished=' . $finished . '&alsoproductwithnosupplierprice=' . $alsoproductwithnosupplierprice;
4444
4445 $s = ($hidelabel ? '' : $langs->trans("RefOrLabel") . ' : ') . '<input type="text" class="'.$morecss.'" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . $placeholder . '"' : '') . ' spellcheck="false">';
4446
4447 $s .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/product/ajax/products.php', $urloption, getDolGlobalInt('PRODUIT_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
4448 } else {
4449 $s = $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $notused, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, getDolGlobalInt('SUPPLIER_SHOW_STOCK_IN_PRODUCTS_COMBO'), $placeholder);
4450 }
4451
4452 if ($nooutput) {
4453 return $s;
4454 } else {
4455 print $s;
4456 }
4457 }
4458
4459 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4460
4479 public function select_produits_fournisseurs_list($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $notused = '', $filterkey = '', $statut = -1, $outputmode = 0, $limit = 100, $alsoproductwithnosupplierprice = 0, $morecss = '', $showstockinlist = 0, $placeholder = '')
4480 {
4481 // phpcs:enable
4482 global $langs, $conf, $user;
4483 global $hookmanager;
4484
4485 $out = '';
4486 $outarray = array();
4487
4488 $maxlengtharticle = getDolGlobalInt('PRODUCT_MAX_LENGTH_COMBO', 48);
4489
4490 $langs->load('stocks');
4491 // Units
4492 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4493 $langs->load('other');
4494 }
4495
4496 $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,";
4497 $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice, pfp.barcode";
4498 $sql .= ", pfp.multicurrency_code, pfp.multicurrency_unitprice";
4499 $sql .= ", pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.default_vat_code, pfp.fk_soc, s.nom as name";
4500 $sql .= ", pfp.supplier_reputation";
4501 // if we use supplier description of the products
4502 if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
4503 $sql .= ", pfp.desc_fourn as description";
4504 } else {
4505 $sql .= ", p.description";
4506 }
4507 // Units
4508 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4509 $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";
4510 }
4511
4512 // Add select from hooks
4513 $parameters = [];
4514 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
4515 $sql .= $hookmanager->resPrint;
4516
4517 $sql .= " FROM " . $this->db->prefix() . "product as p";
4518
4519 // Add join from hooks
4520 $parameters = [];
4521 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
4522 $sql .= $hookmanager->resPrint;
4523
4524 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (" . getEntity('product') . ") )";
4525 if ($socid > 0) {
4526 $sql .= " AND pfp.fk_soc = " . ((int) $socid);
4527 }
4528 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
4529 // Units
4530 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4531 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units u ON u.rowid = p.fk_unit";
4532 }
4533 $sql .= " WHERE p.entity IN (" . getEntity('product') . ")";
4534 if ($statut != -1) {
4535 $sql .= " AND p.tobuy = " . ((int) $statut);
4536 }
4537 if (strval($filtertype) != '') {
4538 $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
4539 }
4540
4541 // Add where from hooks
4542 $parameters = array();
4543 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
4544 $sql .= $hookmanager->resPrint;
4545 // Add criteria on ref/label
4546 if ($filterkey != '') {
4547 $sql .= ' AND (';
4548 $prefix = getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
4549 // For natural search
4550 $search_crit = explode(' ', $filterkey);
4551 $i = 0;
4552 if (count($search_crit) > 1) {
4553 $sql .= "(";
4554 }
4555 foreach ($search_crit as $crit) {
4556 if ($i > 0) {
4557 $sql .= " AND ";
4558 }
4559 $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) . "%'";
4560 if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
4561 $sql .= " OR pfp.desc_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
4562 }
4563 $sql .= ")";
4564 $i++;
4565 }
4566 if (count($search_crit) > 1) {
4567 $sql .= ")";
4568 }
4569 if (isModEnabled('barcode')) {
4570 $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
4571 $sql .= " OR pfp.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
4572 }
4573 $sql .= ')';
4574 }
4575 $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
4576 $sql .= $this->db->plimit($limit, 0);
4577
4578 // Build output string
4579
4580 dol_syslog(get_class($this) . "::select_produits_fournisseurs_list", LOG_DEBUG);
4581 $result = $this->db->query($sql);
4582 if ($result) {
4583 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4584 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
4585
4586 $num = $this->db->num_rows($result);
4587
4588 //$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">'; // remove select to have id same with combo and ajax
4589 $out .= '<select class="flat ' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '">';
4590 if (!$selected) {
4591 $out .= '<option value="-1" selected>' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
4592 } else {
4593 $out .= '<option value="-1">' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
4594 }
4595
4596 $i = 0;
4597 while ($i < $num) {
4598 $objp = $this->db->fetch_object($result);
4599
4600 if (is_null($objp->idprodfournprice)) {
4601 // There is no supplier price found, we will use the vat rate for sale
4602 $objp->tva_tx = $objp->tva_tx_sale;
4603 $objp->default_vat_code = $objp->default_vat_code_sale;
4604 }
4605
4606 $outkey = $objp->idprodfournprice; // id in table of price
4607 if (!$outkey && $alsoproductwithnosupplierprice) {
4608 $outkey = 'idprod_' . $objp->rowid; // id of product
4609 }
4610
4611 $outref = $objp->ref;
4612 $outbarcode = $objp->barcode;
4613 $outqty = 1;
4614 $outdiscount = 0;
4615 $outtype = $objp->fk_product_type;
4616 $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
4617 $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
4618
4619 // Units
4620 $outvalUnits = '';
4621 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4622 if (!empty($objp->unit_short)) {
4623 $outvalUnits .= ' - ' . $objp->unit_short;
4624 }
4625 if (!empty($objp->weight) && $objp->weight_units !== null) {
4626 $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
4627 $outvalUnits .= ' - ' . $unitToShow;
4628 }
4629 if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
4630 $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
4631 $outvalUnits .= ' - ' . $unitToShow;
4632 }
4633 if (!empty($objp->surface) && $objp->surface_units !== null) {
4634 $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
4635 $outvalUnits .= ' - ' . $unitToShow;
4636 }
4637 if (!empty($objp->volume) && $objp->volume_units !== null) {
4638 $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
4639 $outvalUnits .= ' - ' . $unitToShow;
4640 }
4641 if ($outdurationvalue && $outdurationunit) {
4642 $da = array(
4643 'h' => $langs->trans('Hour'),
4644 'd' => $langs->trans('Day'),
4645 'w' => $langs->trans('Week'),
4646 'm' => $langs->trans('Month'),
4647 'y' => $langs->trans('Year')
4648 );
4649 if (isset($da[$outdurationunit])) {
4650 $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
4651 }
4652 }
4653 }
4654
4655 $objRef = $objp->ref;
4656 if ($filterkey && $filterkey != '') {
4657 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
4658 }
4659 $objRefFourn = $objp->ref_fourn;
4660 if ($filterkey && $filterkey != '') {
4661 $objRefFourn = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRefFourn, 1);
4662 }
4663 $label = $objp->label;
4664 if ($filterkey && $filterkey != '') {
4665 $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
4666 }
4667
4668 switch ($objp->fk_product_type) {
4670 $picto = 'product';
4671 break;
4673 $picto = 'service';
4674 break;
4675 default:
4676 $picto = '';
4677 break;
4678 }
4679
4680 if (empty($picto)) {
4681 $optlabel = '';
4682 } else {
4683 $optlabel = img_object('', $picto, 'class="paddingright classfortooltip"', 0, 0, 1);
4684 }
4685
4686 $optlabel .= $objp->ref;
4687 if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
4688 $optlabel .= ' <span class="opacitymedium">(' . $objp->ref_fourn . ')</span>';
4689 }
4690 if (isModEnabled('barcode') && !empty($objp->barcode)) {
4691 $optlabel .= ' (' . $outbarcode . ')';
4692 }
4693 $optlabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
4694
4695 $outvallabel = $objRef;
4696 if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
4697 $outvallabel .= ' (' . $objRefFourn . ')';
4698 }
4699 if (isModEnabled('barcode') && !empty($objp->barcode)) {
4700 $outvallabel .= ' (' . $outbarcode . ')';
4701 }
4702 $outvallabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
4703
4704 $outsearchlabel = implode(' ', array_filter(array(
4705 (string) $objp->ref,
4706 (string) $objp->ref_fourn,
4707 (string) $objp->barcode,
4708 (string) $objp->label,
4709 dol_string_nohtmltag((string) $objp->description)
4710 ), function (string $value): bool {
4711 return $value !== '';
4712 }));
4713
4714 // Units
4715 $optlabel .= $outvalUnits;
4716 $outvallabel .= $outvalUnits;
4717
4718 if (!empty($objp->idprodfournprice)) {
4719 $outqty = $objp->quantity;
4720 $outdiscount = $objp->remise_percent;
4721 if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
4722 $prod_supplier = new ProductFournisseur($this->db);
4723 $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
4724 $prod_supplier->id = $objp->fk_product;
4725 $prod_supplier->fourn_qty = $objp->quantity;
4726 $prod_supplier->fourn_tva_tx = $objp->tva_tx;
4727 $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
4728
4729 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4730 $priceparser = new PriceParser($this->db);
4731 $price_result = $priceparser->parseProductSupplier($prod_supplier);
4732 if ($price_result >= 0) {
4733 $objp->fprice = $price_result;
4734 if ($objp->quantity >= 1) {
4735 $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
4736 }
4737 }
4738 }
4739 if ($objp->quantity == 1) {
4740 $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
4741 $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/";
4742 $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
4743 $outvallabel .= $langs->transnoentities("Unit");
4744 } else {
4745 $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4746 $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4747 $optlabel .= ' ' . $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
4748 $outvallabel .= ' ' . $langs->transnoentities("Units");
4749 }
4750
4751 if ($objp->quantity != 1) {
4752 $optlabel .= " (" . price($objp->unitprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
4753 $outvallabel .= " (" . price($objp->unitprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->transnoentities("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
4754 }
4755 if ($objp->remise_percent >= 1) {
4756 $optlabel .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4757 $outvallabel .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4758 }
4759 if ($objp->duration) {
4760 $optlabel .= " - " . $objp->duration;
4761 $outvallabel .= " - " . $objp->duration;
4762 }
4763 if (!$socid) {
4764 $optlabel .= " - " . dol_trunc($objp->name, 8);
4765 $outvallabel .= " - " . dol_trunc($objp->name, 8);
4766 }
4767 if ($objp->supplier_reputation) {
4768 //TODO dictionary
4769 $reputations = array('' => $langs->trans('Standard'), 'FAVORITE' => $langs->trans('Favorite'), 'NOTTHGOOD' => $langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER' => $langs->trans('DoNotOrderThisProductToThisSupplier'));
4770
4771 $optlabel .= " - " . $reputations[$objp->supplier_reputation];
4772 $outvallabel .= " - " . $reputations[$objp->supplier_reputation];
4773 }
4774 } else {
4775 $optlabel .= " - <span class='opacitymedium'>" . $langs->trans("NoPriceDefinedForThisSupplier") . '</span>';
4776 $outvallabel .= ' - ' . $langs->transnoentities("NoPriceDefinedForThisSupplier");
4777 }
4778
4779 if (isModEnabled('stock') && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4780 $novirtualstock = ($showstockinlist == 2);
4781
4782 if ($user->hasRight('stock', 'lire')) {
4783 $outvallabel .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4784
4785 if ($objp->stock > 0) {
4786 $optlabel .= ' - <span class="product_line_stock_ok">';
4787 } elseif ($objp->stock <= 0) {
4788 $optlabel .= ' - <span class="product_line_stock_too_low">';
4789 }
4790 $optlabel .= $langs->transnoentities("Stock") . ':' . price(price2num($objp->stock, 'MS'));
4791 $optlabel .= '</span>';
4792 if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) { // Warning, this option may slow down combo list generation
4793 $langs->load("stocks");
4794
4795 $tmpproduct = new Product($this->db);
4796 $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
4797 $tmpproduct->load_virtual_stock();
4798 $virtualstock = $tmpproduct->stock_theorique;
4799
4800 $outvallabel .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
4801
4802 $optlabel .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
4803 if ($virtualstock > 0) {
4804 $optlabel .= '<span class="product_line_stock_ok">';
4805 } elseif ($virtualstock <= 0) {
4806 $optlabel .= '<span class="product_line_stock_too_low">';
4807 }
4808 $optlabel .= $virtualstock;
4809 $optlabel .= '</span>';
4810
4811 unset($tmpproduct);
4812 }
4813 }
4814 }
4815
4816 $optstart = '<option value="' . $outkey . '"';
4817 if ($selected && preg_match('/^idprod_/', (string) $selected) && (string) $selected == 'idprod_'.$objp->rowid) {
4818 $optstart .= ' selected';
4819 } elseif ($selected && (string) $selected == (string) $objp->idprodfournprice) {
4820 $optstart .= ' selected';
4821 }
4822
4823 if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
4824 $optstart .= ' disabled';
4825 }
4826
4827 if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
4828 $optstart .= ' data-product-id="' . dol_escape_htmltag($objp->rowid) . '"';
4829 $optstart .= ' data-price-id="' . dol_escape_htmltag($objp->idprodfournprice) . '"';
4830 $optstart .= ' data-qty="' . dol_escape_htmltag($objp->quantity) . '"';
4831 $optstart .= ' data-up="' . dol_escape_htmltag(price2num($objp->unitprice)) . '"'; // the price with numeric international format
4832 $optstart .= ' data-up-locale="' . dol_escape_htmltag(price($objp->unitprice)) . '"'; // the price formatted in user language
4833 $optstart .= ' data-discount="' . dol_escape_htmltag((string) $outdiscount) . '"';
4834 $optstart .= ' data-tvatx="' . dol_escape_htmltag(price2num($objp->tva_tx)) . '"'; // the rate with numeric international format
4835 $optstart .= ' data-tvatx-formated="' . dol_escape_htmltag(price($objp->tva_tx, 0, $langs, 1, -1, 2)) . '"'; // the rate formatted in user language
4836 $optstart .= ' data-default-vat-code="' . dol_escape_htmltag($objp->default_vat_code) . '"';
4837 $optstart .= ' data-supplier-ref="' . dol_escape_htmltag($objp->ref_fourn) . '"';
4838 if (isModEnabled('multicurrency')) {
4839 $optstart .= ' data-multicurrency-code="' . dol_escape_htmltag($objp->multicurrency_code) . '"';
4840 $optstart .= ' data-multicurrency-unitprice="' . dol_escape_htmltag(price2num($objp->multicurrency_unitprice)) . '"'; // the price with numeric international format
4841 }
4842 }
4843 $optstart .= ' data-description="' . dol_escape_htmltag($objp->description, 0, 1) . '"';
4844 $optstart .= ' data-search="' . dol_escape_htmltag($outsearchlabel) . '"';
4845
4846 // set $parameters to call hook
4847 $outarrayentry = array(
4848 'key' => $outkey,
4849 'value' => $outref,
4850 'label' => $outvallabel,
4851 'labelhtml' => $optlabel,
4852 'qty' => $outqty,
4853 'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
4854 'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
4855 'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
4856 'tva_tx_formated' => price($objp->tva_tx, 0, $langs, 1, -1, 2),
4857 'tva_tx' => price2num($objp->tva_tx),
4858 'default_vat_code' => $objp->default_vat_code,
4859 'supplier_ref' => $objp->ref_fourn,
4860 'discount' => $outdiscount,
4861 'type' => $outtype,
4862 'duration_value' => $outdurationvalue,
4863 'duration_unit' => $outdurationunit,
4864 'disabled' => empty($objp->idprodfournprice),
4865 'description' => $objp->description
4866 );
4867 if (isModEnabled('multicurrency')) {
4868 $outarrayentry['multicurrency_code'] = $objp->multicurrency_code;
4869 $outarrayentry['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4870 }
4871 $parameters = array(
4872 'objp' => &$objp,
4873 'optstart' => &$optstart,
4874 'optlabel' => &$optlabel,
4875 'outvallabel' => &$outvallabel,
4876 'outarrayentry' => &$outarrayentry,
4877 'fk_soc' => $socid
4878 );
4879 $reshook = $hookmanager->executeHooks('selectProduitsFournisseurListOption', $parameters, $this);
4880
4881
4882 // Add new entry
4883 // "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
4884 // "label" value of json key array is used by jQuery automatically as text for combo box
4885 $out .= $optstart . ' data-html="' . dol_escape_htmltag($optlabel) . '">' . $optlabel . "</option>\n";
4886 $outarraypush = array(
4887 'key' => $outkey,
4888 'value' => $outref,
4889 'label' => $outvallabel,
4890 'labelhtml' => $optlabel,
4891 'qty' => $outqty,
4892 'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
4893 'price_qty_ht_locale' => price($objp->fprice),
4894 'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
4895 'price_unit_ht_locale' => price($objp->unitprice),
4896 'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
4897 'tva_tx_formated' => price($objp->tva_tx),
4898 'tva_tx' => price2num($objp->tva_tx),
4899 'default_vat_code' => $objp->default_vat_code,
4900 'supplier_ref' => $objp->ref_fourn,
4901 'discount' => $outdiscount,
4902 'type' => $outtype,
4903 'duration_value' => $outdurationvalue,
4904 'duration_unit' => $outdurationunit,
4905 'disabled' => empty($objp->idprodfournprice),
4906 'description' => $objp->description
4907 );
4908 if (isModEnabled('multicurrency')) {
4909 $outarraypush['multicurrency_code'] = $objp->multicurrency_code;
4910 $outarraypush['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4911 }
4912 array_push($outarray, $outarraypush);
4913
4914 // Example of var_dump $outarray
4915 // array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
4916 // ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/unit (20,00 Euros/unit)"
4917 // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
4918 //}
4919 //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4920 //$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unit (20,00 Euros/unit)');
4921 //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4922
4923 $i++;
4924 }
4925 $out .= '</select>';
4926
4927 $this->db->free($result);
4928
4929 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
4930 $out .= ajax_combobox($htmlname);
4931 } else {
4932 dol_print_error($this->db);
4933 }
4934
4935 if (empty($outputmode)) {
4936 return $out;
4937 }
4938 return $outarray;
4939 }
4940
4941 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4942
4951 public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = 0)
4952 {
4953 // phpcs:enable
4954 global $langs, $conf;
4955
4956 $langs->load('stocks');
4957
4958 $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
4959 $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
4960 $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
4961 $sql .= " FROM " . $this->db->prefix() . "product as p";
4962 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
4963 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
4964 $sql .= " WHERE pfp.entity IN (" . getEntity('productsupplierprice') . ")";
4965 $sql .= " AND p.tobuy = 1";
4966 $sql .= " AND s.fournisseur = 1";
4967 $sql .= " AND p.rowid = " . ((int) $productid);
4968 if (!getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED')) {
4969 $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
4970 } else {
4971 $sql .= " ORDER BY pfp.unitprice - pfp.unitprice * pfp.remise_percent / 100 ASC";
4972 }
4973
4974 dol_syslog(get_class($this) . "::select_product_fourn_price", LOG_DEBUG);
4975 $result = $this->db->query($sql);
4976
4977 if ($result) {
4978 $num = $this->db->num_rows($result);
4979
4980 $form = '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4981
4982 if (!$num) {
4983 $form .= '<option value="0">-- ' . $langs->trans("NoSupplierPriceDefinedForThisProduct") . ' --</option>';
4984 } else {
4985 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4986 $form .= '<option value="0">&nbsp;</option>';
4987
4988 $i = 0;
4989 while ($i < $num) {
4990 $objp = $this->db->fetch_object($result);
4991
4992 $opt = '<option value="' . $objp->idprodfournprice . '"';
4993 //if there is only one supplier, preselect it
4994 if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier) || ($i == 0 && getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED'))) {
4995 $opt .= ' selected';
4996 }
4997 $opt .= '>' . $objp->name . ' - ' . $objp->ref_fourn . ' - ';
4998
4999 if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
5000 $prod_supplier = new ProductFournisseur($this->db);
5001 $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
5002 $prod_supplier->id = $productid;
5003 $prod_supplier->fourn_qty = $objp->quantity;
5004 $prod_supplier->fourn_tva_tx = $objp->tva_tx;
5005 $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
5006
5007 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
5008 $priceparser = new PriceParser($this->db);
5009 $price_result = $priceparser->parseProductSupplier($prod_supplier);
5010 if ($price_result >= 0) {
5011 $objp->fprice = $price_result;
5012 if ($objp->quantity >= 1) {
5013 $objp->unitprice = $objp->fprice / $objp->quantity;
5014 }
5015 }
5016 }
5017 if ($objp->quantity == 1) {
5018 $opt .= price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
5019 }
5020
5021 $opt .= $objp->quantity . ' ';
5022
5023 if ($objp->quantity == 1) {
5024 $opt .= $langs->trans("Unit");
5025 } else {
5026 $opt .= $langs->trans("Units");
5027 }
5028 if ($objp->quantity > 1) {
5029 $opt .= " - ";
5030 $opt .= price($objp->unitprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit");
5031 }
5032 if ($objp->duration) {
5033 $opt .= " - " . $objp->duration;
5034 }
5035 $opt .= "</option>\n";
5036
5037 $form .= $opt;
5038 $i++;
5039 }
5040 }
5041
5042 $form .= '</select>';
5043 $this->db->free($result);
5044 return $form;
5045 } else {
5046 dol_print_error($this->db);
5047 return '';
5048 }
5049 }
5050
5051
5052 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5059 {
5060 // phpcs:enable
5061 global $langs, $hookmanager;
5062
5063 $num = count($this->cache_conditions_paiements);
5064 if ($num > 0) {
5065 return 0; // Cache already loaded
5066 }
5067
5068 dol_syslog(__METHOD__, LOG_DEBUG);
5069
5070 $this->cache_conditions_paiements = array();
5071
5072 $sql = "SELECT rowid, code, libelle as label, deposit_percent, entity";
5073 $sql .= " FROM " . $this->db->prefix() . 'c_payment_term';
5074 $sql .= " WHERE entity IN (" . getEntity('c_payment_term') . ")";
5075 $sql .= " AND active > 0";
5076 $sql .= " ORDER BY sortorder";
5077
5078 $resql = $this->db->query($sql);
5079 if ($resql) {
5080 $num = $this->db->num_rows($resql);
5081 $i = 0;
5082 while ($i < $num) {
5083 $obj = $this->db->fetch_object($resql);
5084
5085 // If a translation exists, we use it, otherwise, we take the label by default
5086 $label = ($langs->trans("PaymentConditionShort" . $obj->code) != "PaymentConditionShort" . $obj->code ? $langs->trans("PaymentConditionShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5087
5088 $this->cache_conditions_paiements[$obj->rowid]['code'] = (string) $obj->code;
5089 $this->cache_conditions_paiements[$obj->rowid]['label'] = (string) $label;
5090 $this->cache_conditions_paiements[$obj->rowid]['deposit_percent'] = (string) $obj->deposit_percent;
5091 $this->cache_conditions_paiements[$obj->rowid]['entity'] = (int) $obj->entity;
5092
5093 $i++;
5094 }
5095
5096 $parameters = array('dictionary' => 'paymentterm');
5097 $reshook = $hookmanager->executeHooks('loadDictionaryCache', $parameters, $this); // Note that $action and $object may have been modified by hook
5098 if (empty($reshook)) {
5099 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
5100 $this->cache_conditions_paiements = array_merge($this->cache_conditions_paiements, $hookmanager->resArray);
5101 }
5102 } else {
5103 $this->cache_conditions_paiements = $hookmanager->resArray;
5104 }
5105
5106 //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table
5107
5108 return $num;
5109 } else {
5110 dol_print_error($this->db);
5111 return -1;
5112 }
5113 }
5114
5115 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5116
5123 {
5124 // phpcs:enable
5125 $factureRec = new FactureRec($this->db);
5126
5127 $this->cache_rule_for_lines_dates = $factureRec->fields['rule_for_lines_dates']['arrayofkeyval'];
5128
5129 if (empty($this->cache_rule_for_lines_dates)) {
5130 return -1;
5131 }
5132
5133 return 1;
5134 }
5135
5136 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5137
5143 public function load_cache_availability()
5144 {
5145 // phpcs:enable
5146 global $langs;
5147
5148 $num = count($this->cache_availability); // TODO Use $conf->cache['availability'] instead of $this->cache_availability
5149 if ($num > 0) {
5150 return 0; // Cache already loaded
5151 }
5152
5153 dol_syslog(__METHOD__, LOG_DEBUG);
5154
5155 $this->cache_availability = array();
5156
5157 $langs->load('propal');
5158
5159 $sql = "SELECT rowid, code, label, position";
5160 $sql .= " FROM " . $this->db->prefix() . 'c_availability';
5161 $sql .= " WHERE active > 0";
5162
5163 $resql = $this->db->query($sql);
5164 if ($resql) {
5165 $num = $this->db->num_rows($resql);
5166 $i = 0;
5167 while ($i < $num) {
5168 $obj = $this->db->fetch_object($resql);
5169
5170 // If a translation exists, we use is, otherwise, we take the label by default
5171 $label = ($langs->trans("AvailabilityType" . $obj->code) != "AvailabilityType" . $obj->code ? $langs->trans("AvailabilityType" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5172 $this->cache_availability[$obj->rowid]['code'] = (string) $obj->code;
5173 $this->cache_availability[$obj->rowid]['label'] = (string) $label;
5174 $this->cache_availability[$obj->rowid]['position'] = (int) $obj->position;
5175 $i++;
5176 }
5177
5178 // @phan-suppress-next-line PhanTypeMismatchProperty PhanTypeMismatchDimFetch
5179 $this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
5180
5181 return $num;
5182 } else {
5183 dol_print_error($this->db);
5184 return -1;
5185 }
5186 }
5187
5199 public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '', $noouput = 0)
5200 {
5201 global $langs, $user;
5202
5203 $this->load_cache_availability();
5204
5205 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
5206
5207 $out = '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5208 if ($addempty) {
5209 $out .= '<option value="-1">'.(is_numeric($addempty) ? '&nbsp;' : $langs->trans($addempty)).'</option>';
5210 }
5211 foreach ($this->cache_availability as $id => $arrayavailability) {
5212 if ($selected == $id) {
5213 $out .= '<option value="' . $id . '" selected>';
5214 } else {
5215 $out .= '<option value="' . $id . '">';
5216 }
5217 $out .= dol_escape_htmltag($arrayavailability['label']);
5218 $out .= '</option>';
5219 }
5220 $out .= '</select>';
5221 if ($user->admin) {
5222 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5223 }
5224 $out .= ajax_combobox($htmlname);
5225
5226 if ($noouput) {
5227 return $out;
5228 } else {
5229 print $out;
5230 return '';
5231 }
5232 }
5233
5239 public function loadCacheInputReason()
5240 {
5241 global $langs;
5242
5243 $num = count($this->cache_demand_reason); // TODO Use $conf->cache['input_reason'] instead of $this->cache_demand_reason
5244 if ($num > 0) {
5245 return 0; // Cache already loaded
5246 }
5247
5248 $sql = "SELECT rowid, code, label";
5249 $sql .= " FROM " . $this->db->prefix() . 'c_input_reason';
5250 $sql .= " WHERE active > 0";
5251
5252 $resql = $this->db->query($sql);
5253 if ($resql) {
5254 $num = $this->db->num_rows($resql);
5255 $i = 0;
5257 $tmparray = array();
5258 while ($i < $num) {
5259 $obj = $this->db->fetch_object($resql);
5260
5261 // If a translation exists, we use is, otherwise, we take the label by default
5262 $label = ($obj->label != '-' ? (string) $obj->label : '');
5263 if ($langs->trans("DemandReasonType" . $obj->code) != "DemandReasonType" . $obj->code) {
5264 $label = $langs->trans("DemandReasonType" . $obj->code); // So translation key DemandReasonTypeSRC_XXX will work
5265 }
5266 if ($langs->trans($obj->code) != $obj->code) {
5267 $label = $langs->trans($obj->code); // So translation key SRC_XXX will work
5268 }
5269
5270 $tmparray[(int) $obj->rowid]
5271 = array(
5272 'id' => (int) $obj->rowid,
5273 'code' => (string) $obj->code,
5274 'label' => $label,
5275 );
5276 $i++;
5277 }
5278
5279 $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
5280
5281 unset($tmparray);
5282 return $num;
5283 } else {
5284 dol_print_error($this->db);
5285 return -1;
5286 }
5287 }
5288
5301 public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
5302 {
5303 global $langs, $user;
5304
5305 $this->loadCacheInputReason();
5306
5307 print '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="select_' . $htmlname . '" name="' . $htmlname . '">';
5308 if ($addempty) {
5309 print '<option value="0"' . (empty($selected) ? ' selected' : '') . '>&nbsp;</option>';
5310 }
5311 foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
5312 if ($arraydemandreason['code'] == $exclude) {
5313 continue;
5314 }
5315
5316 if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
5317 print '<option value="' . $arraydemandreason['id'] . '" selected>';
5318 } else {
5319 print '<option value="' . $arraydemandreason['id'] . '">';
5320 }
5321 $label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
5322 print $langs->trans($label);
5323 print '</option>';
5324 }
5325 print '</select>';
5326 if ($user->admin && empty($notooltip)) {
5327 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5328 }
5329 print ajax_combobox('select_' . $htmlname);
5330 }
5331
5332 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5333
5340 {
5341 // phpcs:enable
5342 global $langs, $hookmanager;
5343
5344 $num = count($this->cache_types_paiements); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_types_paiements
5345 if ($num > 0) {
5346 return $num; // Cache already loaded
5347 }
5348
5349 dol_syslog(__METHOD__, LOG_DEBUG);
5350
5351 $this->cache_types_paiements = array();
5352
5353 $sql = "SELECT id, code, libelle as label, type, entity, active";
5354 $sql .= " FROM " . $this->db->prefix() . "c_paiement";
5355 $sql .= " WHERE entity IN (" . getEntity('c_paiement') . ")";
5356
5357 $resql = $this->db->query($sql);
5358 if ($resql) {
5359 $num = $this->db->num_rows($resql);
5360 $i = 0;
5361 while ($i < $num) {
5362 $obj = $this->db->fetch_object($resql);
5363
5364 // If a translation exists, we use is, otherwise, we take the label by default
5365 $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5366 $this->cache_types_paiements[(int) $obj->id]['id'] = (int) $obj->id;
5367 $this->cache_types_paiements[(int) $obj->id]['code'] = (string) $obj->code;
5368 $this->cache_types_paiements[(int) $obj->id]['label'] = (string) $label;
5369 $this->cache_types_paiements[(int) $obj->id]['type'] = (int) $obj->type;
5370 $this->cache_types_paiements[(int) $obj->id]['entity'] = (int) $obj->entity;
5371 $this->cache_types_paiements[(int) $obj->id]['active'] = (int) $obj->active;
5372 $i++;
5373 }
5374
5375 $parameters = array('dictionary' => 'paymenttype');
5376 $reshook = $hookmanager->executeHooks('loadDictionaryCache', $parameters, $this); // Note that $action and $object may have been modified by hook
5377 if (empty($reshook)) {
5378 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
5379 $this->cache_types_paiements = array_merge($this->cache_types_paiements, $hookmanager->resArray);
5380 }
5381 } else {
5382 $this->cache_types_paiements = $hookmanager->resArray;
5383 }
5384
5385 $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
5386
5387 return $num;
5388 } else {
5389 dol_print_error($this->db);
5390 return -1;
5391 }
5392 }
5393
5394
5395 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5396
5415 public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1, $noprint = 0)
5416 {
5417 // phpcs:enable
5418 $out = $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, $noinfoadmin, $morecss, $deposit_percent);
5419 if (empty($noprint)) {
5420 print $out;
5421 } else {
5422 return $out;
5423 }
5424 }
5425
5426
5443 public function getSelectConditionsPaiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
5444 {
5445 global $langs, $user;
5446
5447 $out = '';
5448 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
5449
5451
5452 // Set default value if not already set by caller
5453 if (empty($selected) && strpos($htmlname, 'search_') !== 0 && getDolGlobalInt('MAIN_DEFAULT_PAYMENT_TERM_ID')) {
5454 dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TERM_ID", LOG_NOTICE);
5455 $selected = getDolGlobalInt('MAIN_DEFAULT_PAYMENT_TERM_ID');
5456 }
5457
5458 $out .= '<select id="' . $htmlname . '" class="flat selectpaymentterms' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5459 if ($addempty) {
5460 $out .= '<option value="0">&nbsp;</option>';
5461 }
5462
5463 $selectedDepositPercent = null;
5464
5465 foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
5466 if ($filtertype <= 0 && !empty($arrayconditions['deposit_percent'])) {
5467 continue;
5468 }
5469
5470 if ($selected == $id) {
5471 $selectedDepositPercent = $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'];
5472 $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '" selected>';
5473 } else {
5474 $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '">';
5475 }
5476 $label = $arrayconditions['label'];
5477
5478 if (!empty($arrayconditions['deposit_percent'])) {
5479 $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'], $label);
5480 }
5481
5482 $out .= $label;
5483 $out .= '</option>';
5484 }
5485 $out .= '</select>';
5486 if ($user->admin && empty($noinfoadmin)) {
5487 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5488 }
5489 $out .= ajax_combobox($htmlname);
5490
5491 if ($deposit_percent >= 0) {
5492 $out .= ' <span id="' . $htmlname . '_deposit_percent_container"' . (empty($selectedDepositPercent) ? ' style="display: none"' : '') . '>';
5493 $out .= $langs->trans('DepositPercent') . ' : ';
5494 $out .= '<input id="' . $htmlname . '_deposit_percent" name="' . $htmlname . '_deposit_percent" class="maxwidth50" value="' . $deposit_percent . '" />';
5495 $out .= '</span>';
5496 $out .= '
5497 <script nonce="' . getNonce() . '">
5498 $(document).ready(function () {
5499 $("#' . $htmlname . '").change(function () {
5500 let $selected = $(this).find("option:selected");
5501 let depositPercent = $selected.attr("data-deposit_percent");
5502
5503 if (depositPercent.length > 0) {
5504 $("#' . $htmlname . '_deposit_percent_container").show().find("#' . $htmlname . '_deposit_percent").val(depositPercent);
5505 } else {
5506 $("#' . $htmlname . '_deposit_percent_container").hide();
5507 }
5508
5509 return true;
5510 });
5511 });
5512 </script>';
5513 }
5514
5515 return $out;
5516 }
5517
5518
5527 public function getSelectRuleForLinesDates($selected = '', $htmlname = 'rule_for_lines_dates', $addempty = 0)
5528 {
5529 global $langs;
5530
5531 $out = '';
5532
5534
5535 $out .= '<select id="' . $htmlname . '" class="flat selectbillingterm" name="' . $htmlname . '">';
5536 if ($addempty) {
5537 $out .= '<option value="-1">&nbsp;</option>';
5538 }
5539
5540
5541 foreach ($this->cache_rule_for_lines_dates as $rule_for_lines_dates_key => $rule_for_lines_dates_name) {
5542 if ($selected == $rule_for_lines_dates_key) {
5543 $out .= '<option value="' . $rule_for_lines_dates_key . '" selected>';
5544 } else {
5545 $out .= '<option value="' . $rule_for_lines_dates_key . '">';
5546 }
5547
5548 $out .= $langs->trans($rule_for_lines_dates_name);
5549 $out .= '</option>';
5550 }
5551 $out .= '</select>';
5552
5553 $out .= ajax_combobox($htmlname);
5554
5555 return $out;
5556 }
5557
5558
5559 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5560
5577 public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
5578 {
5579 // phpcs:enable
5580 global $langs, $user;
5581
5582 $out = '';
5583
5584 dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $filtertype . ", " . $format, LOG_DEBUG);
5585
5586 $filterarray = array();
5587 if ($filtertype == 'CRDT') {
5588 $filterarray = array(0, 2, 3);
5589 } elseif ($filtertype == 'DBIT') {
5590 $filterarray = array(1, 2, 3);
5591 } elseif ($filtertype != '' && $filtertype != '-1') {
5592 $filterarray = explode(',', $filtertype);
5593 }
5594
5596
5597 // Set default value if not already set by caller
5598 if (empty($selected) && strpos($htmlname, 'search_') !== 0 && getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID')) {
5599 dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TYPE_ID", LOG_NOTICE);
5600 $selected = getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID');
5601 }
5602
5603 $out .= '<select id="select' . $htmlname . '" class="flat selectpaymenttypes' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5604 if ($empty) {
5605 $out .= '<option value="">&nbsp;</option>';
5606 }
5607 foreach ($this->cache_types_paiements as $id => $arraytypes) {
5608 // If not good status
5609 if ($active >= 0 && $arraytypes['active'] != $active) {
5610 continue;
5611 }
5612
5613 // We skip of the user requested to filter on specific payment methods
5614 if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
5615 continue;
5616 }
5617
5618 // We discard empty lines if showempty is on because an empty line has already been output.
5619 if ($empty && empty($arraytypes['code'])) {
5620 continue;
5621 }
5622
5623 if ($format == 0) {
5624 $out .= '<option value="' . $id . '" data-code="'.$arraytypes['code'].'"';
5625 } elseif ($format == 1) {
5626 $out .= '<option value="' . $arraytypes['code'] . '"';
5627 } elseif ($format == 2) {
5628 $out .= '<option value="' . $arraytypes['code'] . '"';
5629 } elseif ($format == 3) {
5630 $out .= '<option value="' . $id . '"';
5631 }
5632 // Print attribute selected or not
5633 if ($format == 1 || $format == 2) {
5634 if ($selected == $arraytypes['code']) {
5635 $out .= ' selected';
5636 }
5637 } else {
5638 if ($selected == $id) {
5639 $out .= ' selected';
5640 }
5641 }
5642 $out .= '>';
5643 $value = '';
5644 if ($format == 0) {
5645 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5646 } elseif ($format == 1) {
5647 $value = $arraytypes['code'];
5648 } elseif ($format == 2) {
5649 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5650 } elseif ($format == 3) {
5651 $value = $arraytypes['code'];
5652 }
5653 $out .= $value ? $value : '&nbsp;';
5654 $out .= '</option>';
5655 }
5656 $out .= '</select>';
5657 if ($user->admin && !$noadmininfo) {
5658 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5659 }
5660 $out .= ajax_combobox('select' . $htmlname);
5661
5662 if (empty($nooutput)) {
5663 print $out;
5664 } else {
5665 return $out;
5666 }
5667 }
5668
5669
5678 public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
5679 {
5680 global $langs;
5681
5682 $return = '<select class="flat maxwidth100" id="select_' . $htmlname . '" name="' . $htmlname . '">';
5683 $options = array(
5684 'HT' => $langs->trans("HT"),
5685 'TTC' => $langs->trans("TTC")
5686 );
5687 foreach ($options as $id => $value) {
5688 if ($selected == $id) {
5689 $return .= '<option value="' . $id . '" selected>' . $value;
5690 } else {
5691 $return .= '<option value="' . $id . '">' . $value;
5692 }
5693 $return .= '</option>';
5694 }
5695 $return .= '</select>';
5696 if ($addjscombo) {
5697 $return .= ajax_combobox('select_' . $htmlname);
5698 }
5699
5700 return $return;
5701 }
5702
5703 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5704
5711 {
5712 // phpcs:enable
5713 global $langs;
5714
5715 $num = count($this->cache_transport_mode); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_transport_mode
5716 if ($num > 0) {
5717 return $num; // Cache already loaded
5718 }
5719
5720 dol_syslog(__METHOD__, LOG_DEBUG);
5721
5722 $this->cache_transport_mode = array();
5723
5724 $sql = "SELECT rowid, code, label, active";
5725 $sql .= " FROM " . $this->db->prefix() . "c_transport_mode";
5726 $sql .= " WHERE entity IN (" . getEntity('c_transport_mode') . ")";
5727
5728 $resql = $this->db->query($sql);
5729 if ($resql) {
5730 $num = $this->db->num_rows($resql);
5731 $i = 0;
5732 while ($i < $num) {
5733 $obj = $this->db->fetch_object($resql);
5734
5735 // If traduction exist, we use it else we take the default label
5736 $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5737 $this->cache_transport_mode[(int) $obj->rowid]
5738 = array(
5739 'rowid' => (int) $obj->rowid,
5740 'code' => (string) $obj->code,
5741 'label' => (string) $label,
5742 'active' => (int) $obj->active,
5743 );
5744 $i++;
5745 }
5746
5747 $this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
5748
5749 return $num;
5750 } else {
5751 dol_print_error($this->db);
5752 return -1;
5753 }
5754 }
5755
5769 public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
5770 {
5771 global $langs, $user;
5772
5773 dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $format, LOG_DEBUG);
5774
5776
5777 print '<select id="select' . $htmlname . '" class="flat selectmodetransport' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5778 if ($empty) {
5779 print '<option value="">&nbsp;</option>';
5780 }
5781 foreach ($this->cache_transport_mode as $id => $arraytypes) {
5782 // If not good status
5783 if ($active >= 0 && $arraytypes['active'] != $active) {
5784 continue;
5785 }
5786
5787 // We discard empty line if showempty is on because an empty line has already been output.
5788 if ($empty && empty($arraytypes['code'])) {
5789 continue;
5790 }
5791
5792 if ($format == 0) {
5793 print '<option value="' . $id . '"';
5794 } elseif ($format == 1) {
5795 print '<option value="' . $arraytypes['code'] . '"';
5796 } elseif ($format == 2) {
5797 print '<option value="' . $arraytypes['code'] . '"';
5798 } elseif ($format == 3) {
5799 print '<option value="' . $id . '"';
5800 }
5801 // If text is selected, we compare with code, else with id
5802 if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
5803 print ' selected';
5804 } elseif ($selected == $id) {
5805 print ' selected';
5806 }
5807 print '>';
5808 $value = '';
5809 if ($format == 0) {
5810 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5811 } elseif ($format == 1) {
5812 $value = $arraytypes['code'];
5813 } elseif ($format == 2) {
5814 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5815 } elseif ($format == 3) {
5816 $value = $arraytypes['code'];
5817 }
5818 print $value ? $value : '&nbsp;';
5819 print '</option>';
5820 }
5821 print '</select>';
5822
5823 print ajax_combobox("select".$htmlname);
5824
5825 if ($user->admin && !$noadmininfo) {
5826 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5827 }
5828 }
5829
5842 public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
5843 {
5844 global $langs, $user;
5845
5846 $langs->loadLangs(array("admin", "sendings"));
5847
5848 $sql = "SELECT rowid, code, libelle as label";
5849 $sql .= " FROM " . $this->db->prefix() . "c_shipment_mode";
5850 $sql .= " WHERE active > 0";
5851 if ($filtre) {
5852 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
5853 }
5854 $sql .= " ORDER BY libelle ASC";
5855
5856 dol_syslog(get_class($this) . "::selectShippingMode", LOG_DEBUG);
5857
5858 $result = $this->db->query($sql);
5859 if ($result) {
5860 $num = $this->db->num_rows($result);
5861 $i = 0;
5862 if ($num) {
5863 print '<select id="select' . $htmlname . '" class="flat selectshippingmethod' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
5864 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
5865 print '<option value="-1">&nbsp;</option>';
5866 }
5867 while ($i < $num) {
5868 $obj = $this->db->fetch_object($result);
5869 if ($selected == $obj->rowid) {
5870 print '<option value="' . $obj->rowid . '" selected>';
5871 } else {
5872 print '<option value="' . $obj->rowid . '">';
5873 }
5874 print ($langs->trans("SendingMethod" . strtoupper($obj->code)) != "SendingMethod" . strtoupper($obj->code)) ? $langs->trans("SendingMethod" . strtoupper($obj->code)) : $obj->label;
5875 print '</option>';
5876 $i++;
5877 }
5878 print "</select>";
5879 if ($user->admin && empty($noinfoadmin)) {
5880 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5881 }
5882
5883 print ajax_combobox('select' . $htmlname);
5884 } else {
5885 print $langs->trans("NoShippingMethodDefined");
5886 }
5887 } else {
5888 dol_print_error($this->db);
5889 }
5890 }
5891
5901 public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
5902 {
5903 global $langs;
5904
5905 $langs->load("sendings");
5906
5907 if ($htmlname != "none") {
5908 print '<form method="POST" action="' . $page . '">';
5909 print '<input type="hidden" name="action" value="setshippingmethod">';
5910 print '<input type="hidden" name="token" value="' . newToken() . '">';
5911 $this->selectShippingMethod($selected, $htmlname, '', $addempty);
5912 print '<input type="submit" class="button valignmiddle" value="' . $langs->trans("Modify") . '">';
5913 print '</form>';
5914 } else {
5915 if ($selected) {
5916 $code = $langs->getLabelFromKey($this->db, $selected, 'c_shipment_mode', 'rowid', 'code');
5917 print $langs->trans("SendingMethod" . strtoupper($code));
5918 } else {
5919 print "&nbsp;";
5920 }
5921 }
5922 }
5923
5932 public function selectSituationInvoices($selected = '', $socid = 0)
5933 {
5934 global $langs;
5935
5936 $langs->load('bills');
5937
5938 $opt = '';
5939
5940 $sql = "SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc";
5941 $sql .= ' FROM ' . $this->db->prefix() . 'facture';
5942 $sql .= ' WHERE entity IN (' . getEntity('invoice') . ')';
5943 $sql .= ' AND situation_counter >= 1';
5944 $sql .= ' AND fk_soc = ' . (int) $socid;
5945 $sql .= ' AND type <> 2';
5946 $sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
5947 $resql = $this->db->query($sql);
5948
5949 $nbSituationInvoiceForThirdparty = 0;
5950
5951 if ($resql && $this->db->num_rows($resql) > 0) {
5952 // Last seen cycle
5953 $ref = 0;
5954 while ($obj = $this->db->fetch_object($resql)) {
5955 //Same cycle ?
5956 if ($obj->situation_cycle_ref != $ref) {
5957 // Just seen this cycle
5958 $ref = $obj->situation_cycle_ref;
5959 //not final ?
5960 if ($obj->situation_final != 1) {
5961 //Not prov?
5962 if (substr($obj->ref, 1, 4) != 'PROV') {
5963 $nbSituationInvoiceForThirdparty++;
5964
5965 if ($selected == $obj->rowid) {
5966 $opt .= '<option value="' . $obj->rowid . '" selected>' . $obj->ref . '</option>';
5967 } else {
5968 $opt .= '<option value="' . $obj->rowid . '">' . $obj->ref . '</option>';
5969 }
5970 }
5971 }
5972 }
5973 }
5974 } else {
5975 dol_syslog("Error sql=" . $sql . ", error=" . $this->error, LOG_ERR);
5976 }
5977
5978 if ($nbSituationInvoiceForThirdparty > 0) {
5979 $opt = '<option class="minwidth100" value="" selected>&nbsp;</option>'.$opt;
5980 } else {
5981 $opt = '<option class="minwidth100" value="-1" selected>'.$langs->trans('NoSituations').'</option>';
5982 }
5983
5984 return $opt;
5985 }
5986
5996 public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
5997 {
5998 global $langs;
5999
6000 $langs->load('products');
6001
6002 $return = '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '">';
6003
6004 $sql = "SELECT rowid, label, code FROM " . $this->db->prefix() . "c_units";
6005 $sql .= ' WHERE active > 0';
6006 if (!empty($unit_type)) {
6007 $sql .= " AND unit_type = '" . $this->db->escape($unit_type) . "'";
6008 }
6009 $sql .= " ORDER BY sortorder";
6010
6011 $resql = $this->db->query($sql);
6012 if ($resql && $this->db->num_rows($resql) > 0) {
6013 if ($showempty) {
6014 $return .= '<option value="-1"></option>';
6015 }
6016
6017 while ($res = $this->db->fetch_object($resql)) {
6018 $unitLabel = $res->label;
6019 if (!empty($langs->tab_translate['unit' . $res->code])) { // check if Translation is available before
6020 $unitLabel = $langs->trans('unit' . $res->code) != $res->label ? $langs->trans('unit' . $res->code) : $res->label;
6021 }
6022
6023 if ($selected == $res->rowid) {
6024 $return .= '<option value="' . $res->rowid . '" selected>' . $unitLabel . '</option>';
6025 } else {
6026 $return .= '<option value="' . $res->rowid . '">' . $unitLabel . '</option>';
6027 }
6028 }
6029 $return .= '</select>';
6030
6031 $return .= ajax_combobox($htmlname);
6032 }
6033 return $return;
6034 }
6035
6036 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6037
6053 public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0, $addentrynone = 0)
6054 {
6055 // phpcs:enable
6056 global $langs;
6057
6058 $out = '';
6059
6060 $langs->loadLangs(array("admin", "banks"));
6061 $num = 0;
6062
6063 $sql = "SELECT rowid, label, bank, clos as status, currency_code";
6064 $sql .= " FROM " . $this->db->prefix() . "bank_account";
6065 $sql .= " WHERE entity IN (" . getEntity('bank_account') . ")";
6066 if ($status != 2) {
6067 $sql .= " AND clos = " . (int) $status;
6068 }
6069 if ($filtre) {
6070 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6071 }
6072 $sql .= " ORDER BY label";
6073
6074 dol_syslog(get_class($this) . "::select_comptes", LOG_DEBUG);
6075 $result = $this->db->query($sql);
6076 if ($result) {
6077 $num = $this->db->num_rows($result);
6078 $i = 0;
6079
6080 $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6081
6082 if ($num == 0) {
6083 if ($status == 0) {
6084 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoActiveBankAccountDefined") . '</span>';
6085 } else {
6086 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoBankAccountDefined") . '</span>';
6087 }
6088 } else {
6089 if (!empty($useempty) && !is_numeric($useempty)) {
6090 $out .= '<option value="-1">'.$langs->trans($useempty).'</option>';
6091 } elseif ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6092 $out .= '<option value="-1">&nbsp;</option>';
6093 }
6094 }
6095
6096 while ($i < $num) {
6097 $obj = $this->db->fetch_object($result);
6098
6099 $labeltoshow = trim($obj->label);
6100 $labeltoshowhtml = trim($obj->label);
6101 if ($showcurrency) {
6102 $labeltoshow .= ' (' . $obj->currency_code . ')';
6103 $labeltoshowhtml .= ' <span class="opacitymedium">(' . $obj->currency_code . ')</span>';
6104 }
6105 if ($status == 2 && $obj->status == 1) {
6106 $labeltoshow .= ' (' . $langs->trans("Closed") . ')';
6107 $labeltoshowhtml .= ' <span class="opacitymedium">(' . $langs->trans("Closed") . ')</span>';
6108 }
6109
6110 if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
6111 $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml).'" selected>';
6112 } else {
6113 $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml).'">';
6114 }
6115 $out .= $labeltoshow;
6116 $out .= '</option>';
6117 $i++;
6118 }
6119
6120 if (!empty($addentrynone)) {
6121 $out .= '<option value="-2"'.($selected == -2 ? ' selected="selected"' : '').' data-html="'.dolPrintHTMLForAttribute('<span class="opacitymedium">'.$langs->trans("None").'</span>').'">'.$langs->trans("None").'</option>';
6122 }
6123
6124 $out .= "</select>";
6125 $out .= ajax_combobox('select' . $htmlname);
6126 } else {
6127 dol_print_error($this->db);
6128 }
6129
6130 // Output or return
6131 if (empty($nooutput)) {
6132 print $out;
6133 } else {
6134 return $out;
6135 }
6136
6137 return $num;
6138 }
6139
6153 public function selectRib($selected = '', $htmlname = 'ribcompanyid', $filtre = '', $useempty = 0, $moreattrib = '', $showibanbic = 0, $morecss = '', $nooutput = 0)
6154 {
6155 // phpcs:enable
6156 global $langs;
6157
6158 $out = '';
6159
6160 $langs->loadLangs(array("admin", "banks"));
6161 $num = 0;
6162
6163 $sql = "SELECT rowid, label, bank, status, iban_prefix, bic, default_rib";
6164 $sql .= " FROM " . $this->db->prefix() . "societe_rib";
6165 $sql .= " WHERE type = 'ban'";
6166 if ($filtre) {
6167 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6168 }
6169 $sql .= " ORDER BY label";
6170 dol_syslog(get_class($this) . "::select_comptes", LOG_DEBUG);
6171 $result = $this->db->query($sql);
6172 if ($result) {
6173 $num = $this->db->num_rows($result);
6174 $i = 0;
6175
6176 $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6177
6178 if ($num == 0) {
6179 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoBankAccountDefined") . '</span>';
6180 } else {
6181 if (!empty($useempty) && !is_numeric($useempty)) {
6182 $out .= '<option value="-1">'.$langs->trans($useempty).'</option>';
6183 } elseif ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6184 $out .= '<option value="-1">&nbsp;</option>';
6185 }
6186 }
6187
6188 while ($i < $num) {
6189 $obj = $this->db->fetch_object($result);
6190 $iban = dolDecrypt($obj->iban_prefix);
6191 if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
6192 $out .= '<option value="' . $obj->rowid . '" data-iban-prefix="' . $iban . ' data-bic="' . $obj->bic . '" selected>';
6193 } else {
6194 $out .= '<option value="' . $obj->rowid . '" data-iban-prefix="' . $iban . ' data-bic="' . $obj->bic . '">';
6195 }
6196 $out .= trim($obj->label);
6197 if ($showibanbic) {
6198 $out .= ' (' . $iban . '/' .$obj->bic. ')' . ($obj->default_rib ? ' ['.$langs->trans("ByDefault").']' : '');
6199 }
6200 $out .= '</option>';
6201 $i++;
6202 }
6203 $out .= "</select>";
6204 $out .= ajax_combobox('select' . $htmlname);
6205 } else {
6206 dol_print_error($this->db);
6207 }
6208
6209 // Output or return
6210 if (empty($nooutput)) {
6211 print $out;
6212 } else {
6213 return $out;
6214 }
6215
6216 return $num;
6217 }
6218
6230 public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
6231 {
6232 global $langs;
6233
6234 $langs->load("admin");
6235 $num = 0;
6236
6237 $sql = "SELECT rowid, name, fk_country, status, entity";
6238 $sql .= " FROM " . $this->db->prefix() . "establishment";
6239 $sql .= " WHERE 1=1";
6240 if ($status != 2) {
6241 $sql .= " AND status = " . (int) $status;
6242 }
6243 if ($filtre) {
6244 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6245 }
6246 $sql .= " ORDER BY name";
6247
6248 dol_syslog(get_class($this) . "::select_establishment", LOG_DEBUG);
6249 $result = $this->db->query($sql);
6250 if ($result) {
6251 $num = $this->db->num_rows($result);
6252 $i = 0;
6253 if ($num) {
6254 print '<select id="select' . $htmlname . '" class="flat selectestablishment" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6255 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6256 print '<option value="-1">&nbsp;</option>';
6257 }
6258
6259 while ($i < $num) {
6260 $obj = $this->db->fetch_object($result);
6261 if ($selected == $obj->rowid) {
6262 print '<option value="' . $obj->rowid . '" selected>';
6263 } else {
6264 print '<option value="' . $obj->rowid . '">';
6265 }
6266 print trim($obj->name);
6267 if ($status == 2 && $obj->status == 1) {
6268 print ' (' . $langs->trans("Closed") . ')';
6269 }
6270 print '</option>';
6271 $i++;
6272 }
6273 print "</select>";
6274 } else {
6275 if ($status == 0) {
6276 print '<span class="opacitymedium">' . $langs->trans("NoActiveEstablishmentDefined") . '</span>';
6277 } else {
6278 print '<span class="opacitymedium">' . $langs->trans("NoEstablishmentFound") . '</span>';
6279 }
6280 }
6281
6282 return $num;
6283 } else {
6284 dol_print_error($this->db);
6285 return -1;
6286 }
6287 }
6288
6298 public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
6299 {
6300 global $langs;
6301 if ($htmlname != "none") {
6302 print '<form method="POST" action="' . $page . '">';
6303 print '<input type="hidden" name="action" value="setbankaccount">';
6304 print '<input type="hidden" name="token" value="' . newToken() . '">';
6305 print img_picto('', 'bank_account', 'class="pictofixedwidth"');
6306 $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
6307 if ($nbaccountfound > 0) {
6308 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6309 }
6310 print '</form>';
6311 } else {
6312 $langs->load('banks');
6313
6314 if ($selected) {
6315 require_once DOL_DOCUMENT_ROOT . '/compta/bank/class/account.class.php';
6316 $bankstatic = new Account($this->db);
6317 $result = $bankstatic->fetch((int) $selected);
6318 if ($result) {
6319 print $bankstatic->getNomUrl(1);
6320 }
6321 } else {
6322 print "&nbsp;";
6323 }
6324 }
6325 }
6326
6338 public function formRib($page, $selected = '', $htmlname = 'ribcompanyid', $filtre = '', $addempty = 0, $showibanbic = 0)
6339 {
6340 global $langs;
6341 if ($htmlname != "none") {
6342 print '<form method="POST" action="' . $page . '">';
6343 print '<input type="hidden" name="action" value="setbankaccountcustomer">';
6344 print '<input type="hidden" name="token" value="' . newToken() . '">';
6345 $nbaccountfound = $this->selectRib($selected, $htmlname, $filtre, $addempty, '', $showibanbic);
6346 if ($nbaccountfound > 0) {
6347 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6348 }
6349 print '</form>';
6350 } else {
6351 $langs->load('banks');
6352
6353 if ($selected) {
6354 require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php';
6355 $bankstatic = new CompanyBankAccount($this->db);
6356 $result = $bankstatic->fetch((int) $selected);
6357 if ($result) {
6358 print $bankstatic->label;
6359 if ($showibanbic) {
6360 print ' (' . $bankstatic->iban . '/' .$bankstatic->bic. ')';
6361 }
6362 }
6363 } else {
6364 print "&nbsp;";
6365 }
6366 }
6367 }
6368
6378 public function selectCategories($categtype, $htmlname, $object = null)
6379 {
6380 global $langs;
6381
6382 $out = '';
6383
6384 $cate_arbo = $this->select_all_categories($categtype, '', '', 64, 0, 3);
6385
6386 $arrayselected = array();
6387 if (GETPOSTISARRAY($htmlname)) {
6388 $arrayselected = GETPOST($htmlname, 'array:int');
6389 } elseif (is_object($object) && $object->id > 0) {
6390 $c = new Categorie($this->db);
6391 $cats = $c->containing($object->id, $categtype);
6392 $arrayselected = array();
6393 foreach ($cats as $cat) {
6394 $arrayselected[] = $cat->id;
6395 }
6396 }
6397
6398 $out .= img_picto('', 'category', 'class="pictofixedwidth"');
6399 $out .= $this->multiselectarray($htmlname, $cate_arbo, $arrayselected, 0, 0, 'minwidth100 widthcentpercentminusxx', 0, 0);
6400
6401 if (!getDolGlobalString('CATEGORY_EDIT_IN_MENU_NOT_IN_POPUP')) {
6402 // Add html code to add the edit button and go back
6403 $jsonclose = 'doJsCodeAfterPopupClose'.dol_sanitizeKeyCode($htmlname).'()';
6404 $urltoopen = '/categories/categorie_list.php?type='.urlencode($categtype).'&nosearch=1';
6405
6406 $s = dolButtonToOpenUrlInDialogPopup($htmlname, $langs->transnoentitiesnoconv("Categories"), img_picto('', 'add', 'class="editfielda"'), $urltoopen, '', '', '', $jsonclose);
6407 $out .= $s;
6408 // Add js code to add the edit button and go back
6409 $out .= '<!-- Add js code to open the popup for category/edit/add -->'."\n";
6410 $out .= '<script>function doJsCodeAfterPopupClose'.dol_sanitizeKeyCode($htmlname).'() {
6411 console.log("doJsCodeAfterPopupClose'.dol_sanitizeKeyCode($htmlname).' has been called, we refresh the combo content + refresh select2...");
6412
6413 // Call an ajax to reload values and update the select
6414
6415 $.ajax({
6416 url: \''.DOL_URL_ROOT.'/core/ajax/fetchCategories.php\',
6417 data: {
6418 action: \'getCategories\',
6419 type: \''.dol_escape_htmltag($categtype).'\'
6420 },
6421 type: \'GET\',
6422 dataType: \'json\',
6423 success: function (data) {
6424 var $select = $(\'#'.dol_sanitizeKeyCode($htmlname).'\');
6425 var selectedValues = $select.val(); // This is an array of selected values
6426 console.log(selectedValues);
6427 $select.empty();
6428 $.each(data, function (index, item) {
6429 $select.append(\'<option value="\' + item.id + \'" data-html="\' + item.htmlforattribute + \'">\' + item.htmlforoption + \'</option>\');
6430 });
6431 $select.val(selectedValues);
6432 },
6433 error: function (xhr, status, error) {
6434 console.log("Error when loading ajax page : " + error);
6435 }
6436 });
6437
6438 // Refresh select2 to take account of new values (enough for small change)
6439 $("#'.dol_sanitizeKeyCode($htmlname).'").trigger("change");
6440
6441 // Alternative if change in select is complex
6442 /*
6443 $("#'.dol_sanitizeKeyCode($htmlname).'").select2("destroy");
6444 $("#'.dol_sanitizeKeyCode($htmlname).'").select2();
6445 */
6446 }</script>';
6447 }
6448
6449 return $out;
6450 }
6451
6452
6453 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6473 public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $fromid = 0, $outputmode = 0, $include = 0, $morecss = '', $useempty = 1)
6474 {
6475 // phpcs:enable
6476 global $langs;
6477
6478 include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
6479
6480 $cat = new Categorie($this->db);
6481
6482 if (is_numeric($type)) {
6483 $type = array_search($type, $cat->MAP_ID); // For backward compatibility
6484 }
6485
6486 $cate_arbo = $cat->get_full_arbo($type, $fromid, $include);
6487
6488 $outarray = array();
6489 $outarrayrichhtml = array();
6490
6491
6492 $output = '<select class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
6493 $num = 0;
6494 if (is_array($cate_arbo)) {
6495 $num = count($cate_arbo);
6496
6497 if (!$num) {
6498 $langs->load("categories");
6499 $output .= '<option value="-1" disabled>' . $langs->trans("NoCategoriesDefined") . '</option>';
6500 } else {
6501 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6502 $output .= '<option value="-1">&nbsp;</option>';
6503 }
6504 foreach ($cate_arbo as $key => $value) {
6505 if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
6506 $add = 'selected ';
6507 } else {
6508 $add = '';
6509 }
6510
6511 $labeltoshow = img_picto('', 'category', 'class="pictofixedwidth"'.(empty($cate_arbo[$key]['color']) ? '' : ' style="color: #' . $cate_arbo[$key]['color'] . '"'));
6512 $labeltoshow .= dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle');
6513
6514 $outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
6515
6516 $outarrayrichhtml[$cate_arbo[$key]['id']] = $labeltoshow;
6517
6518 $output .= '<option ' . $add . 'value="' . $cate_arbo[$key]['id'] . '"';
6519 $output .= ' data-html="' . dol_escape_htmltag($labeltoshow) . '"';
6520 $output .= '>';
6521 // The visible (truncated) label is rendered via data-html in
6522 // templateResult of the select2 combobox; the bare option text
6523 // must keep the full label so that the select2 search matcher
6524 // (ajax_combobox in core/lib/ajax.lib.php) can find a hit on
6525 // characters that lie outside the truncated portion.
6526 $output .= dol_escape_htmltag($cate_arbo[$key]['fulllabel']);
6527 $output .= '</option>';
6528
6529 $cate_arbo[$key]['data-html'] = $labeltoshow;
6530 }
6531 }
6532 }
6533 $output .= '</select>';
6534 $output .= "\n";
6535
6536 $this->num = $num;
6537
6538 if ($outputmode == 2) {
6539 // TODO: handle error when $cate_arbo is not an array
6540 return $cate_arbo;
6541 } elseif ($outputmode == 1) {
6542 return $outarray;
6543 } elseif ($outputmode == 3) {
6544 return $outarrayrichhtml;
6545 }
6546 return $output;
6547 }
6548
6557 public function getHelpBlock($content, $icon = 'fa-question-circle')
6558 {
6559 global $langs;
6560
6561 // Sanitize content (assuming it might contain HTML, but escaping text nodes if needed)
6562 // We trust the caller to pass safe HTML or translated strings.
6563
6564 $html = '<details class="dolibarr-help-block" style="margin-top:8px;">';
6565 $html .= '<summary style="cursor:pointer; color:#0056b3; font-weight:normal; list-style:none; font-size:0.9em; display:flex; align-items:center;">';
6566 $html .= '<span class="fa ' . $icon . '" style="margin-right:6px;"></span>';
6567 $html .= $langs->trans("Help"); // Standardized title
6568 $html .= '</summary>';
6569 $html .= '<div style="margin-top:6px; padding:10px; background:#f8f9fa; border:1px solid #dee2e6; border-radius:4px; font-size:0.9em; color:#555; line-height:1.5;">';
6570 $html .= $content;
6571 $html .= '</div>';
6572 $html .= '</details>';
6573
6574 return $html;
6575 }
6576
6577 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6578
6597 public function form_confirm($page, $title, $question, $action, $formquestion = array(), $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
6598 {
6599 // phpcs:enable
6600 dol_syslog(__METHOD__ . ': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
6601 print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
6602 }
6603
6633 public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 600, $disableformtag = 0, $labelbuttonyes = 'Yes', $labelbuttonno = 'No', $helpContent = '')
6634 {
6635 global $langs, $conf;
6636
6637 $more = '';
6638 $formconfirm = '<!-- formconfirm - before call, page=' . dol_escape_htmltag($page) . ' -->';
6639
6640 $inputok = array();
6641 $inputko = array();
6642
6643 // Clean parameters
6644 $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
6645 if ($conf->browser->layout == 'phone') {
6646 $width = '95%';
6647 }
6648
6649 // Set height automatically if not defined
6650 if (empty($height)) {
6651 $height = 185;
6652 if (is_array($formquestion)) {
6653 $height += (count($formquestion) * 40);
6654 }
6655 if ($question) {
6656 $height += dol_nboflines_bis($question, 80) * 40;
6657 }
6658 }
6659
6660 if (is_array($formquestion) && !empty($formquestion)) {
6661 // First add hidden fields and value
6662 foreach ($formquestion as $key => $input) {
6663 if (is_array($input) && !empty($input)) {
6664 if ($input['type'] == 'hidden') {
6665 $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
6666 $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
6667
6668 $more .= '<input type="hidden" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '" value="' . dol_escape_htmltag(isset($input['value']) ? $input['value'] : '') . '" class="' . $morecss . '"' . $moreattr . '>' . "\n"; // 'value' non fourni par tous les appelants
6669 }
6670 }
6671 }
6672
6673 // Now add questions
6674 $moreonecolumn = '';
6675 $more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">' . "\n";
6676 foreach ($formquestion as $key => $input) {
6677 if (is_array($input) && !empty($input)) {
6678 $size = (!empty($input['size']) ? ' size="' . $input['size'] . '"' : ''); // deprecated. Use morecss instead.
6679 $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
6680 $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
6681
6682 if ($input['type'] == 'text' || $input['type'] == 'input') { // traditional input
6683 $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 . ' spellcheck="false" /></div></div>' . "\n";
6684 } elseif ($input['type'] == 'password') {
6685 $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";
6686 } elseif ($input['type'] == 'textarea') {
6687 $moreonecolumn .= '<div class="margintoponly">';
6688 $moreonecolumn .= ($input['label'] ?? '') . '<br>';
6689 $moreonecolumn .= '<textarea name="' . dol_escape_htmltag($input['name']) . '" id="' . dol_escape_htmltag($input['name']) . '" class="' . $morecss . '"' . $moreattr . '>';
6690 $moreonecolumn .= $input['value'] ?? ''; // 'value' is optional (blank textarea by default), like for the 'text' and 'password' types above
6691 $moreonecolumn .= '</textarea>';
6692 $moreonecolumn .= '</div>';
6693 } elseif (in_array($input['type'], ['select', 'multiselect'])) {
6694 if (empty($morecss)) {
6695 $morecss = 'minwidth100';
6696 }
6697
6698 $show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
6699 $key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
6700 $value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
6701 $translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
6702 $maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
6703 $disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
6704 $sort = isset($input['select_sort']) ? $input['select_sort'] : '';
6705
6706 $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">';
6707 if (!empty($input['label'])) {
6708 $more .= $input['label'] . '</div><div class="tagtd left">';
6709 }
6710 if ($input['type'] == 'select') {
6711 $more .= $this->selectarray($input['name'], $input['values'], isset($input['default']) ? $input['default'] : '-1', $show_empty, $key_in_label, $value_as_key, $moreattr, $translate, $maxlen, $disabled, $sort, $morecss);
6712 } else {
6713 $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);
6714 }
6715 $more .= '</div></div>' . "\n";
6716 } elseif ($input['type'] == 'checkbox') {
6717 $more .= '<div class="tagtr">';
6718 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '"><label for="' . dol_escape_htmltag($input['name']) . '">' . $input['label'] . '</label></div><div class="tagtd">';
6719 $more .= '<input type="checkbox" class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '"' . $moreattr;
6720 if (isset($input['value']) && !is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0' && $input['value'] != '') {
6721 $more .= ' checked';
6722 }
6723 if (isset($input['value']) && is_bool($input['value']) && $input['value']) {
6724 $more .= ' checked';
6725 }
6726 if (isset($input['disabled'])) {
6727 $more .= ' disabled';
6728 }
6729 $more .= ' /></div>';
6730 $more .= '</div>' . "\n";
6731 } elseif ($input['type'] == 'radio') {
6732 $i = 0;
6733 foreach ($input['values'] as $selkey => $selval) {
6734 $more .= '<div class="tagtr">';
6735 if (isset($input['label'])) {
6736 if ($i == 0) {
6737 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? ' tdtop' : (' tdtop ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
6738 } else {
6739 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' "' . $input['tdclass'])) . '">&nbsp;</div>';
6740 }
6741 }
6742 $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;
6743 if (!empty($input['disabled'])) {
6744 $more .= ' disabled';
6745 }
6746 if (isset($input['default']) && $input['default'] === $selkey) {
6747 $more .= ' checked="checked"';
6748 }
6749 $more .= ' /> ';
6750 $more .= '<label for="' . dol_escape_htmltag($input['name'] . $selkey) . '" class="valignmiddle">' . $selval . '</label>';
6751 $more .= '</div></div>' . "\n";
6752 $i++;
6753 }
6754 } elseif ($input['type'] == 'date' || $input['type'] == 'datetime') {
6755 $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
6756 $more .= '<div class="tagtd">';
6757 $addnowlink = (empty($input['datenow']) ? 0 : 1);
6758 $h = $m = 0;
6759 if ($input['type'] == 'datetime') {
6760 $h = isset($input['hours']) ? $input['hours'] : 1;
6761 $m = isset($input['minutes']) ? $input['minutes'] : 1;
6762 }
6763 $more .= $this->selectDate(isset($input['value']) ? $input['value'] : -1, $input['name'], $h, $m, 0, '', 1, $addnowlink);
6764 $more .= '</div></div>'."\n";
6765 $formquestion[] = array('name' => $input['name'].'day');
6766 $formquestion[] = array('name' => $input['name'].'month');
6767 $formquestion[] = array('name' => $input['name'].'year');
6768 $formquestion[] = array('name' => $input['name'].'hour');
6769 $formquestion[] = array('name' => $input['name'].'min');
6770 } elseif ($input['type'] == 'other') { // can be 1 column or 2 depending if label is set or not
6771 $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
6772 if (!empty($input['label'])) {
6773 $more .= $input['label'] . '</div><div class="tagtd">';
6774 }
6775 if (!empty($input['value'])) {
6776 $more .= $input['value'];
6777 }
6778 $more .= '</div></div>' . "\n";
6779 } elseif ($input['type'] == 'onecolumn') {
6780 $moreonecolumn .= '<div class="margintoponly">';
6781 $moreonecolumn .= $input['value'] ?? '';
6782 $moreonecolumn .= '</div>' . "\n";
6783 } elseif ($input['type'] == 'hidden') {
6784 // Do nothing more, already added by a previous loop
6785 } elseif ($input['type'] == 'separator') {
6786 $more .= '<br>';
6787 } else {
6788 $more .= 'Error type ' . $input['type'] . ' for the confirm box is not a supported type';
6789 }
6790 }
6791 }
6792 $more .= '</div>' . "\n";
6793 $more .= $moreonecolumn;
6794 }
6795
6796 // JQUERY method dialog is broken with smartphone, we use standard HTML.
6797 // 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
6798 // See page product/card.php for example
6799 if (!empty($conf->dol_use_jmobile)) {
6800 $useajax = 0;
6801 }
6802 if (empty($conf->use_javascript_ajax)) {
6803 $useajax = 0;
6804 }
6805
6806 if ($useajax) {
6807 $autoOpen = true;
6808 $dialogconfirm = 'dialog-confirm';
6809 $button = '';
6810 if (!is_numeric($useajax)) {
6811 $button = $useajax;
6812 $useajax = 1;
6813 $autoOpen = false;
6814 $dialogconfirm .= '-' . $button;
6815 }
6816 $pageyes = $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=yes';
6817 $pageno = ($useajax == 2 ? $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=no' : '');
6818
6819 // Add input fields into list of fields to read during submit (inputok and inputko)
6820 if (is_array($formquestion)) {
6821 foreach ($formquestion as $key => $input) {
6822 //print "xx ".$key." rr ".is_array($input)."<br>\n";
6823 // Add name of fields to propagate with the GET when submitting the form with button OK.
6824 if (is_array($input) && isset($input['name'])) {
6825 if (strpos($input['name'], ',') > 0) {
6826 $inputok = array_merge($inputok, explode(',', $input['name']));
6827 } else {
6828 array_push($inputok, $input['name']);
6829 }
6830 }
6831 // Add name of fields to propagate with the GET when submitting the form with button KO.
6832 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
6833 if (is_array($input) && isset($input['inputko']) && $input['inputko'] == 1 && isset($input['name'])) {
6834 array_push($inputko, $input['name']);
6835 }
6836 }
6837 }
6838
6839 // Show JQuery confirm box.
6840 // Add 'flex-direction: column' and 'justify-content: space-between' to push content to top and buttons to bottom
6841 $formconfirm .= '<div id="' . $dialogconfirm . '" title="' . dol_escape_htmltag($title) . '" style="display: none;">';
6842 $formconfirm .= '<div style="display: flex; flex-direction: column; height: 100%;">';
6843 if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
6844 $formconfirm .= '<div class="confirmtext">' . $formquestion['text'] . '</div>' . "\n";
6845 }
6846 if (!empty($more)) {
6847 $formconfirm .= '<div class="confirmquestions">' . $more . '</div>' . "\n";
6848 }
6849 // NEW: Add help block if content provided
6850 if (!empty($helpContent)) {
6851 $formconfirm .= '<div style="text-align:left; margin-top:12px; padding-top:8px; border-top:1px solid #eee; clear:both;">';
6852 $formconfirm .= $this->getHelpBlock($helpContent);
6853 $formconfirm .= '</div>';
6854 }
6855 if (!empty($question)) {
6856 $formconfirm .= '<div class="confirmmessage" style="padding-top: 15px;">';
6857 $formconfirm .= img_help(0, '') . ' ' . $question;
6858 $formconfirm .= '</div>';
6859 }
6860 $formconfirm .= '</div>';
6861 $formconfirm .= '</div>' . "\n";
6862
6863 $formconfirm .= "\n<!-- begin code of popup for formconfirm page=" . $page . " -->\n";
6864 $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
6865 $formconfirm .= "/* Code for the jQuery('#dialogforpopup').dialog() */\n";
6866 $formconfirm .= 'jQuery(document).ready(function() {
6867 $(function() {
6868 $( "#' . $dialogconfirm . '" ).dialog({
6869 autoOpen: ' . ($autoOpen ? "true" : "false") . ',';
6870 if ($newselectedchoice == 'no') {
6871 $formconfirm .= '
6872 open: function() {
6873 $(this).parent().find("button.ui-button:eq(2)").focus();
6874 },';
6875 }
6876
6877 $jsforcursor = '';
6878 if ($useajax == 1) {
6879 $jsforcursor = '// The call to urljump can be slow, so we set the wait cursor' . "\n";
6880 $jsforcursor .= 'jQuery("html,body,#id-container").addClass("cursorwait");' . "\n";
6881 }
6882
6883 $postconfirmas = 'GET';
6884 $maxurllengthforget = getDolGlobalInt('MAIN_MAX_URL_LENGTH_FOR_GET', 2000);
6885
6886 $formconfirm .= '
6887 resizable: false,
6888 height: \'' . dol_escape_js($height) . '\',
6889 width: \'' . dol_escape_js($width) . '\',
6890 modal: true,
6891 closeOnEscape: false,
6892 buttons: {
6893 "' . dol_escape_js($langs->transnoentities($labelbuttonyes)) . '": function() {
6894 var options = "token=' . urlencode(newToken()) . '";
6895 var inputok = ' . json_encode($inputok) . '; /* List of fields into form */
6896 var page = \'' . dol_escape_js(!empty($page) ? $page : '') . '\';
6897 var pageyes = \'' . dol_escape_js(!empty($pageyes) ? $pageyes : '') . '\';
6898
6899 if (inputok.length > 0) {
6900 $.each(inputok, function(i, inputname) {
6901 var more = "";
6902 var inputvalue;
6903 if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
6904 inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
6905 } else {
6906 if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
6907 inputvalue = $("#" + inputname + more).val();
6908 }
6909 if (typeof inputvalue == "undefined") { inputvalue=""; }
6910 console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
6911 options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
6912 });
6913 }
6914 var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "&") + options;
6915 if (pageyes.length > 0) {';
6916 if ($postconfirmas == 'GET') {
6917 $formconfirm .= 'dolSubmitConfirmForm(urljump, pageyes, options, ' . $maxurllengthforget . ');';
6918 } else {
6919 $formconfirm .= $jsforcursor;
6920 $formconfirm .= 'var post = $.post(
6921 pageyes,
6922 options,
6923 function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
6924 );';
6925 }
6926 $formconfirm .= '
6927 console.log("after post ok");
6928 }
6929 $(this).dialog("close");
6930 },
6931 "' . dol_escape_js($langs->transnoentities($labelbuttonno)) . '": function() {
6932 var options = "token=' . urlencode(newToken()) . '";
6933 var inputko = ' . json_encode($inputko) . '; /* List of fields into form */
6934 var page = "' . dol_escape_js(!empty($page) ? $page : '') . '";
6935 var pageno="' . dol_escape_js(!empty($pageno) ? $pageno : '') . '";
6936 if (inputko.length > 0) {
6937 $.each(inputko, function(i, inputname) {
6938 var more = "";
6939 if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
6940 var inputvalue = $("#" + inputname + more).val();
6941 if (typeof inputvalue == "undefined") { inputvalue=""; }
6942 options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
6943 });
6944 }
6945 var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "&") + options;
6946 //alert(urljump);
6947 if (pageno.length > 0) {';
6948 if ($postconfirmas == 'GET') {
6949 $formconfirm .= 'dolSubmitConfirmForm(urljump, pageno, options, ' . $maxurllengthforget . ');';
6950 } else {
6951 $formconfirm .= $jsforcursor;
6952 $formconfirm .= 'var post = $.post(
6953 pageno,
6954 options,
6955 function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
6956 );';
6957 }
6958 $formconfirm .= '
6959 console.log("after post ko");
6960 }
6961 $(this).dialog("close");
6962 }
6963 }
6964 }
6965 );
6966
6967 var button = "' . $button . '";
6968 if (button.length > 0) {
6969 $( "#" + button ).click(function() {
6970 $("#' . $dialogconfirm . '").dialog("open");
6971 });
6972 }
6973 });
6974 });
6975 </script>';
6976 $formconfirm .= "<!-- end ajax formconfirm -->\n";
6977 } else {
6978 $formconfirm .= "\n<!-- begin formconfirm page=" . dol_escape_htmltag($page) . " -->\n";
6979
6980 if (empty($disableformtag)) {
6981 $formconfirm .= '<form method="POST" action="' . $page . '" class="notoptoleftnoright">' . "\n";
6982 }
6983
6984 $formconfirm .= '<input type="hidden" name="action" value="' . $action . '">' . "\n";
6985 $formconfirm .= '<input type="hidden" name="token" value="' . newToken() . '">' . "\n";
6986
6987 $formconfirm .= '<div class="valid">' . "\n";
6988
6989 // Line title
6990 $formconfirm .= '<div class="validtitre">';
6991 $formconfirm .= img_picto('', 'pictoconfirm') . ' ' . $title;
6992 $formconfirm .= '</div>' . "\n";
6993
6994 // Line text
6995 if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
6996 $formconfirm .= '<div class="valid">' . $formquestion['text'] . '</div>' . "\n";
6997 }
6998
6999 // Line form fields
7000 if ($more) {
7001 $formconfirm .= '<div>' . "\n";
7002 $formconfirm .= $more;
7003 $formconfirm .= '</div>' . "\n";
7004 }
7005
7006 // NEW: Help block row (between form fields and question)
7007 if (!empty($helpContent)) {
7008 $formconfirm .= '<div style="padding-top:8px; border-top:1px solid #888;">';
7009 $formconfirm .= $this->getHelpBlock($helpContent);
7010 $formconfirm .= '</div>' . "\n";
7011 }
7012
7013 // Let's add a row that acts as a spacer.
7014 $formconfirm .= '<div style="padding-top: 20px;"></div>' . "\n";
7015
7016 // Question row
7017 $formconfirm .= '<div class="inline-block">' . $question . '</div>';
7018
7019 $formconfirm .= '<div class="inline-block">';
7020 $formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly', $labelbuttonyes, $labelbuttonno);
7021 $formconfirm .= '<input class="button valignmiddle confirmvalidatebutton small" type="submit" value="' . $langs->trans("Validate") . '">';
7022 $formconfirm .= '</div>';
7023
7024 $formconfirm .= '</div>';
7025
7026 if (empty($disableformtag)) {
7027 $formconfirm .= "</form>\n";
7028 }
7029 $formconfirm .= '<br>';
7030
7031 if (!empty($conf->use_javascript_ajax)) {
7032 $formconfirm .= '<!-- code to disable button to avoid double clic -->';
7033 $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
7034 $formconfirm .= '
7035 $(document).ready(function () {
7036 $(".confirmvalidatebutton").on("click", function() {
7037 console.log("We click on button confirmvalidatebutton");
7038 $(this).attr("disabled", "disabled");
7039 setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
7040 //console.log($(this).closest("form"));
7041 $(this).closest("form").submit();
7042 });
7043 });
7044 ';
7045 $formconfirm .= '</script>' . "\n";
7046 }
7047
7048 $formconfirm .= "<!-- end formconfirm -->\n";
7049 }
7050
7051 return $formconfirm;
7052 }
7053
7054 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7055
7072 public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0, $textifnoproject = '', $morecss = '', $option = '')
7073 {
7074 // phpcs:enable
7075 global $langs;
7076
7077 require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
7078 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
7079
7080 $out = '';
7081
7082 $formproject = new FormProjets($this->db);
7083
7084 $langs->load("project");
7085 if ($htmlname != "none") {
7086 $out .= '<form method="post" action="' . $page . '">';
7087 $out .= '<input type="hidden" name="action" value="classin">';
7088 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7089 $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1, 0, $morecss);
7090 $out .= '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7091 $out .= '</form>';
7092 } else {
7093 $out .= '<span class="project_head_block">';
7094 if ($selected instanceof Project) {
7095 $out .= $selected->getNomUrl(0, $option, 1);
7096 } elseif (is_numeric($selected)) {
7097 $projet = new Project($this->db);
7098 $projet->fetch((int) $selected);
7099 $out .= $projet->getNomUrl(0, $option, 1);
7100 } else {
7101 $out .= '<span class="opacitymedium">' . $textifnoproject . '</span>';
7102 }
7103 $out .= '</span>';
7104 }
7105
7106 if (empty($nooutput)) {
7107 print $out;
7108 return '';
7109 }
7110 return $out;
7111 }
7112
7113 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7114
7130 public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0, $type = '', $filtertype = -1, $deposit_percent = -1, $nooutput = 0)
7131 {
7132 // phpcs:enable
7133 global $langs;
7134
7135 $selected = (int) $selected;
7136
7137 $out = '';
7138
7139 if ($htmlname != "none") {
7140 $out .= '<form method="POST" action="' . $page . '">';
7141 $out .= '<input type="hidden" name="action" value="setconditions">';
7142 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7143 if ($type) {
7144 $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7145 }
7146 $out .= $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, 0, '', $deposit_percent);
7147 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7148 $out .= '</form>';
7149 } else {
7150 if ($selected) {
7151 $this->load_cache_conditions_paiements();
7152
7153 if (isset($this->cache_conditions_paiements[$selected])) {
7154 $label = $this->cache_conditions_paiements[$selected]['label'];
7155
7156 if (!empty($this->cache_conditions_paiements[$selected]['deposit_percent'])) {
7157 $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $this->cache_conditions_paiements[$selected]['deposit_percent'], $label);
7158 }
7159
7160 $out .= $label;
7161 } else {
7162 $langs->load('errors');
7163 $out .= $langs->trans('ErrorNotInDictionaryPaymentConditions');
7164 }
7165 } else {
7166 $out .= '&nbsp;';
7167 }
7168 }
7169
7170 if (empty($nooutput)) {
7171 print $out;
7172 return '';
7173 }
7174 return $out;
7175 }
7176
7177 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7178
7189 public function form_rule_for_lines_dates($page, $selected = '', $htmlname = 'rule_for_lines_dates', $addempty = 0, $nooutput = 0): string
7190 {
7191 // phpcs:enable
7192 global $langs;
7193
7194 $out = '';
7195
7196 if ($htmlname != 'none') {
7197 $out .= '<form method="POST" action="' . $page . '">';
7198 $out .= '<input type="hidden" name="action" value="setruleforlinesdates">';
7199 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7200 $out .= $this->getSelectRuleForLinesDates($selected, $htmlname, $addempty);
7201 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7202 $out .= '</form>';
7203 } else {
7204 if (isset($selected)) {
7205 $this->load_cache_rule_for_lines_dates();
7206 if (isset($this->cache_rule_for_lines_dates[$selected])) {
7207 $label = $this->cache_rule_for_lines_dates[$selected];
7208 $out .= $langs->trans($label);
7209 }
7210 } else {
7211 $out .= '&nbsp;';
7212 }
7213 }
7214
7215 if (empty($nooutput)) {
7216 print $out;
7217 return '';
7218 }
7219
7220 return $out;
7221 }
7222
7223 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7224
7234 public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
7235 {
7236 dol_syslog(__METHOD__, LOG_DEBUG);
7237 // phpcs:enable
7238 global $langs;
7239 if ($htmlname != "none") {
7240 print '<form method="post" action="' . $page . '">';
7241 print '<input type="hidden" name="action" value="setavailability">';
7242 print '<input type="hidden" name="token" value="' . newToken() . '">';
7243 print $this->selectAvailabilityDelay($selected, $htmlname, '', $addempty, '', 1);
7244 print '<input type="submit" name="modify" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7245 print '<input type="submit" name="cancel" class="button smallpaddingimp" value="' . $langs->trans("Cancel") . '">';
7246 print '</form>';
7247 } else {
7248 if ($selected) {
7249 $this->load_cache_availability();
7250 // @phan-suppress-next-line PhanTypeMismatchProperty
7251 if (isset($this->cache_availability[$selected])) {
7252 print $this->cache_availability[$selected]['label'];
7253 } else {
7254 print "&nbsp;";
7255 }
7256 } else {
7257 print "&nbsp;";
7258 }
7259 }
7260 }
7261
7273 public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0, $morecss = '')
7274 {
7275 global $langs;
7276 if ($htmlname != "none") {
7277 print '<form method="post" action="' . $page . '">';
7278 print '<input type="hidden" name="action" value="setdemandreason">';
7279 print '<input type="hidden" name="token" value="' . newToken() . '">';
7280 $this->selectInputReason($selected, $htmlname, '-1', $addempty, $morecss);
7281 print '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7282 print '</form>';
7283 } else {
7284 if ($selected) {
7285 $this->loadCacheInputReason();
7286 foreach ($this->cache_demand_reason as $key => $val) {
7287 if ($val['id'] == $selected) {
7288 print $val['label'];
7289 break;
7290 }
7291 }
7292 } else {
7293 print "&nbsp;";
7294 }
7295 }
7296 }
7297
7298 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7299
7313 public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0, $type = '')
7314 {
7315 // phpcs:enable
7316 global $langs;
7317
7318 $ret = '';
7319
7320 if ($htmlname != "none") {
7321 $ret .= '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
7322 $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
7323 $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
7324 if ($type) {
7325 $ret .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7326 }
7327 $ret .= '<table class="nobordernopadding">';
7328 $ret .= '<tr><td>';
7329 $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form' . $htmlname, 1, 0);
7330 $ret .= '</td>';
7331 $ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
7332 $ret .= '</tr></table></form>';
7333 } else {
7334 if ($displayhour) {
7335 $ret .= dol_print_date($selected, 'dayhour');
7336 } else {
7337 $ret .= dol_print_date($selected, 'day');
7338 }
7339 }
7340
7341 if (empty($nooutput)) {
7342 print $ret;
7343 }
7344 return $ret;
7345 }
7346
7347
7348 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7349
7360 public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = array(), $include = array())
7361 {
7362 // phpcs:enable
7363 global $langs;
7364
7365 if ($htmlname != "none") {
7366 print '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
7367 print '<input type="hidden" name="action" value="set' . $htmlname . '">';
7368 print '<input type="hidden" name="token" value="' . newToken() . '">';
7369 print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
7370 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7371 print '</form>';
7372 } else {
7373 if ($selected) {
7374 require_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
7375 $theuser = new User($this->db);
7376 $theuser->fetch((int) $selected);
7377 print $theuser->getNomUrl(1);
7378 } else {
7379 print "&nbsp;";
7380 }
7381 }
7382 }
7383
7384
7385 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7386
7400 public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0, $type = '', $nooutput = 0)
7401 {
7402 // phpcs:enable
7403 global $langs;
7404
7405 $out = '';
7406 if ($htmlname != "none") {
7407 $out .= '<form method="POST" action="' . $page . '">';
7408 $out .= '<input type="hidden" name="action" value="setmode">';
7409 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7410 if ($type) {
7411 $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7412 }
7413 $out .= $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active, '', 1);
7414 $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7415 $out .= '</form>';
7416 } else {
7417 if ((int) $selected) {
7418 $this->load_cache_types_paiements();
7419 $out .= $this->cache_types_paiements[(int) $selected]['label'] ?? '&nbsp;';
7420 } else {
7421 $out .= "&nbsp;";
7422 }
7423 }
7424
7425 if ($nooutput) {
7426 return $out;
7427 } else {
7428 print $out;
7429 }
7430 return '';
7431 }
7432
7443 public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
7444 {
7445 global $langs;
7446 if ($htmlname != "none") {
7447 print '<form method="POST" action="' . $page . '">';
7448 print '<input type="hidden" name="action" value="settransportmode">';
7449 print '<input type="hidden" name="token" value="' . newToken() . '">';
7450 $this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
7451 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7452 print '</form>';
7453 } else {
7454 if ($selected) {
7455 $this->load_cache_transport_mode();
7456 print $this->cache_transport_mode[$selected]['label'];
7457 } else {
7458 print "&nbsp;";
7459 }
7460 }
7461 }
7462
7463 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7464
7473 public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
7474 {
7475 // phpcs:enable
7476 global $langs;
7477 if ($htmlname != "none") {
7478 print '<form method="POST" action="' . $page . '">';
7479 print '<input type="hidden" name="action" value="setmulticurrencycode">';
7480 print '<input type="hidden" name="token" value="' . newToken() . '">';
7481 print $this->selectMultiCurrency($selected, $htmlname, 0);
7482 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7483 print '</form>';
7484 } else {
7485 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
7486 print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
7487 }
7488 }
7489
7490 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7491
7502 public function form_multicurrency_rate($page, $rate = 0.0, $htmlname = 'multicurrency_tx', $currency = '', $rate_direct = 0.0)
7503 {
7504 // phpcs:enable
7505 global $langs, $conf;
7506
7507 if ($htmlname != "none") {
7508 print '<form method="POST" action="' . $page . '">';
7509 print '<input type="hidden" name="action" value="setmulticurrencyrate">';
7510 print '<input type="hidden" name="token" value="' . newToken() . '">';
7511 print '<input type="text" class="maxwidth75" name="' . $htmlname . '" value="' . (!empty($rate) ? price(price2num($rate, 'CU')) : 1) . '" spellcheck="false" /> ';
7512 print '<select name="calculation_mode" id="calculation_mode">';
7513 print '<option value="1">Change ' . $langs->trans("PriceUHT") . ' of lines</option>';
7514 print '<option value="2">Change ' . $langs->trans("PriceUHTCurrency") . ' of lines</option>';
7515 print '</select> ';
7516 print ajax_combobox("calculation_mode");
7517 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7518 print '</form>';
7519 } else {
7520 if (!empty($rate)) {
7521 print price($rate, 1, $langs, 0, 0);
7522 if ($currency && $rate != 1) {
7528 if (getDolGlobalString('MULTICURRENCY_USE_RATE_DIRECT')) {
7529 print ' &nbsp; <span class="opacitymedium">(' . price($rate_direct, 1, $langs, 0, 0) . ' ' . $conf->currency . ' = 1 ' . $currency . ')</span>';
7530 } else {
7531 print ' &nbsp; <span class="opacitymedium">(' . price($rate, 1, $langs, 0, 0) . ' ' . $currency . ' = 1 ' . $conf->currency . ')</span>';
7532 }
7533 }
7534 } else {
7535 print 1;
7536 }
7537 }
7538 }
7539
7540 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7541
7559 public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0, $filterabsolutediscount = 0, $filtercreditnote = 0)
7560 {
7561 // phpcs:enable
7562 global $conf, $langs;
7563
7564 if ($htmlname != "none") {
7565 print '<form method="post" action="' . $page . '" class="inline-block">';
7566 print '<input type="hidden" name="action" value="setabsolutediscount">';
7567 print '<input type="hidden" name="token" value="' . newToken() . '">';
7568 print '<div class="inline-block">';
7569 if (!empty($discount_type)) {
7570 if (getDolGlobalString('FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS')) {
7571 if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
7572 $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be subtracted to payments only and not to total of final invoice
7573 } else {
7574 $translationKey = 'HasCreditNoteFromSupplier';
7575 }
7576 } else {
7577 if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
7578 $translationKey = 'HasAbsoluteDiscountFromSupplier';
7579 } else {
7580 $translationKey = 'HasCreditNoteFromSupplier';
7581 }
7582 }
7583 } else {
7584 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) {
7585 if (!$filter || $filter == "fk_facture_source IS NULL") {
7586 $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be subtracted to payments only and not to total of final invoice
7587 } else {
7588 $translationKey = 'CompanyHasCreditNote';
7589 }
7590 } else {
7591 if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
7592 $translationKey = 'CompanyHasAbsoluteDiscount';
7593 } else {
7594 $translationKey = 'CompanyHasCreditNote';
7595 }
7596 }
7597 }
7598 print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
7599 if (empty($hidelist)) {
7600 print ' ';
7601 }
7602 print '</div>';
7603 if (empty($hidelist)) {
7604 print '<div class="inline-block" style="padding-right: 10px">';
7605 $newfilter = 'discount_type = ' . intval($discount_type);
7606 if (!empty($discount_type)) {
7607 $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
7608 } else {
7609 $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
7610 }
7611 if ($filter) {
7612 $sanitizedfilter = $filter; // @phan-suppress-current-line SqlInjection
7613 $newfilter .= ' AND (' . $sanitizedfilter . ')';
7614 }
7615 // output the combo of discounts
7616 $nbqualifiedlines = $this->select_remises((string) $selected, $htmlname, $newfilter, $socid, $maxvalue);
7617 if ($nbqualifiedlines > 0) {
7618 print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="' . dol_escape_htmltag($langs->trans("UseLine")) . '"';
7619 if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
7620 print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
7621 }
7622 if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
7623 print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
7624 }
7625
7626 print '>';
7627 }
7628 print '</div>';
7629 }
7630 if ($more) {
7631 print '<div class="inline-block">';
7632 print $more;
7633 print '</div>';
7634 }
7635 print '</form>';
7636 } else {
7637 if ($selected) {
7638 print $selected;
7639 } else {
7640 print "0";
7641 }
7642 }
7643 }
7644
7645
7646 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7647
7657 public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
7658 {
7659 // phpcs:enable
7660 global $langs;
7661
7662 if ($htmlname != "none") {
7663 print '<form method="post" action="' . $page . '">';
7664 print '<input type="hidden" name="action" value="set_contact">';
7665 print '<input type="hidden" name="token" value="' . newToken() . '">';
7666 print '<table class="nobordernopadding">';
7667 print '<tr><td>';
7668 print $this->selectcontacts($societe->id, $selected, $htmlname);
7669 $num = $this->num;
7670 if ($num == 0) {
7671 $addcontact = (getDolGlobalString('SOCIETE_ADDRESSES_MANAGEMENT') ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
7672 print '<a href="' . DOL_URL_ROOT . '/contact/card.php?socid=' . $societe->id . '&action=create&backtoreferer=1">' . $addcontact . '</a>';
7673 }
7674 print '</td>';
7675 print '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
7676 print '</tr></table></form>';
7677 } else {
7678 if ($selected) {
7679 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
7680 $contact = new Contact($this->db);
7681 $contact->fetch((int) $selected);
7682 print $contact->getFullName($langs);
7683 } else {
7684 print "&nbsp;";
7685 }
7686 }
7687 }
7688
7689 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7690
7707 public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array(), $textifnothirdparty = '')
7708 {
7709 // phpcs:enable
7710 global $langs;
7711
7712 $out = '';
7713 if ($htmlname != "none") {
7714 $limit = getDolGlobalInt('THIRDPARTY_LIMIT_SIZE');
7715
7716 $out .= '<form method="post" action="' . $page . '">';
7717 $out .= '<input type="hidden" name="action" value="set_thirdparty">';
7718 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7719 $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, $limit, 'minwidth100', '', '', 1, array(), false, $excludeids);
7720 $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7721 $out .= '</form>';
7722 } else {
7723 if ($selected) {
7724 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
7725 $soc = new Societe($this->db);
7726 $soc->fetch((int) $selected);
7727 $out .= $soc->getNomUrl(0, '');
7728 } else {
7729 $out .= '<span class="opacitymedium">' . $textifnothirdparty . '</span>';
7730 }
7731 }
7732
7733 if ($nooutput) {
7734 return $out;
7735 } else {
7736 print $out;
7737 }
7738
7739 return '';
7740 }
7741
7742 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7743
7752 public function select_currency($selected = '', $htmlname = 'currency_id')
7753 {
7754 // phpcs:enable
7755 print $this->selectCurrency($selected, $htmlname);
7756 }
7757
7767 public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0, $useempty = '')
7768 {
7769 global $langs, $user;
7770
7771 $langs->loadCacheCurrencies('');
7772
7773 $out = '';
7774
7775 if ($selected == 'euro' || $selected == 'euros') {
7776 $selected = 'EUR'; // Pour compatibilite
7777 }
7778
7779 $out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="' . $htmlname . '" id="' . $htmlname . '">';
7780 if ($useempty) {
7781 $out .= '<option value="-1" selected></option>';
7782 }
7783 foreach ($langs->cache_currencies as $code_iso => $currency) {
7784 $labeltoshow = $currency['label'];
7785 if ($mode == 1) {
7786 $labeltoshow .= ' <span class="opacitymedium">(' . $code_iso . ')</span>';
7787 } elseif ($mode == 2) {
7788 $labeltoshow .= ' <span class="opacitymedium">(' . $code_iso.' - '.$langs->getCurrencySymbol($code_iso) . ')</span>';
7789 } else {
7790 $labeltoshow .= ' <span class="opacitymedium">(' . $langs->getCurrencySymbol($code_iso) . ')</span>';
7791 }
7792
7793 if ($selected && $selected == $code_iso) {
7794 $out .= '<option value="' . $code_iso . '" selected data-html="' . dol_escape_htmltag($labeltoshow) . '">';
7795 } else {
7796 $out .= '<option value="' . $code_iso . '" data-html="' . dol_escape_htmltag($labeltoshow) . '">';
7797 }
7798 $out .= dol_string_nohtmltag($labeltoshow);
7799 $out .= '</option>';
7800 }
7801 $out .= '</select>';
7802 if ($user->admin) {
7803 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
7804 }
7805
7806 // Make select dynamic
7807 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7808 $out .= ajax_combobox($htmlname);
7809
7810 return $out;
7811 }
7812
7825 public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false, $morecss = 'maxwidth200 widthcentpercentminusx')
7826 {
7827 global $conf, $langs;
7828
7829 $langs->loadCacheCurrencies(''); // Load ->cache_currencies
7830
7831 $TCurrency = array();
7832
7833 $sql = "SELECT code FROM " . $this->db->prefix() . "multicurrency";
7834 $sql .= " WHERE entity IN ('" . getEntity('multicurrency') . "')";
7835 if ($filter) {
7836 $sql .= forgeSQLFromUniversalSearchCriteria($filter);
7837 }
7838 $resql = $this->db->query($sql);
7839 if ($resql) {
7840 while ($obj = $this->db->fetch_object($resql)) {
7841 $TCurrency[$obj->code] = $obj->code;
7842 }
7843 }
7844
7845 $out = '';
7846 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
7847 if ($useempty) {
7848 $out .= '<option value="">&nbsp;</option>';
7849 }
7850 // If company current currency not in table, we add it into list. Should always be available.
7851 if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
7852 $TCurrency[$conf->currency] = $conf->currency;
7853 }
7854 if (count($TCurrency) > 0) {
7855 foreach ($langs->cache_currencies as $code_iso => $currency) {
7856 if (isset($TCurrency[$code_iso])) {
7857 if (!empty($selected) && $selected == $code_iso) {
7858 $out .= '<option value="' . $code_iso . '" selected="selected">';
7859 } else {
7860 $out .= '<option value="' . $code_iso . '">';
7861 }
7862
7863 $out .= $currency['label'];
7864 $out .= ' (' . $langs->getCurrencySymbol($code_iso) . ')';
7865 $out .= '</option>';
7866 }
7867 }
7868 }
7869
7870 $out .= '</select>';
7871
7872 // Make select dynamic
7873 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7874 $out .= ajax_combobox($htmlname);
7875
7876 return $out;
7877 }
7878
7879 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7880
7887 public function load_cache_vatrates($country_code)
7888 {
7889 // phpcs:enable
7890 global $langs, $user, $hookmanager;
7891
7892 $num = count($this->cache_vatrates);
7893 if ($num > 0) {
7894 return $num; // Cache already loaded
7895 }
7896
7897 dol_syslog(__METHOD__, LOG_DEBUG);
7898
7899 // entity and fk_pays are returned so a hook on loadDictionaryCache can tell two rows apart
7900 // when the dictionary is read across entities (most rows carry an empty code)
7901 $sql = "SELECT t.rowid, t.entity, t.fk_pays, t.type_vat, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly, t.einvoice_vatex";
7902 $sql .= " FROM ".$this->db->prefix()."c_tva as t, ".$this->db->prefix()."c_country as c";
7903 $sql .= " WHERE t.fk_pays = c.rowid";
7904 $sql .= " AND t.active > 0";
7905 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7906 $sql .= " AND c.code IN (" . $this->db->sanitize($country_code, 1) . ")";
7907 $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
7908
7909 $resql = $this->db->query($sql);
7910 if ($resql) {
7911 $num = $this->db->num_rows($resql);
7912 if ($num) {
7913 for ($i = 0; $i < $num; $i++) {
7914 $obj = $this->db->fetch_object($resql);
7915
7916 $tmparray = array();
7917 $tmparray['rowid'] = (int) $obj->rowid;
7918 $tmparray['entity'] = (int) $obj->entity;
7919 $tmparray['fk_pays'] = (int) $obj->fk_pays;
7920 $tmparray['type_vat'] = ($obj->type_vat <= 0 ? 0 : $obj->type_vat); // Some version have type_vat corrupted with value -1
7921 $tmparray['code'] = $obj->code;
7922 $tmparray['txtva'] = $obj->taux;
7923 $tmparray['nprtva'] = $obj->recuperableonly;
7924 $tmparray['localtax1'] = $obj->localtax1;
7925 $tmparray['localtax1_type'] = $obj->localtax1_type;
7926 $tmparray['localtax2'] = $obj->localtax2;
7927 $tmparray['localtax2_type'] = $obj->localtax1_type;
7928 $tmparray['einvoice_vatex'] = $obj->einvoice_vatex;
7929
7930 $tmparray['label'] = $obj->taux . '%' . ($obj->code ? ' (' . $obj->code . ')' : ''); // Label must contains only 0-9 , . % or *
7931 $tmparray['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
7932 $positiverates = '';
7933 if ($obj->taux) {
7934 $positiverates .= ($positiverates ? '/' : '') . $obj->taux;
7935 }
7936 if ($obj->localtax1) {
7937 $positiverates .= ($positiverates ? '/' : '') . $obj->localtax1;
7938 }
7939 if ($obj->localtax2) {
7940 $positiverates .= ($positiverates ? '/' : '') . $obj->localtax2;
7941 }
7942 if (empty($positiverates)) {
7943 $positiverates = '0';
7944 }
7945 $tmparray['labelpositiverates'] = $positiverates . ($obj->code ? ' (' . $obj->code . ')' : ''); // Must never be used as key, only label
7946
7947 $this->cache_vatrates[$obj->rowid] = $tmparray;
7948 }
7949
7950 $parameters = array('dictionary' => 'vatrate', 'country_code' => $country_code);
7951 $reshook = $hookmanager->executeHooks('loadDictionaryCache', $parameters, $this); // Note that $action and $object may have been modified by hook
7952 if (empty($reshook)) {
7953 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
7954 $this->cache_vatrates = array_merge($this->cache_vatrates, $hookmanager->resArray);
7955 }
7956 } else {
7957 $this->cache_vatrates = $hookmanager->resArray;
7958 }
7959
7960 return count($this->cache_vatrates);
7961 } else {
7962 $this->error = '<span class="error">';
7963 $this->error .= $langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code);
7964 $reg = array();
7965 if (!empty($user) && $user->admin && preg_match('/\'(..)\'/', $country_code, $reg)) {
7966 $langs->load("errors");
7967 $new_country_code = $reg[1];
7968 $country_id = dol_getIdFromCode($this->db, $new_country_code, 'c_country', 'code', 'rowid');
7969 $this->error .= '<br>'.$langs->trans("ErrorFixThisHere", DOL_URL_ROOT.'/admin/dict.php?id=10'.($country_id > 0 ? '&countryidforinsert='.$country_id : ''));
7970 }
7971 $this->error .= '</span>';
7972 return -1;
7973 }
7974 } else {
7975 $this->error = '<span class="error">' . $this->db->error() . '</span>';
7976 return -2;
7977 }
7978 }
7979
7980 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7981
8004 public function load_tva($htmlname = 'tauxtva', $selectedrate = '', $societe_vendeuse = null, $societe_acheteuse = null, $idprod = 0, $info_bits = 0, $type = '', $options_only = false, $mode = 0, $type_vat = 0)
8005 {
8006 // phpcs:enable
8007 global $langs, $mysoc, $hookmanager;
8008
8009 $langs->load('errors');
8010
8011 $return = '';
8012 // Bypass the default method
8013 $hookmanager->initHooks(array('commonobject'));
8014 $info_bits == 1 ? $is_npr = 1 : $is_npr = 0;
8015 $parameters = array(
8016 'htmlname' => $htmlname,
8017 'selectedrate' => $selectedrate,
8018 'seller' => $societe_vendeuse,
8019 'buyer' => $societe_acheteuse,
8020 'idprod' => $idprod,
8021 'is_npr' => $is_npr,
8022 'type' => $type,
8023 'options_only' => $options_only,
8024 'mode' => $mode,
8025 'type_vat' => $type_vat
8026 );
8027 $reshook = $hookmanager->executeHooks('load_tva', $parameters);
8028 if ($reshook > 0) {
8029 return $hookmanager->resPrint;
8030 } elseif ($reshook === 0) {
8031 $return .= $hookmanager->resPrint;
8032 }
8033
8034 // Define defaultnpr, defaultttx and defaultcode
8035 $defaultnpr = ($info_bits & 0x01);
8036 $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
8037 $defaulttx = str_replace('*', '', $selectedrate);
8038 $defaultcode = '';
8039 $reg = array();
8040 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8041 $defaultcode = $reg[1];
8042 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8043 }
8044 //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
8045
8046 // Check parameters
8047 if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
8048 if ($societe_vendeuse->id == $mysoc->id) {
8049 $return .= '<span class="error">' . $langs->trans("ErrorYourCountryIsNotDefined") . '</span>';
8050 } else {
8051 $return .= '<span class="error">' . $langs->trans("ErrorSupplierCountryIsNotDefined") . '</span>';
8052 }
8053 return $return;
8054 }
8055
8056 //var_dump($societe_acheteuse);
8057 //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";
8058 //exit;
8059
8060 // Define list of countries to use to search VAT rates to show
8061 // First we defined code_country to use to find list.
8062 // country_code must be a c_country ISO code (e.g. 'FR', 'CH'). In some setups it may hold a
8063 // country label (e.g. 'Suisse') instead, which would make the "c.code IN (...)" lookup done by
8064 // load_cache_vatrates() match nothing and wrongly force the VAT rate to 0%. A valid ISO code is
8065 // always 2 chars, so when the value is not a well formed ISO code (empty or a label) and we have
8066 // a valid country id, we recover the ISO code from the authoritative country id. This way we do
8067 // not run any SQL on each page access when we already have a valid ISO code.
8068 $sellercountrycode = is_object($societe_vendeuse) ? $societe_vendeuse->country_code : $mysoc->country_code;
8069 $sellercountryid = is_object($societe_vendeuse) ? $societe_vendeuse->country_id : $mysoc->country_id;
8070 if ((int) $sellercountryid > 0 && strlen((string) $sellercountrycode) != 2) {
8071 $tmpcountrycode = dol_getIdFromCode($this->db, (string) $sellercountryid, 'c_country', 'rowid', 'code');
8072 if (!empty($tmpcountrycode) && !is_numeric($tmpcountrycode)) {
8073 $sellercountrycode = $tmpcountrycode;
8074 }
8075 }
8076 $code_country = "'" . $sellercountrycode . "'"; // Pour compatibilite ascendente
8077
8078 if ($societe_vendeuse == $mysoc && getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) { // If option to have vat for end customer for services is on
8079 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
8080 // If SERVICE_ARE_ECOMMERCE_200238EC=1 combo list vat rate of purchaser and seller countries
8081 // If SERVICE_ARE_ECOMMERCE_200238EC=2 combo list only the vat rate of the purchaser country
8082 $selectVatComboMode = getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC');
8083 if (is_object($societe_vendeuse) && is_object($societe_acheteuse) && isInEEC($societe_vendeuse) && isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()) {
8084 // We also add the buyer country code
8085 if (is_numeric($type)) {
8086 if ($type == 1) { // We know product is a service
8087 switch ($selectVatComboMode) {
8088 case '1':
8089 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8090 break;
8091 case '2':
8092 $code_country = "'" . $societe_acheteuse->country_code . "'";
8093 break;
8094 }
8095 }
8096 } elseif (!$idprod) { // We don't know type of product
8097 switch ($selectVatComboMode) {
8098 case '1':
8099 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8100 break;
8101 case '2':
8102 $code_country = "'" . $societe_acheteuse->country_code . "'";
8103 break;
8104 }
8105 } else {
8106 $prodstatic = new Product($this->db);
8107 $prodstatic->fetch($idprod);
8108 if ($prodstatic->type == Product::TYPE_SERVICE) { // We know product is a service
8109 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8110 }
8111 }
8112 }
8113 }
8114
8115 // Now we load the list of VAT
8116 $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
8117
8118 // Keep only the VAT qualified for $type_vat
8119 $arrayofvatrates = array();
8120 foreach ($this->cache_vatrates as $cachevalue) {
8121 if (empty($cachevalue['type_vat']) || $cachevalue['type_vat'] == $type_vat) {
8122 $arrayofvatrates[] = $cachevalue;
8123 }
8124 }
8125
8126 $num = count($arrayofvatrates);
8127 if ($num > 0) {
8128 // Define the vat rate to preselect (if defaulttx not forced so is -1 or '')
8129 if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
8130 // Define a default thirdparty to use if the seller or buyer is not defined
8131 $tmpthirdparty = new Societe($this->db);
8132 $tmpthirdparty->country_code = $mysoc->country_code;
8133
8134 $defaulttx = get_default_tva(is_object($societe_vendeuse) ? $societe_vendeuse : $tmpthirdparty, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
8135 $defaultnpr = get_default_npr(is_object($societe_vendeuse) ? $societe_vendeuse : $tmpthirdparty, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
8136
8137 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8138 $defaultcode = $reg[1];
8139 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8140 }
8141 if (empty($defaulttx)) {
8142 $defaultnpr = 0;
8143 }
8144 }
8145
8146 // If we fails to find a default vat rate, we take the last one in list
8147 // Because they are sorted in ascending order, the last one will be the higher one (we suppose the higher one is the current rate)
8148 if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
8149 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
8150 // We take the last one found in list
8151 $defaulttx = $arrayofvatrates[$num - 1]['txtva'];
8152 } else {
8153 // We will use the rate defined into MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS
8154 $defaulttx = '';
8155 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
8156 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
8157 }
8158 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8159 $defaultcode = $reg[1];
8160 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8161 }
8162 }
8163 }
8164
8165 // Disabled is true if the seller is not subject to VAT
8166 $disabled = false;
8167 $title = '';
8168 if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && empty($societe_vendeuse->tva_assuj)) {
8169 // When we are seller and we do not use VAT, we want to force to disable VAT selection, except if EXPENSEREPORT_OVERRIDE_VAT is set
8170 // EXPENSEREPORT_OVERRIDE_VAT is a strange option that allow to override/enable VAT regardless of sellet vat option - needed for expense report if
8171 // expense report used for business expenses instead of using supplier invoices (but this is a very bad idea !)
8172 if (!getDolGlobalString('EXPENSEREPORT_OVERRIDE_VAT')) {
8173 $title = ' title="' . dol_escape_htmltag($langs->trans('VATIsNotUsed')) . '"';
8174 $disabled = true;
8175 }
8176 }
8177
8178 if (!$options_only) {
8179 $return .= '<select class="flat valignmiddle minwidth75imp maxwidth100 right" id="' . $htmlname . '" name="' . $htmlname . '"' . ($disabled ? ' disabled' : '') . $title . '>';
8180 }
8181
8182 $selectedfound = false;
8183 foreach ($arrayofvatrates as $rate) {
8184 // Keep only 0 if seller is not subject to VAT
8185 if ($disabled && $rate['txtva'] != 0) {
8186 continue;
8187 }
8188
8189 // Define key to use into select list
8190 $key = $rate['txtva'];
8191 $key .= $rate['nprtva'] ? '*' : '';
8192 if ($mode > 0 && $rate['code']) {
8193 $key .= ' (' . $rate['code'] . ')';
8194 }
8195 if ($mode < 0) {
8196 $key = $rate['rowid'];
8197 }
8198
8199 $return .= '<option value="' . $key . '" data-vatid="'.$rate['rowid'].'"';
8200 if (!$selectedfound) {
8201 if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
8202 if ($defaultcode == $rate['code']) {
8203 $return .= ' selected';
8204 $selectedfound = true;
8205 }
8206 } elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
8207 $return .= ' selected';
8208 $selectedfound = true;
8209 }
8210 }
8211 $return .= '>';
8212
8213 // Show label of VAT
8214 if ($mysoc->country_code == 'IN' || getDolGlobalString('MAIN_VAT_LABEL_IS_POSITIVE_RATES')) {
8215 // Label with all localtax and code. For example: x.y / a.b / c.d (CODE)'
8216 $return .= $rate['labelpositiverates'];
8217 } else {
8218 // Simple label
8219 $return .= vatrate($rate['label']);
8220 }
8221
8222 //$return.=($rate['code']?' '.$rate['code']:'');
8223 $return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the * (old behaviour only if new vat code is not used)
8224
8225 $return .= '</option>';
8226 }
8227
8228 if (!$options_only) {
8229 $return .= '</select>';
8230 //$return .= ajax_combobox($htmlname); // This break for the moment the dynamic autoselection of a value when selecting a product in object lines
8231 }
8232 } else {
8233 $return .= $this->error;
8234 }
8235
8236 $this->num = $num;
8237 return $return;
8238 }
8239
8240
8241 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8242
8267 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 = 0, $addplusone = '', $adddateof = '')
8268 {
8269 // phpcs:enable
8270 dol_syslog(__METHOD__ . ': using select_date is deprecated. Use selectDate instead.', LOG_WARNING);
8271 $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
8272 if (!empty($nooutput)) {
8273 return $retstring;
8274 }
8275 print $retstring;
8276
8277 return '';
8278 }
8279
8295 public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0, $forcenewline = 0)
8296 {
8297 global $langs;
8298
8299 $ret = $this->selectDate($set_time, $prefix . '_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
8300 if ($forcenewline) {
8301 $ret .= '<br>';
8302 }
8303 $ret .= $this->selectDate($set_time_end, $prefix . '_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
8304 return $ret;
8305 }
8306
8335 public function selectDate($set_time = '', $prefix = 're', $h = 0, $m = 0, $empty = 0, $form_name = "", $d = 1, $addnowlink = 0, $disabled = 0, $fullday = '', $addplusone = '', $adddateof = '', $openinghours = '', $stepminutes = 1, $labeladddateof = '', $placeholder = '', $gm = 'auto', $calendarpicto = '')
8336 {
8337 global $conf, $langs;
8338
8339 if ($gm === 'auto') {
8340 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
8341 }
8342
8343 $retstring = '';
8344
8345 if ($prefix == '') {
8346 $prefix = 're';
8347 }
8348 if ($h == '') {
8349 $h = 0;
8350 }
8351 if ($m == '') {
8352 $m = 0;
8353 }
8354 $emptydate = 0;
8355 $emptyhours = 0;
8356 if ($stepminutes <= 0 || $stepminutes > 30) {
8357 $stepminutes = 1;
8358 }
8359 if ($empty == 1) {
8360 $emptydate = 1;
8361 $emptyhours = 1;
8362 }
8363 if ($empty == 2) {
8364 $emptydate = 0;
8365 $emptyhours = 1;
8366 }
8367 $orig_set_time = $set_time;
8368
8369 if ($set_time === '' && $emptydate == 0) {
8370 include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
8371 if ($gm == 'tzuser' || $gm == 'tzuserrel') {
8372 $set_time = dol_now($gm);
8373 } else {
8374 $set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
8375 }
8376 }
8377
8378 // Analysis of the preselected date
8379 $reg = array();
8380 $shour = '';
8381 $smin = '';
8382 $ssec = '';
8383 if (!empty($set_time) && preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', (string) $set_time, $reg)) { // deprecated usage
8384 // Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
8385 $syear = (!empty($reg[1]) ? $reg[1] : '');
8386 $smonth = (!empty($reg[2]) ? $reg[2] : '');
8387 $sday = (!empty($reg[3]) ? $reg[3] : '');
8388 $shour = (!empty($reg[4]) ? $reg[4] : '');
8389 $smin = (!empty($reg[5]) ? $reg[5] : '');
8390 } elseif (strval($set_time) != '' && $set_time != -1) {
8391 // set_time est un timestamps (0 possible)
8392 $syear = dol_print_date($set_time, "%Y", $gm);
8393 $smonth = dol_print_date($set_time, "%m", $gm);
8394 $sday = dol_print_date($set_time, "%d", $gm);
8395 if ($orig_set_time != '') {
8396 $shour = dol_print_date($set_time, "%H", $gm);
8397 $smin = dol_print_date($set_time, "%M", $gm);
8398 $ssec = dol_print_date($set_time, "%S", $gm);
8399 }
8400 } else {
8401 // Date est '' ou vaut -1
8402 $syear = '';
8403 $smonth = '';
8404 $sday = '';
8405 $shour = getDolGlobalString('MAIN_DEFAULT_DATE_HOUR', ($h == -1 ? '23' : ''));
8406 $smin = getDolGlobalString('MAIN_DEFAULT_DATE_MIN', ($h == -1 ? '59' : ''));
8407 $ssec = getDolGlobalString('MAIN_DEFAULT_DATE_SEC', ($h == -1 ? '59' : ''));
8408 }
8409 if ($h == 3 || $h == 4) {
8410 $shour = '';
8411 }
8412 if ($m == 3) {
8413 $smin = '';
8414 }
8415
8416 $nowgmt = dol_now('gmt');
8417 //var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
8418
8419 // You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
8420 $usecalendar = 'combo';
8421 if (!empty($conf->use_javascript_ajax) && (!getDolGlobalString('MAIN_POPUP_CALENDAR') || getDolGlobalString('MAIN_POPUP_CALENDAR') != "none")) {
8422 $usecalendar = ((!getDolGlobalString('MAIN_POPUP_CALENDAR') || getDolGlobalString('MAIN_POPUP_CALENDAR') == 'eldy') ? 'jquery' : getDolGlobalString("MAIN_POPUP_CALENDAR"));
8423 }
8424 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
8425 // If we use a text browser or screen reader, we use the 'combo' date selector
8426 $usecalendar = 'html';
8427 }
8428
8429 if ($d) {
8430 // Show date with popup
8431 if ($usecalendar != 'combo') {
8432 // Set $format and $formatjs and $formatjquery
8433 $reduceformat = (!empty($conf->dol_optimize_smallscreen) ? 1 : 0); // Test on original $format param.
8434 if ($reduceformat) {
8435 $format = str_replace('%Y', '%y', $langs->transnoentitiesnoconv("FormatDateShortInput")); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
8436 $formatjslong = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // don't trust the name
8437 $formatjs = str_replace('yyyy', 'yy', $langs->transnoentitiesnoconv("FormatDateShortJavaInput"));
8438 $formatjquery = str_replace('yyyy', 'yy', $langs->trans("FormatDateShortJQueryInput"));
8439 } else {
8440 $format = $langs->transnoentitiesnoconv("FormatDateShortInput"); // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8441 $formatjslong = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // don't trust the name
8442 $formatjs = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8443 $formatjquery = $langs->trans("FormatDateShortJQueryInput");
8444 }
8445
8446 // Set formatted_date (for example: '%d/%m/%Y', '%m-%d-%y', ...
8447 $formatted_date = '';
8448 if (strval($set_time) != '' && $set_time != -1) {
8449 $formatted_date = dol_print_date($set_time, $format, $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
8450 }
8451
8452 // Calendrier popup version eldy
8453 if ($usecalendar == "eldy") {
8454 // To have this manager working back, you must retrieve all functions showDP child found into the lib_head.js of v4 for example
8455 // and load the js that contains them so the call of showDP will works.
8456 /*
8457 // Input area to enter date manually
8458 $retstring .= '<!-- datepicker usecalendar=eldy --><input id="' . $prefix . '" name="' . $prefix . '" type="text" class="maxwidthdate center" maxlength="11" value="' . $formatted_date . '"';
8459 $retstring .= ($disabled ? ' disabled' : '');
8460 $retstring .= ' onChange="dpChangeDay(\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($formatjslong")) . '\'); "'; // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8461 $retstring .= ' autocomplete="off">';
8462
8463 // Icon calendar
8464 $retstringbuttom = '';
8465 if (!$disabled) {
8466 $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons"';
8467 $base = DOL_URL_ROOT . '/core/';
8468 $retstringbuttom .= ' onClick="showDP(\'' . dol_escape_js($base) . '\',\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($langs->trans("FormatDateShortJavaInput")) . '\',\'' . dol_escape_js($langs->defaultlang) . '\');"';
8469 $retstringbuttom .= '>' . img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink paddingright"') . '</button>';
8470 } else {
8471 $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink paddingright"') . '</button>';
8472 }
8473 $retstring = $retstringbuttom . $retstring;
8474
8475 $retstring .= '<input type="hidden" id="' . $prefix . 'day" name="' . $prefix . 'day" value="' . $sday . '">' . "\n";
8476 $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
8477 $retstring .= '<input type="hidden" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">' . "\n";
8478 */
8479 } elseif ($usecalendar == 'jquery' || $usecalendar == 'html') {
8480 if (!$disabled && $usecalendar != 'html') {
8481 // Output javascript for datepicker
8482 $minYear = getDolGlobalInt('MIN_YEAR_SELECT_DATE', (idate('Y') - 100));
8483 $maxYear = getDolGlobalInt('MAX_YEAR_SELECT_DATE', (idate('Y') + 100));
8484
8485 $retstring .= '<!-- datepicker usecalendar='.$usecalendar.' --><script nonce="' . getNonce() . '" type="text/javascript">';
8486 $retstring .= "$(function(){ $('#" . $prefix . "').datepicker({
8487 dateFormat: '" . dol_escape_js($formatjquery) . "',
8488 autoclose: true,
8489 todayHighlight: true,
8490 yearRange: '" . $minYear . ":" . $maxYear . "',";
8491 if (!empty($conf->dol_use_jmobile)) {
8492 $retstring .= "
8493 beforeShow: function (input, datePicker) {
8494 input.disabled = true;
8495 },
8496 onClose: function (dateText, datePicker) {
8497 this.disabled = false;
8498 },
8499 ";
8500 }
8501 // Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
8502 if (!getDolGlobalString('MAIN_POPUP_CALENDAR_ON_FOCUS')) {
8503 $buttonImage = $calendarpicto ?: DOL_URL_ROOT . "/theme/" . dol_escape_js($conf->theme) . "/img/object_calendarday.png";
8504 $retstring .= "
8505 showOn: 'button', /* both has problem with autocompletion */
8506 buttonImage: '" . $buttonImage . "',
8507 buttonImageOnly: true";
8508 }
8509 $retstring .= "
8510 }) });";
8511 $retstring .= "</script>";
8512 }
8513
8514 // Input area to enter date manually
8515 $retstring .= '<div class="nowraponall inline-block divfordateinput">';
8516 $retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="'.($usecalendar == 'html' ? "date" : "text").'" class="maxwidthdate'.(getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER') ? ' textbrowser' : '').' center" maxlength="11" value="'.$formatted_date.'"';
8517 $retstring .= ($disabled ? ' disabled' : '');
8518 $retstring .= ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '');
8519 $retstring .= ' onChange="dpChangeDay(\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($usecalendar == 'html' ? 'yyyy-mm-dd' : $formatjslong) . '\'); "'; // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
8520 $retstring .= ' autocomplete="off">';
8521
8522 // Icon calendar
8523 if ($disabled) {
8524 $retstringbutton = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink ui-datepicker-notrigger"') . '</button>';
8525 $retstring .= $retstringbutton;
8526 }
8527
8528 $retstring .= '</div>';
8529 $retstring .= '<input type="hidden" id="' . $prefix . 'day" name="' . $prefix . 'day" value="' . $sday . '">' . "\n";
8530 $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
8531 $retstring .= '<input type="hidden" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">' . "\n";
8532 } else {
8533 $retstring .= "Bad value of MAIN_POPUP_CALENDAR";
8534 }
8535 } else {
8536 // Show date with combo selects
8537 // Day
8538 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50imp" id="' . $prefix . 'day" name="' . $prefix . 'day">';
8539
8540 if ($emptydate || $set_time == -1) {
8541 $retstring .= '<option value="0" selected>&nbsp;</option>';
8542 }
8543
8544 for ($day = 1; $day <= 31; $day++) {
8545 $retstring .= '<option value="' . $day . '"' . ($day == $sday ? ' selected' : '') . '>' . $day . '</option>';
8546 }
8547
8548 $retstring .= "</select>";
8549
8550 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'month" name="' . $prefix . 'month">';
8551 if ($emptydate || $set_time == -1) {
8552 $retstring .= '<option value="0" selected>&nbsp;</option>';
8553 }
8554
8555 // Month
8556 for ($month = 1; $month <= 12; $month++) {
8557 $retstring .= '<option value="' . $month . '"' . ($month == $smonth ? ' selected' : '') . '>';
8558 $retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
8559 $retstring .= "</option>";
8560 }
8561 $retstring .= "</select>";
8562
8563 // Year
8564 if ($emptydate || $set_time == -1) {
8565 $retstring .= '<input' . ($disabled ? ' disabled' : '') . ' placeholder="' . dol_escape_htmltag($langs->trans("Year")) . '" class="flat maxwidth50imp valignmiddle" type="number" min="0" max="3000" maxlength="4" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">';
8566 } else {
8567 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'year" name="' . $prefix . 'year">';
8568
8569 $syear = (int) $syear;
8570 for ($year = $syear - 10; $year < (int) $syear + 10; $year++) {
8571 $retstring .= '<option value="' . $year . '"' . ($year == $syear ? ' selected' : '') . '>' . $year . '</option>';
8572 }
8573 $retstring .= "</select>\n";
8574 }
8575 }
8576 }
8577
8578 if ($d && $h) {
8579 $retstring .= (($h == 2 || $h == 4) ? '<br>' : ' ');
8580 $retstring .= '<span class="nowraponall">';
8581 }
8582
8583 if ($h) {
8584 $hourstart = 0;
8585 $hourend = 24;
8586 if ($openinghours != '') {
8587 $openinghours = explode(',', $openinghours);
8588 $hourstart = $openinghours[0];
8589 $hourend = $openinghours[1];
8590 if ($hourend < $hourstart) {
8591 $hourend = $hourstart;
8592 }
8593 }
8594
8595 // Show hour
8596 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75 '; // Note maxwidth50 generates truncated number on some desktops even with same version of chrome that works on others
8597 $retstring .= ($fullday ? $fullday . 'hour' : '') . '" id="' . $prefix . 'hour" name="' . $prefix . 'hour">';
8598 if ($emptyhours) {
8599 $retstring .= '<option value="-1">&nbsp;</option>';
8600 }
8601 for ($hour = $hourstart; $hour < $hourend; $hour++) {
8602 if (strlen($hour) < 2) {
8603 $hour = "0" . $hour;
8604 }
8605 $retstring .= '<option value="' . $hour . '"' . (($hour == $shour) ? ' selected' : '') . '>' . $hour;
8606 $retstring .= '</option>';
8607 }
8608 $retstring .= '</select>';
8609
8610 if ($disabled) {
8611 $retstring .= '<input type="hidden" id="' . $prefix . 'hour" name="' . $prefix . 'hour" value="' . $shour . '">' . "\n";
8612 }
8613 if ($m) {
8614 $retstring .= ":";
8615 }
8616 }
8617
8618 if ($m) {
8619 // Show minutes
8620 $retstring .= '<select ' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75 '; // Note maxwidth50 generates truncated number on some desktops even with same version of chrome that works on others
8621 $retstring .= ($fullday ? $fullday . 'min' : '') . '" id="' . $prefix . 'min" name="' . $prefix . 'min">';
8622 if ($emptyhours) {
8623 $retstring .= '<option value="-1">&nbsp;</option>';
8624 }
8625 for ($min = 0; $min < 60; $min += $stepminutes) {
8626 $min_str = sprintf("%02d", $min);
8627 $retstring .= '<option value="' . $min_str . '"' . (($min_str == $smin) ? ' selected' : '') . '>' . $min_str . '</option>';
8628 }
8629 $retstring .= '</select>';
8630 if ($disabled) {
8631 $retstring .= '<input type="hidden" id="' . $prefix . 'min" name="' . $prefix . 'min" value="' . $smin . '">' . "\n";
8632 }
8633 // Add also seconds
8634 $retstring .= '<input type="hidden" name="' . $prefix . 'sec" value="' . $ssec . '">';
8635 }
8636
8637 if ($d && $h) {
8638 $retstring .= '</span>';
8639 }
8640
8641 // Add a "Now" link
8642 if (!empty($conf->use_javascript_ajax) && $addnowlink && !$disabled) {
8643 // Script which will be inserted in the onClick of the "Now" link
8644 $reset_scripts = "";
8645 if ($addnowlink == 2) { // local computer time
8646 // pad add leading 0 on numbers
8647 $reset_scripts .= "Number.prototype.pad = function(size) {
8648 var s = String(this);
8649 while (s.length < (size || 2)) {s = '0' + s;}
8650 return s;
8651 };
8652 var d = new Date();";
8653 }
8654
8655 // Generate the date part, depending on the use or not of the javascript calendar
8656 if ($addnowlink == 1) { // server time expressed in user time setup
8657 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
8658 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8659 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8660 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8661 } elseif ($addnowlink == 2) {
8662 /* Disabled because the output does not use the string format defined by FormatDateShort key to forge the value into #prefix.
8663 * This break application for foreign languages.
8664 $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));';
8665 $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());';
8666 $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);';
8667 $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());';
8668 */
8669 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
8670 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8671 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8672 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8673 }
8674 /*if ($usecalendar == "eldy")
8675 {
8676 $base=DOL_URL_ROOT.'/core/';
8677 $reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');';
8678 }
8679 else
8680 {
8681 $reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); ';
8682 $reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); ';
8683 $reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); ';
8684 }*/
8685 // Update the hour part
8686 if ($h) {
8687 if ($fullday) {
8688 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8689 }
8690 //$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); ';
8691 if ($addnowlink == 1) {
8692 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
8693 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
8694 } elseif ($addnowlink == 2) {
8695 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(d.getHours().pad());';
8696 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
8697 }
8698
8699 if ($fullday) {
8700 $reset_scripts .= ' } ';
8701 }
8702 }
8703 // Update the minute part
8704 if ($m) {
8705 if ($fullday) {
8706 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8707 }
8708 //$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); ';
8709 if ($addnowlink == 1) {
8710 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
8711 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
8712 } elseif ($addnowlink == 2) {
8713 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(d.getMinutes().pad());';
8714 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
8715 }
8716 if ($fullday) {
8717 $reset_scripts .= ' } ';
8718 }
8719 }
8720 // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
8721 if ($reset_scripts && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
8722 $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonNow" type="button" name="_useless" value="now" onClick="' . $reset_scripts . '">';
8723 $retstring .= $langs->trans("Now");
8724 $retstring .= '</button> ';
8725 }
8726 }
8727
8728 // Add a "Plus one hour" link
8729 if ($conf->use_javascript_ajax && $addplusone && !$disabled) {
8730 // Script which will be inserted in the onClick of the "Add plusone" link
8731 $reset_scripts = "";
8732
8733 // Generate the date part, depending on the use or not of the javascript calendar
8734 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'dayinputnoreduce', 'tzuserrel') . '\');';
8735 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8736 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8737 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8738 // Update the hour part
8739 if ($h) {
8740 if ($fullday) {
8741 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8742 }
8743 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
8744 if ($fullday) {
8745 $reset_scripts .= ' } ';
8746 }
8747 }
8748 // Update the minute part
8749 if ($m) {
8750 if ($fullday) {
8751 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8752 }
8753 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
8754 if ($fullday) {
8755 $reset_scripts .= ' } ';
8756 }
8757 }
8758 // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
8759 if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
8760 $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonPlusOne" type="button" name="_useless2" value="plusone" onClick="' . $reset_scripts . '">';
8761 $retstring .= $langs->trans("DateStartPlusOne");
8762 $retstring .= '</button> ';
8763 }
8764 }
8765
8766 // Add a link to set data
8767 if ($conf->use_javascript_ajax && !empty($adddateof) && !$disabled) {
8768 if (!is_array($adddateof)) {
8769 $arrayofdateof = array(array('adddateof' => $adddateof, 'labeladddateof' => $labeladddateof));
8770 } else {
8771 $arrayofdateof = $adddateof;
8772 }
8773 foreach ($arrayofdateof as $valuedateof) {
8774 $tmpadddateof = empty($valuedateof['adddateof']) ? 0 : $valuedateof['adddateof'];
8775 $tmplabeladddateof = empty($valuedateof['labeladddateof']) ? '' : $valuedateof['labeladddateof'];
8776 $tmparray = dol_getdate($tmpadddateof);
8777 if (empty($tmplabeladddateof)) {
8778 $tmplabeladddateof = $langs->trans("DateInvoice");
8779 }
8780 $reset_scripts = 'console.log(\'Click on now link\'); ';
8781 $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($tmpadddateof, 'dayinputnoreduce').'\');';
8782 $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.$tmparray['mday'].'\');';
8783 $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.$tmparray['mon'].'\');';
8784 $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.$tmparray['year'].'\');';
8785 $retstring .= ' - <button class="dpInvisibleButtons datenowlink" id="dateofinvoice" type="button" name="_dateofinvoice" value="now" onclick="'.$reset_scripts.'">'.$tmplabeladddateof.'</button>';
8786 }
8787 }
8788
8789 return $retstring;
8790 }
8791
8801 public function selectTypeDuration($prefix, $selected = 'i', $excludetypes = array(), $morecss = 'minwidth75 maxwidth100')
8802 {
8803 global $langs;
8804
8805 $TDurationTypes = $this->getDurationTypes($langs);
8806
8807 // Removed undesired duration types
8808 foreach ($excludetypes as $value) {
8809 unset($TDurationTypes[$value]);
8810 }
8811
8812 $retstring = '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $prefix . 'type_duration" name="' . $prefix . 'type_duration">';
8813 foreach ($TDurationTypes as $key => $typeduration) {
8814 $retstring .= '<option value="' . $key . '"';
8815 if ($key == $selected) {
8816 $retstring .= " selected";
8817 }
8818 $retstring .= ">" . $typeduration . "</option>";
8819 }
8820 $retstring .= "</select>";
8821
8822 $retstring .= ajax_combobox('select_' . $prefix . 'type_duration');
8823
8824 return $retstring;
8825 }
8826
8827 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8828
8842 public function select_duration($prefix, $iSecond = '', $disabled = 0, $typehour = 'select', $minunderhours = 0, $nooutput = 0)
8843 {
8844 // phpcs:enable
8845 global $langs;
8846
8847 $retstring = '<span class="nowraponall">';
8848
8849 $hourSelected = '';
8850 $minSelected = '';
8851
8852 // Hours
8853 if ($iSecond != '') {
8854 require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
8855
8856 $hourSelected = convertSecondToTime($iSecond, 'allhour');
8857 $minSelected = convertSecondToTime($iSecond, 'min');
8858 }
8859
8860 if ($typehour == 'select') {
8861 $retstring .= '<select class="flat" id="select_' . $prefix . 'hour" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . '>';
8862 for ($hour = 0; $hour < 25; $hour++) { // For a duration, we allow 24 hours
8863 $retstring .= '<option value="' . $hour . '"';
8864 if (is_numeric($hourSelected) && $hourSelected == $hour) {
8865 $retstring .= " selected";
8866 }
8867 $retstring .= ">" . $hour . "</option>";
8868 }
8869 $retstring .= "</select>";
8870 } elseif ($typehour == 'text' || $typehour == 'textselect') {
8871 $retstring .= '<input placeholder="' . $langs->trans('HourShort') . '" type="number" min="0" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputhour right" value="' . (($hourSelected != '') ? ((int) $hourSelected) : '') . '">';
8872 } else {
8873 return 'BadValueForParameterTypeHour';
8874 }
8875
8876 if ($typehour != 'text') {
8877 $retstring .= ' ' . $langs->trans('HourShort');
8878 } else {
8879 $retstring .= '<span class="">:</span>';
8880 }
8881
8882 // Minutes
8883 if ($minunderhours) {
8884 $retstring .= '<br>';
8885 } else {
8886 if ($typehour != 'text') {
8887 $retstring .= '<span class="hideonsmartphone">&nbsp;</span>';
8888 }
8889 }
8890
8891 if ($typehour == 'select' || $typehour == 'textselect') {
8892 $retstring .= '<select class="flat" id="select_' . $prefix . 'min" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . '>';
8893 $step = getDolGlobalInt('MAIN_DURATION_STEP');
8894 $duration_step = ($step > 0) ? $step : 5;
8895 for ($min = 0; $min <= 59; $min += $duration_step) {
8896 $retstring .= '<option value="' . $min . '"';
8897 if (is_numeric($minSelected) && $minSelected == $min) {
8898 $retstring .= ' selected';
8899 }
8900 $retstring .= '>' . $min . '</option>';
8901 }
8902 $retstring .= "</select>";
8903 } elseif ($typehour == 'text') {
8904 $retstring .= '<input placeholder="' . $langs->trans('MinuteShort') . '" type="number" min="0" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputminute right" value="' . (($minSelected != '') ? ((int) $minSelected) : '') . '">';
8905 }
8906
8907 if ($typehour != 'text') {
8908 $retstring .= ' ' . $langs->trans('MinuteShort');
8909 }
8910
8911 $retstring .= "</span>";
8912
8913 if (!empty($nooutput)) {
8914 return $retstring;
8915 }
8916
8917 print $retstring;
8918
8919 return '';
8920 }
8921
8941 public function selectTickets($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0)
8942 {
8943 global $langs, $conf;
8944
8945 $out = '';
8946
8947 // check parameters
8948 if (is_null($ajaxoptions)) {
8949 $ajaxoptions = array();
8950 }
8951
8952 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
8953 $placeholder = '';
8954
8955 if ($selected && empty($selected_input_value)) {
8956 require_once DOL_DOCUMENT_ROOT . '/ticket/class/ticket.class.php';
8957 $tickettmpselect = new Ticket($this->db);
8958 $tickettmpselect->fetch((int) $selected);
8959 $selected_input_value = $tickettmpselect->ref;
8960 unset($tickettmpselect);
8961 }
8962
8963 $urloption = '';
8964 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/ticket/ajax/tickets.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
8965
8966 if (empty($hidelabel)) {
8967 $out .= $langs->trans("RefOrLabel") . ' : ';
8968 } elseif ($hidelabel > 1) {
8969 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
8970 if ($hidelabel == 2) {
8971 $out .= img_picto($langs->trans("Search"), 'search');
8972 }
8973 }
8974 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
8975 if ($hidelabel == 3) {
8976 $out .= img_picto($langs->trans("Search"), 'search');
8977 }
8978 } else {
8979 $out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, '', $status, 0, $showempty, $forcecombo, $morecss);
8980 }
8981
8982 if (empty($nooutput)) {
8983 print $out;
8984 } else {
8985 return $out;
8986 }
8987 return '';
8988 }
8989
8990
9007 public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
9008 {
9009 global $langs;
9010
9011 $out = '';
9012 $outarray = array();
9013
9014 $selectFields = " p.rowid, p.ref, p.message";
9015
9016 $sql = "SELECT ";
9017 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9018 $sql .= " FROM " . $this->db->prefix() . "ticket as p";
9019 $sql .= ' WHERE p.entity IN (' . getEntity('ticket') . ')';
9020
9021 // Add criteria on ref/label
9022 if ($filterkey != '') {
9023 $sql .= ' AND (';
9024 $prefix = getDolGlobalString('TICKET_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if TICKET_DONOTSEARCH_ANYWHERE is on
9025 // For natural search
9026 $search_crit = explode(' ', $filterkey);
9027 $i = 0;
9028 if (count($search_crit) > 1) {
9029 $sql .= "(";
9030 }
9031 foreach ($search_crit as $crit) {
9032 if ($i > 0) {
9033 $sql .= " AND ";
9034 }
9035 $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.subject LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9036 $sql .= ")";
9037 $i++;
9038 }
9039 if (count($search_crit) > 1) {
9040 $sql .= ")";
9041 }
9042 $sql .= ')';
9043 }
9044
9045 $sql .= $this->db->plimit($limit, 0);
9046
9047 // Build output string
9048 dol_syslog(get_class($this) . "::selectTicketsList search tickets", LOG_DEBUG);
9049 $result = $this->db->query($sql);
9050 if ($result) {
9051 require_once DOL_DOCUMENT_ROOT . '/ticket/class/ticket.class.php';
9052 require_once DOL_DOCUMENT_ROOT . '/core/lib/ticket.lib.php';
9053
9054 $num = $this->db->num_rows($result);
9055
9056 $events = array();
9057
9058 if (!$forcecombo) {
9059 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9060 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('TICKET_USE_SEARCH_TO_SELECT'));
9061 }
9062
9063 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9064
9065 $textifempty = '';
9066 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9067 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9068 if (getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9069 if ($showempty && !is_numeric($showempty)) {
9070 $textifempty = $langs->trans($showempty);
9071 } else {
9072 $textifempty .= $langs->trans("All");
9073 }
9074 } else {
9075 if ($showempty && !is_numeric($showempty)) {
9076 $textifempty = $langs->trans($showempty);
9077 }
9078 }
9079 if ($showempty) {
9080 $out .= '<option value="0" selected>' . $textifempty . '</option>';
9081 }
9082
9083 $i = 0;
9084 while ($num && $i < $num) {
9085 $opt = '';
9086 $optJson = array();
9087 $objp = $this->db->fetch_object($result);
9088
9089 $this->constructTicketListOption($objp, $opt, $optJson, $selected, $filterkey);
9090 '@phan-var-force array{key:string,value:mixed,type:int} $optJson';
9091 // Add new entry
9092 // "key" value of json key array is used by jQuery automatically as selected value
9093 // "label" value of json key array is used by jQuery automatically as text for combo box
9094 $out .= $opt;
9095 array_push($outarray, $optJson);
9096
9097 $i++;
9098 }
9099
9100 $out .= '</select>';
9101
9102 $this->db->free($result);
9103
9104 if (empty($outputmode)) {
9105 return $out;
9106 }
9107 return $outarray;
9108 } else {
9109 dol_print_error($this->db);
9110 }
9111
9112 return array();
9113 }
9114
9126 protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9127 {
9128 $outkey = '';
9129 $outref = '';
9130 $outtype = '';
9131
9132 $outkey = $objp->rowid;
9133 $outref = $objp->ref;
9134
9135 $opt = '<option value="' . $objp->rowid . '"';
9136 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9137 $opt .= '>';
9138 $opt .= $objp->ref;
9139 $objRef = $objp->ref;
9140 if (!empty($filterkey) && $filterkey != '') {
9141 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
9142 }
9143
9144 $opt .= "</option>\n";
9145 $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
9146 }
9147
9167 public function selectProjects($selected = '', $htmlname = 'projectid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0)
9168 {
9169 global $langs, $conf;
9170
9171 $out = '';
9172
9173 // check parameters
9174 if (is_null($ajaxoptions)) {
9175 $ajaxoptions = array();
9176 }
9177
9178 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9179 $placeholder = '';
9180
9181 if ($selected && empty($selected_input_value)) {
9182 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
9183 $projecttmpselect = new Project($this->db);
9184 $projecttmpselect->fetch((int) $selected);
9185 $selected_input_value = $projecttmpselect->ref;
9186 unset($projecttmpselect);
9187 }
9188
9189 $urloption = '';
9190 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/projet/ajax/projects.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
9191
9192 if (empty($hidelabel)) {
9193 $out .= $langs->trans("RefOrLabel") . ' : ';
9194 } elseif ($hidelabel > 1) {
9195 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
9196 if ($hidelabel == 2) {
9197 $out .= img_picto($langs->trans("Search"), 'search');
9198 }
9199 }
9200 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
9201 if ($hidelabel == 3) {
9202 $out .= img_picto($langs->trans("Search"), 'search');
9203 }
9204 } else {
9205 $out .= $this->selectProjectsList($selected, $htmlname, $filtertype, $limit, '', $status, 0, $showempty, $forcecombo, $morecss);
9206 }
9207
9208 if (empty($nooutput)) {
9209 print $out;
9210 } else {
9211 return $out;
9212 }
9213 return '';
9214 }
9215
9232 public function selectProjectsList($selected = '', $htmlname = 'projectid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
9233 {
9234 global $langs, $conf;
9235
9236 $out = '';
9237 $outarray = array();
9238
9239 $selectFields = " p.rowid, p.ref";
9240
9241 $sql = "SELECT ";
9242 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9243 $sql .= " FROM " . $this->db->prefix() . "projet as p";
9244 $sql .= ' WHERE p.entity IN (' . getEntity('project') . ')';
9245
9246 // Add criteria on ref/label
9247 if ($filterkey != '') {
9248 $sql .= ' AND (';
9249 $prefix = !getDolGlobalString('TICKET_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
9250 // For natural search
9251 $search_crit = explode(' ', $filterkey);
9252 $i = 0;
9253 if (count($search_crit) > 1) {
9254 $sql .= "(";
9255 }
9256 foreach ($search_crit as $crit) {
9257 if ($i > 0) {
9258 $sql .= " AND ";
9259 }
9260 $sql .= "p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9261 $sql .= "";
9262 $i++;
9263 }
9264 if (count($search_crit) > 1) {
9265 $sql .= ")";
9266 }
9267 $sql .= ')';
9268 }
9269
9270 $sql .= $this->db->plimit($limit, 0);
9271
9272 // Build output string
9273 dol_syslog(get_class($this) . "::selectProjectsList search projects", LOG_DEBUG);
9274 $result = $this->db->query($sql);
9275 if ($result) {
9276 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
9277 require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
9278
9279 $num = $this->db->num_rows($result);
9280
9281 $events = array();
9282
9283 if (!$forcecombo) {
9284 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9285 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('PROJECT_USE_SEARCH_TO_SELECT'));
9286 }
9287
9288 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9289
9290 $textifempty = '';
9291 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9292 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9293 if (getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT')) {
9294 if ($showempty && !is_numeric($showempty)) {
9295 $textifempty = $langs->trans($showempty);
9296 } else {
9297 $textifempty .= $langs->trans("All");
9298 }
9299 } else {
9300 if ($showempty && !is_numeric($showempty)) {
9301 $textifempty = $langs->trans($showempty);
9302 }
9303 }
9304 if ($showempty) {
9305 $out .= '<option value="0" selected>' . $textifempty . '</option>';
9306 }
9307
9308 $i = 0;
9309 while ($num && $i < $num) {
9310 $opt = '';
9311 $optJson = array();
9312 $objp = $this->db->fetch_object($result);
9313
9314 $this->constructProjectListOption($objp, $opt, $optJson, $selected, $filterkey);
9315 // Add new entry
9316 // "key" value of json key array is used by jQuery automatically as selected value
9317 // "label" value of json key array is used by jQuery automatically as text for combo box
9318 $out .= $opt;
9319 array_push($outarray, $optJson);
9320
9321 $i++;
9322 }
9323
9324 $out .= '</select>';
9325
9326 $this->db->free($result);
9327
9328 if (empty($outputmode)) {
9329 return $out;
9330 }
9331 return $outarray;
9332 } else {
9333 dol_print_error($this->db);
9334 }
9335
9336 return array();
9337 }
9338
9352 protected function constructProjectListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9353 {
9354 $outkey = '';
9355 $outref = '';
9356 $outtype = '';
9357
9358 $label = $objp->label;
9359
9360 $outkey = $objp->rowid;
9361 $outref = $objp->ref;
9362 $outlabel = $objp->label;
9363 $outtype = $objp->fk_product_type;
9364
9365 $opt = '<option value="' . $objp->rowid . '"';
9366 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9367 $opt .= '>';
9368 $opt .= $objp->ref;
9369 $objRef = $objp->ref;
9370 if (!empty($filterkey) && $filterkey != '') {
9371 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', (string) $objRef, 1);
9372 }
9373
9374 $opt .= "</option>\n";
9375 $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
9376 }
9377
9378
9399 public function selectMembers($selected = '', $htmlname = 'adherentid', $filtertype = '', $limit = 0, $status = 1, $selected_input_value = '', $hidelabel = 0, $ajaxoptions = array(), $socid = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $selected_combinations = null, $nooutput = 0, $excludeids = array())
9400 {
9401 global $langs, $conf;
9402
9403 $out = '';
9404
9405 // check parameters
9406 if (is_null($ajaxoptions)) {
9407 $ajaxoptions = array();
9408 }
9409
9410 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9411 $placeholder = '';
9412
9413 if ($selected && empty($selected_input_value)) {
9414 require_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
9415 $adherenttmpselect = new Adherent($this->db);
9416 $adherenttmpselect->fetch((int) $selected);
9417 $selected_input_value = $adherenttmpselect->ref;
9418 unset($adherenttmpselect);
9419 }
9420
9421 $urloption = '';
9422
9423 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/adherents/ajax/adherents.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
9424
9425 if (empty($hidelabel)) {
9426 $out .= $langs->trans("RefOrLabel") . ' : ';
9427 } elseif ($hidelabel > 1) {
9428 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
9429 if ($hidelabel == 2) {
9430 $out .= img_picto($langs->trans("Search"), 'search');
9431 }
9432 }
9433 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
9434 if ($hidelabel == 3) {
9435 $out .= img_picto($langs->trans("Search"), 'search');
9436 }
9437 } else {
9438 $filterkey = '';
9439
9440 $out .= $this->selectMembersList($selected, $htmlname, $filtertype, $limit, $filterkey, $status, 0, $showempty, $forcecombo, $morecss, $excludeids);
9441 }
9442
9443 if (empty($nooutput)) {
9444 print $out;
9445 } else {
9446 return $out;
9447 }
9448 return '';
9449 }
9450
9468 public function selectMembersList($selected = '', $htmlname = 'adherentid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $excludeids = array())
9469 {
9470 global $langs, $conf;
9471
9472 $out = '';
9473 $outarray = array();
9474
9475 $selectFields = " p.rowid, p.ref, p.firstname, p.lastname, p.fk_adherent_type";
9476
9477 $sql = "SELECT ";
9478 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9479 $sql .= " FROM " . $this->db->prefix() . "adherent as p";
9480 $sql .= ' WHERE p.entity IN (' . getEntity('adherent') . ')';
9481
9482 // Add criteria on ref/label
9483 if ($filterkey != '') {
9484 $sql .= ' AND (';
9485 $prefix = !getDolGlobalString('MEMBER_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
9486 // For natural search
9487 $search_crit = explode(' ', $filterkey);
9488 $i = 0;
9489 if (count($search_crit) > 1) {
9490 $sql .= "(";
9491 }
9492 foreach ($search_crit as $crit) {
9493 if ($i > 0) {
9494 $sql .= " AND ";
9495 }
9496 $sql .= "(p.firstname LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9497 $sql .= " OR p.lastname LIKE '" . $this->db->escape($prefix . $crit) . "%')";
9498 $i++;
9499 }
9500 if (count($search_crit) > 1) {
9501 $sql .= ")";
9502 }
9503 $sql .= ')';
9504 }
9505 if ($status != -1) {
9506 $sql .= ' AND statut = ' . ((int) $status);
9507 }
9508 if (!empty($excludeids)) {
9509 $sql .= " AND p.rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeids)) . ")";
9510 }
9511 $sql .= $this->db->plimit($limit, 0);
9512
9513 // Build output string
9514 dol_syslog(get_class($this) . "::selectMembersList search adherents", LOG_DEBUG);
9515 $result = $this->db->query($sql);
9516 if ($result) {
9517 require_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
9518 require_once DOL_DOCUMENT_ROOT . '/core/lib/member.lib.php';
9519
9520 $num = $this->db->num_rows($result);
9521
9522 $events = array();
9523
9524 if (!$forcecombo) {
9525 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9526 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('PROJECT_USE_SEARCH_TO_SELECT'));
9527 }
9528
9529 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9530
9531 $textifempty = '';
9532 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9533 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9534 if (getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT')) {
9535 if ($showempty && !is_numeric($showempty)) {
9536 $textifempty = $langs->trans($showempty);
9537 } else {
9538 $textifempty .= $langs->trans("All");
9539 }
9540 } else {
9541 if ($showempty && !is_numeric($showempty)) {
9542 $textifempty = $langs->trans($showempty);
9543 }
9544 }
9545 if ($showempty) {
9546 $out .= '<option value="-1" selected>' . $textifempty . '</option>';
9547 }
9548
9549 $i = 0;
9550 while ($num && $i < $num) {
9551 $opt = '';
9552 $optJson = array();
9553 $objp = $this->db->fetch_object($result);
9554
9555 $this->constructMemberListOption($objp, $opt, $optJson, $selected, $filterkey);
9556
9557 // Add new entry
9558 // "key" value of json key array is used by jQuery automatically as selected value
9559 // "label" value of json key array is used by jQuery automatically as text for combo box
9560 $out .= $opt;
9561 array_push($outarray, $optJson);
9562
9563 $i++;
9564 }
9565
9566 $out .= '</select>';
9567
9568 $this->db->free($result);
9569
9570 if (empty($outputmode)) {
9571 return $out;
9572 }
9573 return $outarray;
9574 } else {
9575 dol_print_error($this->db);
9576 }
9577
9578 return array();
9579 }
9580
9592 protected function constructMemberListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9593 {
9594 $outkey = '';
9595 $outlabel = '';
9596 $outtype = '';
9597
9598 $outkey = $objp->rowid;
9599 $outlabel = dolGetFirstLastname($objp->firstname, $objp->lastname);
9600 $outtype = $objp->fk_adherent_type;
9601
9602 $opt = '<option value="' . $objp->rowid . '"';
9603 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9604 $opt .= '>';
9605 if (!empty($filterkey) && $filterkey != '') {
9606 $outlabel = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $outlabel, 1);
9607 }
9608 $opt .= $outlabel;
9609 $opt .= "</option>\n";
9610
9611 $optJson = array('key' => $outkey, 'value' => $outlabel, 'type' => $outtype);
9612 }
9613
9635 public function selectForForms($objectdesc, $htmlname, $preSelectedValue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $disabled = 0, $selected_input_value = '', $objectfield = '')
9636 {
9637 global $conf, $extrafields, $user, $hookmanager, $action;
9638
9639 // Example of common usage for a link to a thirdparty
9640
9641 // We got this in a modulebuilder form of "MyObject" of module "mymodule".
9642 // When ->fields is array( ... "fk_soc" => array("type"=>"integer:Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))" ...), we have
9643 // $objectdesc = 'Societe'
9644 // $objectfield = Method 1: 'myobject@mymodule:fk_soc' ('fk_soc' is code to retrieve myobject->fields['fk_soc'])
9645 // Method 2 recommended (it can be the array): array("type"=>"integer:Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))" ...)
9646
9647 // We got this when showing an extrafields on resource that is a link to societe
9648 // When extrafields 'link_to_societe' for object Resource is 'link' to 'Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))', we have
9649 // $objectdesc = 'Societe'
9650 // $objectfield = Method 1: 'resource:options_link_to_societe'
9651 // Method 2 recommended (it can be the array): array("type"=>'Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))" ...)
9652
9653 // With old usage:
9654 // $objectdesc = 'Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))'
9655 // $objectfield = ''
9656
9657 //var_dump($objectdesc.' '.$objectfield);
9658 //debug_print_backtrace();
9659
9660 $objectdescorig = $objectdesc;
9661 $objecttmp = null;
9662 $InfoFieldList = array();
9663 $classname = '';
9664 $filter = ''; // Ensure filter has value (for static analysis)
9665 $sortfield = ''; // Ensure filter has value (for static analysis)
9666
9667 if (is_array($objectfield)) { // objectfield is an array
9668 $objectdesc = $objectfield['type'];
9669 $objectdesc = preg_replace('/^integer[^:]*:/', '', $objectdesc);
9670 } elseif ($objectfield) { // objectfield is a string. We must retrieve the objectdesc from the field or extrafield. Deprecated, it is better to provide the array record directly.
9671 // Example: $objectfield = 'product:options_package' or 'myobject@mymodule:options_myfield'
9672 $tmparray = explode(':', $objectfield);
9673
9674 // Get instance of object from $element
9675 $objectforfieldstmp = fetchObjectByElement(0, strtolower($tmparray[0]));
9676
9677 if (is_object($objectforfieldstmp)) {
9678 $objectdesc = '';
9679
9680 $reg = array();
9681 if (preg_match('/^options_(.*)$/', $tmparray[1], $reg)) {
9682 // For a property in extrafields
9683 $key = $reg[1];
9684 // fetch optionals attributes and labels
9685 $extrafields->fetch_name_optionals_label($objectforfieldstmp->table_element);
9686
9687 if (!empty($extrafields->attributes[$objectforfieldstmp->table_element]['type'][$key]) && $extrafields->attributes[$objectforfieldstmp->table_element]['type'][$key] == 'link') {
9688 if (!empty($extrafields->attributes[$objectforfieldstmp->table_element]['param'][$key]['options'])) {
9689 $tmpextrafields = array_keys($extrafields->attributes[$objectforfieldstmp->table_element]['param'][$key]['options']);
9690 $objectdesc = $tmpextrafields[0];
9691 }
9692 }
9693 } else {
9694 // For a property in ->fields
9695 if (array_key_exists($tmparray[1], $objectforfieldstmp->fields)) {
9696 $objectdesc = $objectforfieldstmp->fields[$tmparray[1]]['type'];
9697 $objectdesc = preg_replace('/^integer[^:]*:/', '', $objectdesc);
9698 }
9699 }
9700 }
9701 }
9702
9703 if ($objectdesc) {
9704 // Example of value for $objectdesc:
9705 // Bom:bom/class/bom.class.php:0:t.status=1
9706 // Bom:bom/class/bom.class.php:0:t.status=1:ref
9707 // Bom:bom/class/bom.class.php:0:(t.status:=:1) OR (t.field2:=:2):ref
9708 $InfoFieldList = explode(":", $objectdesc, 4);
9709 $vartmp = (empty($InfoFieldList[3]) ? '' : $InfoFieldList[3]);
9710 $reg = array();
9711 if (preg_match('/^.*:(\w*)$/', $vartmp, $reg)) {
9712 $InfoFieldList[4] = $reg[1]; // take the sort field
9713 }
9714 $InfoFieldList[3] = preg_replace('/:\w*$/', '', $vartmp); // take the filter field
9715
9716 $classname = $InfoFieldList[0];
9717 $classpath = empty($InfoFieldList[1]) ? '' : $InfoFieldList[1];
9718 //$addcreatebuttonornot = empty($InfoFieldList[2]) ? 0 : $InfoFieldList[2];
9719 $filter = empty($InfoFieldList[3]) ? '' : $InfoFieldList[3];
9720 $sortfield = empty($InfoFieldList[4]) ? '' : $InfoFieldList[4];
9721
9722 // Load object according to $id and $element
9723 $objecttmp = fetchObjectByElement(0, strtolower($InfoFieldList[0]));
9724
9725 // Fallback to another solution to get $objecttmp
9726 if (empty($objecttmp) && !empty($classpath)) {
9727 dol_include_once($classpath);
9728
9729 if ($classname && class_exists($classname)) {
9730 $objecttmp = new $classname($this->db);
9731 }
9732 }
9733 }
9734
9735 // Make some replacement in $filter. May not be used if we used the ajax mode with $objectfield. In such a case
9736 // we propagate the $objectfield and not the filter and replacement is done by the ajax/selectobject.php component.
9737 $sharedentities = (is_object($objecttmp) && property_exists($objecttmp, 'element')) ? getEntity($objecttmp->element) : strtolower($classname);
9738 $filter = str_replace(
9739 array('__ENTITY__', '__SHARED_ENTITIES__', '__USER_ID__'),
9740 array($conf->entity, $sharedentities, $user->id),
9741 $filter
9742 );
9743
9744 if (!is_object($objecttmp)) {
9745 dol_syslog('selectForForms: Error bad setup of field objectdescorig=' . $objectdescorig.', objectfield='.(is_array($objectfield) ? 'array' : $objectfield).', objectdesc='.$objectdesc, LOG_WARNING);
9746 return 'selectForForms: Error bad setup of field objectdescorig=' . $objectdescorig.', objectfield='.(is_array($objectfield) ? 'array' : $objectfield).', objectdesc='.$objectdesc;
9747 }
9748 '@phan-var-force CommonObject $objecttmp';
9750 //var_dump($filter);
9751 $prefixforautocompletemode = $objecttmp->element;
9752 if ($prefixforautocompletemode == 'societe') {
9753 $prefixforautocompletemode = 'company';
9754 }
9755 if ($prefixforautocompletemode == 'product') {
9756 $prefixforautocompletemode = 'produit';
9757 }
9758
9759 $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
9760
9761 dol_syslog(get_class($this) . "::selectForForms filter=" . $filter, LOG_DEBUG);
9762
9763 // Generate the combo HTML component
9764 $out = '';
9765 if (!empty($conf->use_javascript_ajax) && getDolGlobalString($confkeyforautocompletemode) && !$forcecombo) {
9766 // No immediate load of all database
9767 $placeholder = '';
9768
9769 if ($preSelectedValue && empty($selected_input_value)) {
9770 $objecttmp->fetch($preSelectedValue);
9771 $selected_input_value = ($prefixforautocompletemode == 'company' ? $objecttmp->name : $objecttmp->ref);
9772
9773 $oldValueForShowOnCombobox = 0;
9774 foreach ($objecttmp->fields as $fieldK => $fielV) {
9775 if (!array_key_exists('showoncombobox', $fielV) || !$fielV['showoncombobox'] || empty($objecttmp->$fieldK)) {
9776 continue;
9777 }
9778
9779 if (!$oldValueForShowOnCombobox) {
9780 $selected_input_value = '';
9781 }
9782
9783 $selected_input_value .= $oldValueForShowOnCombobox ? ' - ' : '';
9784 $selected_input_value .= $objecttmp->$fieldK;
9785 $oldValueForShowOnCombobox = empty($fielV['showoncombobox']) ? 0 : $fielV['showoncombobox'];
9786 }
9787 }
9788
9789 // Set url and param to call to get json of the search results
9790 $urlforajaxcall = DOL_URL_ROOT . '/core/ajax/selectobject.php';
9791 $urloption = 'htmlname=' . urlencode($htmlname) . '&outjson=1&objectdesc=' . urlencode($objectdescorig) . (is_scalar($objectfield) ? '&objectfield='.urlencode($objectfield) : '') . ($sortfield ? '&sortfield=' . urlencode($sortfield) : '');
9792 //$urloption = 'htmlname=' . urlencode($htmlname) . '&outjson=1'.(is_scalar($objectfield) ? '&objectfield='.urlencode($objectfield) : '') . ($sortfield ? '&sortfield=' . urlencode($sortfield) : '');
9793
9794 // Hook 'selectForFormsListUrl' - Added to allow modules to modify the AJAX URL
9795 $parameters = array(
9796 'urloption' => $urloption,
9797 'object' => $objecttmp,
9798 'htmlname' => $htmlname,
9799 'filter' => $filter,
9800 'searchkey' => $searchkey,
9801 );
9802 $reshook = $hookmanager->executeHooks('selectForFormsListUrl', $parameters, $objecttmp, $action);
9803 if (!empty($reshook)) {
9804 $urloption = $hookmanager->resPrint;
9805 $hookmanager->resPrint = '';
9806 }
9807
9808 // Activate the auto complete using ajax call.
9809 $out .= ajax_autocompleter((string) $preSelectedValue, $htmlname, $urlforajaxcall, $urloption, getDolGlobalInt($confkeyforautocompletemode), 0);
9810 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
9811 $out .= '<input type="text" class="' . $morecss . '"' . ($disabled ? ' disabled="disabled"' : '') . ' name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '') . ' />';
9812 } else {
9813 // Immediate load of table record.
9814 $out .= $this->selectForFormsList($objecttmp, $htmlname, $preSelectedValue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled, $sortfield, $filter);
9815 }
9816
9817 return $out;
9818 }
9819
9820
9842 public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0, $sortfield = '', $filter = '', $sortorder = 'ASC')
9843 {
9844 global $langs, $user, $hookmanager;
9845
9846 //print "$htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, $outputmode, $disabled";
9847
9848 $prefixforautocompletemode = $objecttmp->element;
9849 if ($prefixforautocompletemode == 'societe') {
9850 $prefixforautocompletemode = 'company';
9851 }
9852 $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
9853
9854 $fieldstoshow = '';
9855 if (!empty($objecttmp->fields)) { // For object that declare it, it is better to use declared fields (like societe, contact, ...)
9856 $tmpfieldstoshow = '';
9857 foreach ($objecttmp->fields as $key => $val) {
9858 if (! (int) dol_eval((string) $val['enabled'], 1, 1, '1')) {
9859 continue;
9860 }
9861 if (!empty($val['showoncombobox'])) {
9862 $tmpfieldstoshow .= ($tmpfieldstoshow ? ',' : '') . 't.' . $key;
9863 }
9864 }
9865 if ($tmpfieldstoshow) {
9866 $fieldstoshow = $tmpfieldstoshow;
9867 }
9868 } elseif ($objecttmp->element === 'category') {
9869 $fieldstoshow = 't.label';
9870 } else {
9871 // For backward compatibility
9872 $objecttmp->fields['ref'] = array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'position' => 10, 'visible' => 4, 'showoncombobox' => 1);
9873 }
9874
9875 if (empty($fieldstoshow)) {
9876 if (!empty($objecttmp->parent_element)) {
9877 $fieldstoshow = 'o.ref';
9878 if (empty($sortfield)) {
9879 $sortfield = 'o.ref';
9880 }
9881 if (in_array($objecttmp->element, ['commandedet', 'propaldet', 'facturedet', 'expeditiondet'])) {
9882 $fieldstoshow .= ',p.ref AS p_ref,p.label,t.description';
9883 $sortfield .= ', p.ref';
9884 }
9885 } elseif (isset($objecttmp->fields['ref'])) {
9886 $fieldstoshow = 't.ref';
9887 } else {
9888 $langs->load("errors");
9889 $this->error = $langs->trans("ErrorNoFieldWithAttributeShowoncombobox");
9890 return $langs->trans('ErrorNoFieldWithAttributeShowoncombobox');
9891 }
9892 }
9893
9894 $out = '';
9895 $outarray = array();
9896 $tmparray = array();
9897
9898 $num = 0;
9899
9900 $sanitizedfieldstoshow = $fieldstoshow;
9901
9902 // Search data
9903 $sql = "SELECT t.rowid, " . $sanitizedfieldstoshow . " FROM " . $this->db->prefix() . $this->db->sanitize($objecttmp->table_element) . " as t";
9904 if (!empty($objecttmp->isextrafieldmanaged)) {
9905 $extrafieldTable = $objecttmp->table_element;
9906 if ($extrafieldTable == 'categorie') {
9907 $extrafieldTable = 'categories'; // For compatibility
9908 }
9909 $sql .= " LEFT JOIN " . $this->db->prefix() . $this->db->sanitize($extrafieldTable) . "_extrafields as e ON t.rowid = e.fk_object";
9910 }
9911 if (!empty($objecttmp->parent_element)) { // If parent_element is defined
9912 '@phan-var-force CommonObjectLine $objecttmp';
9913 $parent_properties = getElementProperties($objecttmp->parent_element);
9914 // @phan-suppress-next-line SqlInjection
9915 $sql .= " INNER JOIN " . $this->db->prefix() . $this->db->sanitize($parent_properties['table_element']) . " as o ON o.rowid = t.".$this->db->sanitize($objecttmp->fk_parent_attribute);
9916 }
9917 if (!empty($objecttmp->parent_element) && in_array($objecttmp->parent_element, ['commande', 'propal', 'facture', 'expedition'])) {
9918 $sql .= " LEFT JOIN " . $this->db->prefix() . "product as p ON p.rowid = t.fk_product";
9919 }
9920 if (!empty($objecttmp->ismultientitymanaged)) {
9921 if ($objecttmp->ismultientitymanaged == 1) { // @phan-suppress-current-line PhanPluginEmptyStatementIf
9922 // No need to join/link another table
9923 }
9924 if (!is_numeric($objecttmp->ismultientitymanaged)) {
9925 $tmparray = explode('@', $objecttmp->ismultientitymanaged);
9926 $sql .= " INNER JOIN " . $this->db->prefix() . $this->db->sanitize($tmparray[1]) . " as parenttable ON parenttable.rowid = t." . $this->db->sanitize($tmparray[0]);
9927 }
9928 }
9929
9930 // Add where from hooks
9931 $parameters = array(
9932 'object' => $objecttmp,
9933 'htmlname' => $htmlname,
9934 'filter' => $filter,
9935 'searchkey' => $searchkey
9936 );
9937
9938 $reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook
9939 if (!empty($hookmanager->resPrint)) {
9940 $sql .= $hookmanager->resPrint;
9941 } else {
9942 $sql .= " WHERE 1=1";
9943
9944 // If table need a multientity restriction
9945 if (!empty($objecttmp->ismultientitymanaged)) {
9946 if ($objecttmp->ismultientitymanaged == 1) {
9947 $sql .= " AND t.entity IN (" . getEntity($objecttmp->element) . ")";
9948 }
9949 if (!is_numeric($objecttmp->ismultientitymanaged)) {
9950 $sql .= " AND parenttable.entity = t." . $this->db->sanitize($tmparray[0]);
9951 }
9952 // If the parent table is llx_societe and user is not an external user (a more robust test done later for external users),
9953 // then we must also check that user has permissions
9954 if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
9955 if (!$user->hasRight('societe', 'client', 'voir') && empty($user->socid)) {
9956 $sql .= " AND EXISTS (SELECT sc.rowid FROM ".$this->db->prefix() . "societe_commerciaux as sc";
9957 $sql .= " WHERE sc.fk_soc = t.fk_soc AND sc.fk_user = ".((int) $user->id).")";
9958 }
9959 }
9960 }
9961
9962 // If user is external user, we must also make a test on thirdparty
9963 if (!empty($user->socid)) {
9964 if ($objecttmp->element == 'societe') {
9965 $sql .= " AND t.rowid = " . ((int) $user->socid);
9966 } elseif (!empty($objecttmp->fields['fk_soc']) || !empty($objecttmp->fields['t.fk_soc']) || property_exists($objecttmp, 'fk_soc') || property_exists($objecttmp, 'socid')) {
9967 $sql .= " AND t.fk_soc = " . ((int) $user->socid);
9968 } elseif (!empty($objecttmp->parent_element)) {
9969 $tmpparent = fetchObjectByElement(0, $objecttmp->parent_element, '', 1);
9970 if (is_object($tmpparent) && (!empty($tmpparent->fields['fk_soc']) || !empty($tmpparent->fields['t.fk_soc']) || property_exists($tmpparent, 'fk_soc') || property_exists($tmpparent, 'socid'))) {
9971 $sql .= " AND o.fk_soc = " . ((int) $user->socid);
9972 }
9973 }
9974 }
9975
9976 $splittedfieldstoshow = explode(',', $fieldstoshow);
9977 foreach ($splittedfieldstoshow as &$field2) {
9978 if (is_numeric($pos = strpos($field2, ' '))) {
9979 $field2 = substr($field2, 0, $pos);
9980 }
9981 }
9982 if ($searchkey != '') {
9983 $sql .= natural_search($splittedfieldstoshow, $searchkey);
9984 }
9985
9986 if ($filter) { // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:>:'20160101')"
9987 $errormessage = '';
9988 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
9989 if ($errormessage) {
9990 return 'Error forging a SQL request from an universal criteria: ' . $errormessage;
9991 }
9992 }
9993 }
9994 $sql .= $this->db->order($sortfield ? $sortfield : $fieldstoshow, $sortorder);
9995 //$sql.=$this->db->plimit($limit, 0);
9996 //print $sql;
9997
9998 // Build output string
9999 $resql = $this->db->query($sql);
10000 if ($resql) {
10001 // Construct $out and $outarray
10002 $out .= '<select id="' . $htmlname . '" class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ($moreparams ? ' ' . $moreparams : '') . ' name="' . $htmlname . '">' . "\n";
10003
10004 // Warning: Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'. Seems it is no more true with selec2 v4
10005 $textifempty = '&nbsp;';
10006
10007 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
10008 if (getDolGlobalInt($confkeyforautocompletemode)) {
10009 if ($showempty && !is_numeric($showempty)) {
10010 $textifempty = $langs->trans($showempty);
10011 } else {
10012 $textifempty .= $langs->trans("All");
10013 }
10014 }
10015 if ($showempty) {
10016 $out .= '<option value="-1">' . $textifempty . '</option>' . "\n";
10017 }
10018
10019 $num = $this->db->num_rows($resql);
10020 $i = 0;
10021 if ($num) {
10022 while ($i < $num) {
10023 $obj = $this->db->fetch_object($resql);
10024 $label = '';
10025 $labelhtml = '';
10026 $tmparray = explode(',', $fieldstoshow);
10027 $oldvalueforshowoncombobox = 0;
10028 foreach ($tmparray as $key => $val) {
10029 $val = preg_replace('/(t|p|o)\./', '', $val);
10030 $label .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
10031 $labelhtml .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
10032 $label .= $obj->$val;
10033 $labelhtml .= $obj->$val;
10034
10035 $oldvalueforshowoncombobox = empty($objecttmp->fields[$val]['showoncombobox']) ? 0 : $objecttmp->fields[$val]['showoncombobox'];
10036 }
10037 if (empty($outputmode)) {
10038 if ($preselectedvalue > 0 && $preselectedvalue == $obj->rowid) {
10039 $out .= '<option value="' . $obj->rowid . '" selected data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
10040 } else {
10041 $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
10042 }
10043 } else {
10044 array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label));
10045 }
10046
10047 $i++;
10048 if (($i % 10) == 0) {
10049 $out .= "\n";
10050 }
10051 }
10052 }
10053
10054 $out .= '</select>' . "\n";
10055
10056 if (!$forcecombo) {
10057 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10058 $out .= ajax_combobox($htmlname, array(), getDolGlobalInt($confkeyforautocompletemode, 0));
10059 }
10060 } else {
10061 dol_print_error($this->db);
10062 }
10063
10064 $this->result = array('nbofelement' => $num);
10065
10066 if ($outputmode) {
10067 return $outarray;
10068 }
10069 return $out;
10070 }
10071
10082 public static function radio($htmlName, $radioItems, $selected = '', $moreGlobalParams = [])
10083 {
10084 // Default parameters for each radio input
10085 $defaultParams = [
10086 'disabled' => false,
10087 'attr' => [
10088 'type' => 'radio',
10089 'name' => $htmlName,
10090 ],
10091 'attrLabel' => [],
10092 'labelIsHtml' => false
10093 ];
10094
10095 // Merge global parameters with defaults
10096 $params = array_merge_recursive_distinct($defaultParams, $moreGlobalParams);
10097
10098 $out = '';
10099 if (!empty($radioItems)) {
10100 foreach ($radioItems as $key => $item) {
10101 // Normalize item to array structure if it's a simple string
10102 if (!is_array($item)) {
10103 $item = [
10104 'attr' => [
10105 'value' => $key,
10106 ],
10107 'label' => $item
10108 ];
10109 }
10110
10111 // Default properties for individual item
10112 $defaultItem = [
10113 'attr' => [
10114 'value' => !isset($item['attr']['value']) ? $key : '',
10115 ],
10116 'label' => '',
10117 ];
10118
10119 // Merge defaults with global params and item-specific properties
10120 $defaultItem = array_merge_recursive_distinct($params, $defaultItem);
10121 $item = array_merge_recursive_distinct($defaultItem, $item);
10122
10123 // Determine if this radio should be checked
10124 if ((is_array($selected) && in_array($item['attr']['value'], $selected, true)) || $selected === $item['attr']['value']) {
10125 $item['attr']['checked'] = true;
10126 }
10127
10128 // Build HTML attributes for input and label
10129 $inputAttributes = implode(' ', commonHtmlAttributeBuilder($item['attr']));
10130 $labelAttributes = implode(' ', commonHtmlAttributeBuilder($item['attrLabel']));
10131
10132 // prevent accidental Xss todo : escape $item['label'] but html friendly compatible
10133 $text = $item['labelIsHtml'] ? $item['label'] : htmlspecialchars($item['label'], ENT_QUOTES | ENT_SUBSTITUTE);
10134
10135 // Generate HTML
10136 $out .= '<label ' . $labelAttributes . '><input ' . $inputAttributes . ' /> ' . $text . '</label> ';
10137 }
10138 }
10139
10140 return $out;
10141 }
10142
10143
10167 public static function selectarray($htmlname, $array, $id = '', $show_empty = 0, $key_in_label = 0, $value_as_key = 0, $moreparam = '', $translate = 0, $maxlen = 0, $disabled = 0, $sort = '', $morecss = 'minwidth75', $addjscombo = 1, $moreparamonempty = '', $disablebademail = 0, $nohtmlescape = 0)
10168 {
10169 global $conf, $langs;
10170
10171 // Do we want a multiselect ?
10172 //$jsbeautify = 0;
10173 //if (preg_match('/^multi/',$htmlname)) $jsbeautify = 1;
10174 $jsbeautify = 1;
10175
10176 if ($value_as_key) {
10177 $array = array_combine($array, $array);
10178 }
10179
10180 '@phan-var-force array{label:string,data-html:string,disable?:int<0,1>,css?:string} $array'; // Array combine breaks information
10181
10182 $out = '';
10183
10184 if ($addjscombo < 0) {
10185 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10186 $addjscombo = 1;
10187 } else {
10188 $addjscombo = 0;
10189 }
10190 }
10191 $idname = str_replace(array('[', ']'), array('', ''), $htmlname);
10192 $out .= '<select id="' . preg_replace('/^\./', '', $idname) . '" ' . ($disabled ? 'disabled="disabled" ' : '') . 'class="flat ' . (preg_replace('/^\./', '', $htmlname)) . ($morecss ? ' ' . $morecss : '') . ' selectformat"';
10193 $out .= ' name="' . preg_replace('/^\./', '', $htmlname) . '" ' . ($moreparam ? $moreparam : '');
10194 $out .= '>'."\n";
10195
10196 if ($show_empty) {
10197 $textforempty = ' ';
10198 if (!empty($conf->use_javascript_ajax)) {
10199 $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
10200 }
10201 if (!is_numeric($show_empty)) {
10202 $textforempty = $show_empty;
10203 }
10204 $out .= '<option class="optiongrey" ' . ($moreparamonempty ? $moreparamonempty . ' ' : '') . 'value="' . (((int) $show_empty) < 0 ? $show_empty : -1) . '"' . ($id == $show_empty ? ' selected' : '') . '>' . dol_escape_htmltag($textforempty) . '</option>' . "\n";
10205 }
10206 if (is_array($array)) {
10207 // Translate
10208 if ($translate) {
10209 foreach ($array as $key => $value) {
10210 if (!is_array($value)) {
10211 $array[$key] = $langs->trans($value);
10212 } else {
10213 $array[$key]['label'] = $langs->trans($value['label']);
10214 }
10215 }
10216 }
10217 // Sort
10218 if ($sort == 'ASC') {
10219 asort($array);
10220 } elseif ($sort == 'DESC') {
10221 arsort($array);
10222 }
10223
10224 foreach ($array as $key => $tmpvalue) {
10225 if (is_array($tmpvalue)) {
10226 $value = $tmpvalue['label'];
10227 //$valuehtml = empty($tmpvalue['data-html']) ? $value : $tmpvalue['data-html'];
10228 $disabled = empty($tmpvalue['disabled']) ? '' : ' disabled';
10229 $style = empty($tmpvalue['css']) ? '' : ' class="' . $tmpvalue['css'] . '"';
10230 } else {
10231 $value = $tmpvalue;
10232 //$valuehtml = $tmpvalue;
10233 $disabled = '';
10234 $style = '';
10235 }
10236 if (!empty($disablebademail)) {
10237 if (($disablebademail == 1 && !preg_match('/&lt;.+@.+&gt;/', $value))
10238 || ($disablebademail == 2 && preg_match('/---/', $value))) {
10239 $disabled = ' disabled';
10240 $style = ' class="warning"';
10241 }
10242 }
10243 if ($key_in_label) {
10244 if (empty($nohtmlescape)) {
10245 $selectOptionValue = dol_escape_htmltag($key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value));
10246 } else {
10247 $selectOptionValue = $key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value);
10248 }
10249 } else {
10250 if (empty($nohtmlescape)) {
10251 $selectOptionValue = dol_escape_htmltag($maxlen ? dol_trunc($value, $maxlen) : $value);
10252 } else {
10253 $selectOptionValue = $maxlen ? dol_trunc($value, $maxlen) : $value;
10254 }
10255 if ($value == '' || $value == '-') {
10256 $selectOptionValue = '&nbsp;';
10257 }
10258 }
10259 $out .= '<option value="' . $key . '"';
10260 $out .= $style . $disabled;
10261 $out .= is_array($tmpvalue) && !empty($tmpvalue['parent']) ? ' parent="' . dolPrintHTMLForAttribute($tmpvalue['parent']) . '"' : '';
10262 if (is_array($id)) {
10263 if (in_array($key, $id) && !$disabled) {
10264 $out .= ' selected'; // To preselect a value
10265 }
10266 } else {
10267 $id = (string) $id; // if $id = 0, then $id = '0'
10268 if ($id != '' && (($id == (string) $key) || ($id == 'ifone' && count($array) == 1)) && !$disabled) {
10269 $out .= ' selected'; // To preselect a value
10270 }
10271 }
10272
10273 if (is_array($tmpvalue)) {
10274 foreach ($tmpvalue as $keyforvalue => $valueforvalue) {
10275 if ($keyforvalue == 'labelhtml') {
10276 $keyforvalue = 'data-html';
10277 }
10278 if (preg_match('/^data-/', $keyforvalue)) { // The best solution if you want to use HTML values into the list is to use data-html.
10279 $out .= ' '.dol_escape_htmltag($keyforvalue).'="'.dol_escape_htmltag($valueforvalue).'"';
10280 }
10281 }
10282 } elseif (!empty($nohtmlescape)) { // deprecated. Use instead the previous cas, an array with 'data-html', 'data-xxx' ... to use HTML content in the select
10283 $out .= ' data-html="' . dol_escape_htmltag($selectOptionValue) . '"';
10284 }
10285
10286 $out .= '>';
10287 $out .= $selectOptionValue;
10288 $out .= "</option>\n";
10289 }
10290 }
10291 $out .= "</select>";
10292
10293 // Add code for jquery to use multiselect
10294 if ($addjscombo && $jsbeautify) {
10295 // Enhance with select2
10296 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10297 $out .= ajax_combobox($idname, array(), 0, 0, 'resolve', (((int) $show_empty) < 0 ? (string) $show_empty : '-1'), $morecss);
10298 }
10299
10300 return $out;
10301 }
10302
10321 public static function selectArrayAjax($htmlname, $url, $id = '', $moreparam = '', $moreparamtourl = '', $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
10322 {
10323 global $conf;
10324 global $delayedhtmlcontent; // Will be used later outside of this function
10325
10326 // TODO Use an internal dolibarr component instead of select2
10327 if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
10328 return '';
10329 }
10330
10331 $out = '<select type="text" class="' . $htmlname . ($morecss ? ' ' . $morecss : '') . '" ' . ($moreparam ? $moreparam . ' ' : '') . 'name="' . $htmlname . '"></select>';
10332
10333 $outdelayed = '';
10334 if (!empty($conf->use_javascript_ajax)) {
10335 $tmpplugin = 'select2';
10336 $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
10337 <script nonce="' . getNonce() . '">
10338 $(document).ready(function () {
10339
10340 ' . ($callurlonselect ? 'var saveRemoteData = [];' : '') . '
10341
10342 $(".' . $htmlname . '").select2({
10343 ajax: {
10344 dir: "ltr",
10345 url: "' . $url . '",
10346 dataType: \'json\',
10347 delay: 250,
10348 data: function (params) {
10349 return {
10350 q: params.term, // search term
10351 page: params.page
10352 }
10353 },
10354 processResults: function (data) {
10355 // parse the results into the format expected by Select2.
10356 // since we are using custom formatting functions we do not need to alter the remote JSON data
10357 //console.log(data);
10358 saveRemoteData = data;
10359 /* format json result for select2 */
10360 result = []
10361 $.each( data, function( key, value ) {
10362 result.push({id: key, text: value.text});
10363 });
10364 //return {results:[{id:\'none\', text:\'aa\'}, {id:\'rrr\', text:\'Red\'},{id:\'bbb\', text:\'Search a into projects\'}], more:false}
10365 //console.log(result);
10366 return {results: result, more: false}
10367 },
10368 cache: true
10369 },
10370 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage,
10371 containerCssClass: \':all:\', /* Line to add class from the original SELECT propagated to the new <span class="select2-selection...> tag */
10372 placeholder: \'' . dol_escape_js($placeholder) . '\',
10373 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10374 minimumInputLength: ' . ((int) $minimumInputLength) . ',
10375 formatResult: function (result, container, query, escapeMarkup) {
10376 return escapeMarkup(result.text);
10377 },
10378 });
10379
10380 ' . ($callurlonselect ? '
10381 /* Code to execute a GET when we select a value */
10382 $(".' . $htmlname . '").change(function() {
10383 var selected = $(\'.' . dol_escape_js($htmlname) . '\').val();
10384 console.log("We select in selectArrayAjax the entry "+selected)
10385 $(\'.' . dol_escape_js($htmlname) . '\').val(""); /* reset visible combo value */
10386 $.each( saveRemoteData, function( key, value ) {
10387 if (key == selected)
10388 {
10389 console.log("selectArrayAjax - Do a redirect to "+value.url)
10390 location.assign(value.url);
10391 }
10392 });
10393 });' : '') . '
10394
10395 });
10396 </script>';
10397 }
10398
10399 if ($acceptdelayedhtml) {
10400 $delayedhtmlcontent .= $outdelayed;
10401 } else {
10402 $out .= $outdelayed;
10403 }
10404 return $out;
10405 }
10406
10426 public static function selectArrayFilter($htmlname, $array, $id = '', $moreparam = '', $disableFiltering = 0, $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0, $textfortitle = '')
10427 {
10428 global $conf;
10429 global $delayedhtmlcontent; // Will be used later outside of this function
10430
10431 // TODO Use an internal dolibarr component instead of select2
10432 if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
10433 return '';
10434 }
10435
10436 $out = '<select type="text"'.($textfortitle ? ' title="'.dol_escape_htmltag($textfortitle).'"' : '').' id="'.$htmlname.'" class="'.$htmlname.($morecss ? ' ' . $morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.'"><option></option></select>';
10437
10438 $formattedarrayresult = array();
10439
10440 foreach ($array as $key => $value) {
10441 $o = new stdClass();
10442 $o->id = $key;
10443 $o->text = $value['text'];
10444 $o->url = $value['url'];
10445 $formattedarrayresult[] = $o;
10446 }
10447
10448 $outdelayed = '';
10449 if (!empty($conf->use_javascript_ajax)) {
10450 $tmpplugin = 'select2';
10451 $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
10452 <script nonce="' . getNonce() . '">
10453 $(document).ready(function () {
10454 var data = ' . json_encode($formattedarrayresult) . ';
10455
10456 ' . ($callurlonselect ? 'var saveRemoteData = ' . json_encode($array) . ';' : '') . '
10457
10458 $(\'.' . dol_escape_js($htmlname) . '\').select2({
10459 data: data,
10460 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage,
10461 containerCssClass: \':all:\', /* Line to add class from the original SELECT propagated to the new <span class="select2-selection...> tag */
10462 placeholder: \'' . dol_escape_js($placeholder) . '\',
10463 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10464 minimumInputLength: ' . ((int) $minimumInputLength) . ',
10465 formatResult: function (result, container, query, escapeMarkup) {
10466 return escapeMarkup(result.text);
10467 },
10468 matcher: function (params, data) {
10469
10470 if(! data.id) return null;';
10471
10472 if ($callurlonselect) {
10473 // We forge the url with 'sall='
10474 $outdelayed .= '
10475
10476 var urlBase = data.url;
10477 var separ = urlBase.indexOf("?") >= 0 ? "&" : "?";
10478 /* console.log("params.term="+params.term); */
10479 /* console.log("params.term encoded="+encodeURIComponent(params.term)); */
10480 saveRemoteData[data.id].url = urlBase + separ + "search_all=" + encodeURIComponent(params.term.replace(/\"/g, ""));';
10481 }
10482
10483 if (!$disableFiltering) {
10484 $outdelayed .= '
10485
10486 if(data.text.match(new RegExp(params.term))) {
10487 return data;
10488 }
10489
10490 return null;';
10491 } else {
10492 $outdelayed .= '
10493
10494 return data;';
10495 }
10496
10497 $outdelayed .= '
10498 }
10499 });
10500
10501 ' . ($callurlonselect ? '
10502 /* Code to execute a GET when we select a value */
10503 $(\'.' . dol_escape_js($htmlname) . '\').change(function() {
10504 var selected = $(\'.' . dol_escape_js($htmlname) . '\').val();
10505 console.log("We select "+selected)
10506
10507 $(\'.' . dol_escape_js($htmlname) . '\').val(""); /* reset visible combo value */
10508 $.each( saveRemoteData, function( key, value ) {
10509 if (key == selected)
10510 {
10511 console.log("selectArrayFilter - Do a redirect to "+value.url)
10512 location.assign(value.url);
10513 }
10514 });
10515 });' : '') . '
10516
10517 });
10518 </script>';
10519 }
10520
10521 if ($acceptdelayedhtml) {
10522 $delayedhtmlcontent .= $outdelayed;
10523 } else {
10524 $out .= $outdelayed;
10525 }
10526 return $out;
10527 }
10528
10547 public static function multiselectarray($htmlname, $array, $selected = array(), $key_in_label = 0, $value_as_key = 0, $morecss = '', $translate = 0, $width = 0, $moreattrib = '', $nu = '', $placeholder = '', $addjscombo = -1)
10548 {
10549 global $conf, $langs;
10550 $out = '';
10551
10552 if ($addjscombo < 0) {
10553 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10554 $addjscombo = 1;
10555 } else {
10556 $addjscombo = 0;
10557 }
10558 }
10559
10560 $useenhancedmultiselect = 0;
10561 if (!empty($conf->use_javascript_ajax) && !defined('MAIN_DO_NOT_USE_JQUERY_MULTISELECT') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {
10562 if ($addjscombo) {
10563 $useenhancedmultiselect = 1; // Use the js multiselect in one line. Possible only if $addjscombo not 0.
10564 }
10565 }
10566
10567 $out .= '<span class="multiselectarray'.$htmlname.'">';
10568
10569 // We need a hidden field because when using the multiselect, if we unselect all, there is no
10570 // variable submitted at all, so no way to make a difference between variable not submitted and variable
10571 // submitted to nothing.
10572 $out .= '<input type="hidden" name="'.$htmlname.'_multiselect" value="1">';
10573 // Output select component
10574 $out .= '<select id="'.$htmlname.'" class="multiselect' . ($useenhancedmultiselect ? ' multiselectononeline' : '') . ($morecss ? ' ' . $morecss : '') . '" multiple name="' . $htmlname . '[]"' . ($moreattrib ? ' ' . $moreattrib : '') . ($width ? ' style="width: ' . (preg_match('/%/', (string) $width) ? $width : $width . 'px') . '"' : '') . '>' . "\n";
10575 if (is_array($array) && !empty($array)) {
10576 if ($value_as_key) {
10577 $array = array_combine($array, $array);
10578 }
10579
10580 if (!empty($array)) {
10581 foreach ($array as $key => $value) {
10582 $tmpkey = $key;
10583 $tmplabel = $value;
10584 $tmplabelhtml = '';
10585 $tmpcolor = '';
10586 $tmppicto = '';
10587 $tmpdisabled = '';
10588 if (is_array($value) && array_key_exists('id', $value) && array_key_exists('label', $value)) {
10589 $tmpkey = $value['id'];
10590 $tmplabel = empty($value['label']) ? '' : $value['label'];
10591 $tmplabelhtml = empty($value['labelhtml']) ? (empty($value['data-html']) ? '' : $value['data-html']) : $value['labelhtml'];
10592 $tmpcolor = empty($value['color']) ? '' : $value['color'];
10593 $tmppicto = empty($value['picto']) ? '' : $value['picto'];
10594 $tmpdisabled = empty($value['disabled']) ? '' : $value['disabled'];
10595 }
10596 $newval = ($translate ? $langs->trans($tmplabel) : $tmplabel);
10597 $newval = ($key_in_label ? $tmpkey . ' - ' . $newval : $newval);
10598
10599 $tmplabelhtml = ($translate ? $langs->trans($tmplabelhtml) : $tmplabelhtml);
10600 $tmplabelhtml = ($key_in_label ? $tmpkey . ' - ' . $tmplabelhtml : $tmplabelhtml);
10601
10602 $out .= '<option value="' . $tmpkey . '"';
10603 if (is_array($selected) && !empty($selected) && in_array((string) $tmpkey, $selected) && ((string) $tmpkey != '')) {
10604 $out .= ' selected';
10605 }
10606 $out .= is_array($value) && array_key_exists('parent', $value) && !empty($value['parent']) ? ' parent="' . dolPrintHTMLForAttribute($value['parent']) . '"' : '';
10607 if ($tmpdisabled) {
10608 $out .= ' disabled="disabled"';
10609 }
10610 if (!empty($tmplabelhtml)) {
10611 $out .= ' data-html="' . dolPrintHTMLForAttribute($tmplabelhtml) . '"';
10612 } else {
10613 $tmplabelhtml = ($tmppicto ? img_picto('', $tmppicto, 'class="pictofixedwidth" style="color: #' . $tmpcolor . '"') : '') . $newval;
10614 $out .= ' data-html="' . dolPrintHTMLForAttribute($tmplabelhtml) . '"';
10615 }
10616 $out .= '>';
10617 $out .= dol_htmlentitiesbr($newval);
10618 $out .= '</option>' . "\n";
10619 }
10620 }
10621 }
10622 $out .= '</select>' . "\n";
10623
10624 $out .= '</span>';
10625
10626 // Add code for jquery to use multiselect
10627 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT')) {
10628 $out .= "\n" . '<!-- JS CODE TO ENABLE select for id ' . $htmlname . ', addjscombo=' . $addjscombo . ' -->';
10629 $out .= "\n" . '<script nonce="' . getNonce() . '">' . "\n";
10630 if ($addjscombo == 1) {
10631 $tmpplugin = getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT', (defined('REQUIRE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : 'select2'));
10632
10633 // If property data-html set, we decode html entities and use this.
10634 // Note that HTML content must have been sanitized from js with dol_escape_htmltag(xxx, 0, 0, '', 0, 1) when building the select option.
10635 // TODO Move this into common js ?
10636 $out .= 'function formatResult(record, container) {' . "\n";
10637 $out .= ' if ($(record.element).attr("data-html") != undefined && typeof htmlEntityDecodeJs === "function") {';
10638 $out .= ' return htmlEntityDecodeJs($(record.element).attr("data-html"));';
10639 $out .= ' }'."\n";
10640 $out .= ' return record.text;';
10641 $out .= '}' . "\n";
10642
10643 $out .= 'function formatSelection(record) {' . "\n";
10644 $out .= ' return record.text;';
10645 $out .= '}' . "\n";
10646
10647 // Load the select2 enhancer
10648 //$out .= 'console.log(\'addjscombo=1 for htmlname=' . dol_escape_js($htmlname) . '\');';
10649 $out .= '$(document).ready(function () {
10650 $(\'#' . dol_escape_js($htmlname) . '\').' . $tmpplugin . '({';
10651 if ($placeholder) {
10652 $out .= '
10653 placeholder: {
10654 id: \'-1\',
10655 text: \''.dol_escape_js($placeholder).'\'
10656 },';
10657 }
10658 $out .= ' dir: \'ltr\',
10659 containerCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag (ko with multiselect) */
10660 dropdownCssClass: \'' . dol_escape_js($morecss) . '\', /* Line to add class on the new <span class="select2-selection...> tag (ok with multiselect). Need full version of select2. */
10661 // Specify format function for dropdown item
10662 formatResult: formatResult,
10663 templateResult: formatResult, /* For 4.0 */
10664 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10665 // Specify format function for selected item
10666 formatSelection: formatSelection,
10667 templateSelection: formatSelection, /* For 4.0 */
10668 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage
10669 });
10670
10671 /* Add also morecss to the css .select2 that is after the #htmlname, for component that are shown dynamically after load, because select2 set
10672 the size only if component is not hidden by default on load */
10673 $(\'#' . dol_escape_js($htmlname) . ' + .select2\').addClass(\'' . dol_escape_js($morecss) . '\');
10674 });' . "\n";
10675 } elseif ($addjscombo == 2 && !defined('DISABLE_MULTISELECT')) {
10676 // Add other js lib
10677 // TODO external lib multiselect/jquery.multi-select.js must have been loaded to use this multiselect plugin
10678 // ...
10679 $out .= 'console.log(\'addjscombo=2 for htmlname=' . dol_escape_js($htmlname) . '\');';
10680 $out .= '$(document).ready(function () {
10681 $(\'#' . dol_escape_js($htmlname) . '\').multiSelect({
10682 containerHTML: \'<div class="multi-select-container">\',
10683 menuHTML: \'<div class="multi-select-menu">\',
10684 buttonHTML: \'<span class="multi-select-button ' . dol_escape_js($morecss) . '">\',
10685 menuItemHTML: \'<label class="multi-select-menuitem">\',
10686 activeClass: \'multi-select-container--open\',
10687 noneText: \'' . dol_escape_js($placeholder) . '\'
10688 });
10689 })';
10690 }
10691 $out .= '</script>';
10692 }
10693
10694 return $out;
10695 }
10696
10697
10711 public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage, $pos = '', $draganddrop = 0)
10712 {
10713 global $conf, $langs, $user;
10714
10715 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10716 return '';
10717 }
10718 if (empty($array)) {
10719 return '';
10720 }
10721
10722 $tmpvar = "MAIN_SELECTEDFIELDS_" . $varpage; // To get list of saved selected fields to show
10723
10724 if (!empty($user->conf->$tmpvar)) { // A list of fields was already customized for user
10725 $tmparray = explode(',', $user->conf->$tmpvar);
10726 foreach ($array as $key => $val) {
10727 //var_dump($key);
10728 //var_dump($tmparray);
10729 if (in_array($key, $tmparray)) {
10730 $array[$key]['checked'] = 1;
10731 } else {
10732 $array[$key]['checked'] = 0;
10733 }
10734 }
10735 } else { // There is no list of fields already customized for user
10736 foreach ($array as $key => $val) {
10737 if (!empty($array[$key]['checked']) && $array[$key]['checked'] < 0) {
10738 $array[$key]['checked'] = 0;
10739 }
10740 }
10741 }
10742
10743 $listoffieldsforselection = '';
10744 $listcheckedstring = '';
10745
10746 foreach ($array as $key => $val) {
10747 // var_dump($val);
10748 // var_dump(array_key_exists('enabled', $val));
10749 // var_dump(!$val['enabled']);
10750 if (array_key_exists('enabled', $val) && isset($val['enabled']) && !$val['enabled']) {
10751 unset($array[$key]); // We don't want this field
10752 continue;
10753 }
10754 if (!empty($val['type']) && $val['type'] == 'separate') {
10755 // Field remains in array but we don't add it into $listoffieldsforselection
10756 //$listoffieldsforselection .= '<li>-----</li>';
10757 continue;
10758 }
10759 if (!empty($val['label']) && $val['label']) {
10760 if (!empty($val['langfile']) && is_object($langs)) {
10761 $langs->load($val['langfile']);
10762 }
10763
10764 // Note: $val['checked'] <> 0 means we must show the field into the combo list @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10765 $listoffieldsforselection .= '<li '.(!empty($draganddrop) ? 'class="fieldsortable" id="'.$key : '').'"><input type="checkbox" id="checkbox' . $key . '" value="' . $key . '"' . ((!array_key_exists('checked', $val) || empty($val['checked']) || $val['checked'] == '-1') ? '' : ' checked="checked"') . ' data-position="'.(empty($val['position']) ? '' : $val['position']).'" />';
10766 $listoffieldsforselection .= '<label for="checkbox' . $key . '" class="paddingleft">';
10767 $listoffieldsforselection .= dolPrintHTML(dol_string_nohtmltag($langs->trans($val['label'])));
10768 $listoffieldsforselection .= '</label>';
10769 if (!empty($draganddrop)) {
10770 $listoffieldsforselection .= img_picto($langs->trans("MoveField", !empty($key) ? $key : 'none'), 'grip_title', 'class="opacitymedium boxhandle hideonsmartphone cursormove marginleftonly"');
10771 }
10772 $listoffieldsforselection .= '</li>';
10773 $listcheckedstring .= (empty($val['checked']) ? '' : $key . ',');
10774 }
10775 }
10776
10777 $out = '<!-- Component multiSelectArrayWithCheckbox ' . $htmlname . ' -->
10778
10779 <dl class="dropdown">
10780 <dt>
10781 <a href="#' . $htmlname . '" class="multiselectpicto">
10782 ' . img_picto('', 'list') . '
10783 </a>
10784 <input type="hidden" class="' . $htmlname . '" name="' . $htmlname . '" value="' . $listcheckedstring . '">
10785 </dt>
10786 <dd class="dropdowndd">
10787 <div class="multiselectcheckbox'.$htmlname.'">
10788 <ul class="'.$htmlname.(((string) $pos == '1' || (string) $pos == 'left') ? 'left' : '').(!empty($draganddrop) ? ' sortable' : '').'">
10789 <li class="liinputsearch">
10790 <input class="inputsearch_dropdownselectedfields width90p minwidth200imp" style="width:90%;" type="text" placeholder="'.$langs->trans('Search').'">
10791 </li>
10792 '.$listoffieldsforselection.'
10793 </ul>
10794 </div>
10795 </dd>
10796 </dl>
10797
10798 <script>
10799 function updateFieldOrder() {
10800 var positionfields = $(".sortable").sortable("toArray");
10801 $.ajax({
10802 url: \''.DOL_URL_ROOT.'/core/ajax/changepositionfields.php?positionfields=\'+positionfields+\'&token='.newToken().'&action=listafterchangingpositionfields&contextpage='.$varpage.'&userid='.$user->id.'\',
10803 async: false,
10804 success: function () {
10805 // reload page
10806 window.location.href = "'.$_SERVER["PHP_SELF"].'";
10807 }
10808 });
10809 }
10810 $( ".sortable" ).sortable({
10811 handle: \'.boxhandle\',
10812 revert: \'invalid\',
10813 items: \'.fieldsortable\',
10814 stop: function(event, ui) {
10815 console.log("We moved box so we call updateBoxOrder with ajax actions");
10816 updateFieldOrder(); /* 1 to avoid message after a move */
10817 }
10818 });
10819 </script>
10820
10821 <script nonce="' . getNonce() . '" type="text/javascript">
10822 jQuery(document).ready(function () {
10823 $(\'.multiselectcheckbox' . $htmlname . ' input[type="checkbox"]\').on("click", function () {
10824 console.log("A new field was added/removed, we edit field input[name=formfilteraction]");
10825
10826 $("input:hidden[name=formfilteraction]").val(\'listafterchangingselectedfields\'); // Update field so we know we changed something on selected fields after POST
10827
10828 var title = $(this).val() + ",";
10829 if ($(this).is(\':checked\')) {
10830 $(\'.' . $htmlname . '\').val(title + $(\'.' . $htmlname . '\').val());
10831 }
10832 else {
10833 $(\'.' . $htmlname . '\').val( $(\'.' . $htmlname . '\').val().replace(title, \'\') )
10834 }
10835 // Now, we submit page
10836 //$(this).parents(\'form:first\').submit();
10837 });
10838
10839 $("input.inputsearch_dropdownselectedfields").on("keyup", function() {
10840 console.log("keyup on inputsearch_dropdownselectedfields");
10841 var value = $(this).val().toLowerCase();
10842 $(\'.multiselectcheckbox'.$htmlname.' li > label\').filter(function() {
10843 $(this).parent().toggle($(this).text().toLowerCase().indexOf(value) > -1)
10844 });
10845 });
10846 ';
10847 if (empty($conf->browser->layout) || $conf->browser->layout != 'phone') {
10848 $out .= '
10849 $(".dropdown dt a").on("click", function () {
10850 console.log("Click on dropdown, we set focus to search field");
10851 setTimeout(() => { $(\'.inputsearch_dropdownselectedfields\').focus(); }, 200);
10852 });';
10853 }
10854 $out .= '
10855 });
10856 </script>
10857
10858 ';
10859 return $out;
10860 }
10861
10871 public function showCategories($id, $type, $rendermode = 0, $nolink = 0)
10872 {
10873 global $conf;
10874
10875 include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
10876
10877 $cat = new Categorie($this->db);
10878 $categories = $cat->containing($id, $type);
10879
10880 if ($rendermode == 1 || $rendermode == 2) {
10881 $toprint = array();
10882 foreach ($categories as $c) {
10883 $ways = $c->print_all_ways('auto', ($nolink ? 'none' : ''), 0, 1, ($rendermode == 2 ? 0 : 1)); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
10884 foreach ($ways as $way) {
10885 $color = $c->color;
10886 $sfortag = '<li class="select2-search-choice-dolibarr noborderoncategories'.(empty($toprint) ? ' nomarginleft' : '');
10887 $forced_color = 'categtextwhite'; // We want color white because the getNomUrl of a tag is always called inside a dark background like '<span color="bbb"></span>' to show it as a tag. TODO Add this in param to force when called outside of span.
10888 if ($c->color && colorIsLight($c->color)) {
10889 $forced_color = 'categtextblack';
10890 }
10891 $sfortag .= ' '.$forced_color;
10892 $sfortag .= '"';
10893 $sfortag .= ($color ? ' style="background: #' . $color . ';"' : ' style="background: #bbb"');
10894 $titlestring = $ways[0];
10895 $titlestring = str_replace('>', ' - ', dol_string_nohtmltag($titlestring));
10896 $sfortag .= ' title="' . dolPrintHTMLForAttribute($titlestring) . '"';
10897 $sfortag .= '>';
10898 if ($rendermode == 1) {
10899 $sfortag .= '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.((int) $c->id).'&type='.urlencode($c->type).'" class="'.$forced_color.'">';
10900 $sfortag .= img_picto('', 'category', 'class="paddingright"');
10901 if ($conf->dol_optimize_smallscreen) {
10902 $sfortag .= dolPrintHTML(dol_trunc($c->label, 8));
10903 } else {
10904 $sfortag .= dolPrintHTML($c->label);
10905 }
10906 $sfortag .= '</a>';
10907 } else {
10908 $sfortag .= $way;
10909 }
10910 $sfortag .= '</li>';
10911
10912 $toprint[] = $sfortag; // Add tag in list of tag to show
10913 }
10914 }
10915 if (empty($toprint)) {
10916 return '';
10917 } else {
10918 return '<div class="select2-container-multi-dolibarr"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
10919 }
10920 }
10921
10922 if ($rendermode == 0) {
10923 $arrayselected = array();
10924 $cate_arbo = $this->select_all_categories($type, '', 'parent', 64, 0, 3);
10925 foreach ($categories as $c) {
10926 $arrayselected[(string) $c->id] = (string) $c->id;
10927 }
10928
10929 return $this->multiselectarray('categories', $cate_arbo, $arrayselected, 0, 0, '', 0, '100%', 'disabled', 'category');
10930 }
10931
10932 return 'ErrorBadValueForParameterRenderMode'; // Should not happened
10933 }
10934
10944 public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleImportElementsList = array(), $title = 'RelatedObjects')
10945 {
10946 global $conf, $langs, $hookmanager;
10947 global $action;
10948 global $db, $user; // Will be used into tpl
10949
10950 dol_syslog(__METHOD__, LOG_DEBUG);
10951
10952 $object->fetchObjectLinked();
10953
10954 // Bypass the default method
10955 $hookmanager->initHooks(array('commonobject'));
10956 $parameters = array(
10957 'morehtmlright' => $morehtmlright,
10958 'compatibleImportElementsList' => &$compatibleImportElementsList,
10959 );
10960 $reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
10961
10962 $nbofdifferenttypes = count($object->linkedObjects);
10963
10964 if (empty($reshook)) {
10965 print '<!-- showLinkedObjectBlock -->';
10966 print load_fiche_titre($langs->trans($title), $morehtmlright, '', 0, '', 'showlinkedobjectblock');
10967
10968
10969 print '<div class="div-table-responsive-no-min">';
10970 print '<table class="noborder allwidth" data-block="showLinkedObject" data-element="' . $object->element . '" data-elementid="' . $object->id . '" >';
10971
10972 print '<tr class="liste_titre">';
10973 print '<td>' . $langs->trans("Type") . '</td>';
10974 print '<td>' . $langs->trans("Ref") . '</td>';
10975 print '<td></td>';
10976 print '<td></td>';
10977 print '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
10978 print '<td class="right">' . $langs->trans("Status") . '</td>';
10979 print '<td></td>';
10980 print '</tr>';
10981
10982 $nboftypesoutput = 0;
10983
10984 foreach ($object->linkedObjects as $objecttype => $objects) {
10985 $tplpath = $element = $subelement = $objecttype;
10986
10987 // to display import button on tpl
10988 global $showImportButton; // Will be used into tpl
10989 $showImportButton = false;
10990 if (!empty($compatibleImportElementsList) && in_array($element, $compatibleImportElementsList)) {
10991 $showImportButton = true;
10992 }
10993
10994 $regs = array();
10995
10996 if ($objecttype != 'supplier_proposal' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
10997 $element = $regs[1];
10998 $subelement = $regs[2];
10999 $tplpath = $element . '/' . $subelement;
11000 }
11001 $tplname = 'linkedobjectblock';
11002
11003 // If we ask a resource form external module (instead of default path)
11004 if (preg_match('/^([^@]+)@([^@]+)$/i', $objecttype, $regs)) { // 'myobject@mymodule'
11005 $element = $regs[1];
11006 $module = $regs[2];
11007 $tplpath = $module. '/' . $element;
11008 $tplname = $tplname.'_'.$element;
11009 }
11010
11011 // To work with non standard path
11012 if ($objecttype == 'facture') {
11013 $tplpath = 'compta/' . $element;
11014 if (!isModEnabled('invoice')) {
11015 continue; // Do not show if module disabled
11016 }
11017 } elseif ($objecttype == 'facturerec') {
11018 $tplpath = 'compta/facture';
11019 $tplname = 'linkedobjectblockForRec';
11020 if (!isModEnabled('invoice')) {
11021 continue; // Do not show if module disabled
11022 }
11023 } elseif ($objecttype == 'propal') {
11024 $tplpath = 'comm/' . $element;
11025 if (!isModEnabled('propal')) {
11026 continue; // Do not show if module disabled
11027 }
11028 } elseif ($objecttype == 'supplier_proposal') {
11029 if (!isModEnabled('supplier_proposal')) {
11030 continue; // Do not show if module disabled
11031 }
11032 } elseif ($objecttype == 'shipping' || $objecttype == 'shipment' || $objecttype == 'expedition') {
11033 $tplpath = 'expedition';
11034 if (!isModEnabled('shipping')) {
11035 continue; // Do not show if module disabled
11036 }
11037 } elseif ($objecttype == 'reception') {
11038 $tplpath = 'reception';
11039 if (!isModEnabled('reception')) {
11040 continue; // Do not show if module disabled
11041 }
11042 } elseif ($objecttype == 'delivery') {
11043 $tplpath = 'delivery';
11044 if (!getDolGlobalInt('MAIN_SUBMODULE_DELIVERY')) {
11045 continue; // Do not show if sub module disabled
11046 }
11047 } elseif ($objecttype == 'ficheinter') {
11048 $tplpath = 'fichinter';
11049 if (!isModEnabled('intervention')) {
11050 continue; // Do not show if module disabled
11051 }
11052 } elseif ($objecttype == 'invoice_supplier') {
11053 $tplpath = 'fourn/facture';
11054 } elseif ($objecttype == 'order_supplier') {
11055 $tplpath = 'fourn/commande';
11056 } elseif ($objecttype == 'expensereport') {
11057 $tplpath = 'expensereport';
11058 } elseif ($objecttype == 'subscription') {
11059 $tplpath = 'adherents';
11060 } elseif ($objecttype == 'conferenceorbooth') {
11061 $tplpath = 'eventorganization';
11062 } elseif ($objecttype == 'conferenceorboothattendee') {
11063 $tplpath = 'eventorganization';
11064 } elseif ($objecttype == 'mo') {
11065 $tplpath = 'mrp';
11066 if (!isModEnabled('mrp')) {
11067 continue; // Do not show if module disabled
11068 }
11069 } elseif ($objecttype == 'project_task') {
11070 $tplpath = 'projet/tasks';
11071 }
11072
11073 global $linkedObjectBlock; // Will be used into tpl
11074 $linkedObjectBlock = $objects;
11075
11076 // Output template part (modules that overwrite templates must declare this into descriptor)
11077 $dirtpls = array_merge($conf->modules_parts['tpl'], array('/' . $tplpath . '/tpl'));
11078
11079 foreach ($dirtpls as $reldir) {
11080 $reldir = rtrim($reldir, '/');
11081 if ($nboftypesoutput == ($nbofdifferenttypes - 1)) { // No more type to show after
11082 global $noMoreLinkedObjectBlockAfter; // Will be used into tpl
11083 $noMoreLinkedObjectBlockAfter = 1;
11084 }
11085 $file = dol_buildpath($reldir . '/' . $tplname . '.tpl.php');
11086 if (file_exists($file)) {
11087 $res = @include $file;
11088 if ($res) {
11089 $nboftypesoutput++;
11090 break;
11091 }
11092 }
11093 }
11094 }
11095
11096 if (!$nboftypesoutput) {
11097 print '<tr><td colspan="7"><span class="opacitymedium">' . $langs->trans("None") . '</span></td></tr>';
11098 }
11099
11100 print '</table>';
11101
11102 if (!empty($compatibleImportElementsList)) {
11103 $res = @include dol_buildpath('core/tpl/objectlinked_lineimport.tpl.php');
11104 }
11105
11106 print '</div>';
11107 }
11108
11109 return $nbofdifferenttypes;
11110 }
11111
11121 public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array(), $nooutput = 0)
11122 {
11123 global $conf, $langs, $hookmanager, $form;
11124 global $action;
11125
11126 dol_syslog(__METHOD__, LOG_DEBUG);
11127
11128 if (empty($form)) {
11129 $form = new Form($this->db);
11130 }
11131
11132 $linktoelem = '';
11133 $linktoelemlist = '';
11134 $listofidcompanytoscan = '';
11135
11136 if (!is_object($object->thirdparty)) {
11137 if ($object->element == 'subscription' && isset($object->fk_adherent)) {
11138 $subby = new Subscription($object->db);
11139 $subby->fetch($object->id);
11140 $adh = new Adherent($object->db);
11141 //$fk_adherent = $object->fk_adherent;
11142 // creating new subscription object only to fetch the adherent which obviously exists given the if statement above are Inefficient, but else phan complains
11143 $fk_adherent = $subby->fk_adherent;
11144 $adh->fetch($fk_adherent);
11145 $thirdparty_id = $adh->fetch_thirdparty();
11146 }
11147 } else {
11148 $thirdparty_id = $object->thirdparty->id;
11149 }
11150
11151 $possiblelinks = array();
11152
11153 $dontIncludeCompletedItems = getDolGlobalString('DONT_INCLUDE_COMPLETED_ELEMENTS_LINKS');
11154
11155 if (!empty($thirdparty_id) && $thirdparty_id > 0) {
11156 $listofidcompanytoscan = (int) $thirdparty_id;
11157 if (is_object($object->thirdparty) && ($object->thirdparty->parent > 0) && getDolGlobalString('THIRDPARTY_INCLUDE_PARENT_IN_LINKTO')) {
11158 $listofidcompanytoscan .= ',' . (int) $object->thirdparty->parent;
11159 }
11160 if (($object->fk_project > 0) && getDolGlobalString('THIRDPARTY_INCLUDE_PROJECT_THIRDPARY_IN_LINKTO')) {
11161 include_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
11162 $tmpproject = new Project($this->db);
11163 $tmpproject->fetch((int) $object->fk_project);
11164 if ($tmpproject->socid > 0 && ($tmpproject->socid != $thirdparty_id)) {
11165 $listofidcompanytoscan .= ',' . (int) $tmpproject->socid;
11166 }
11167 unset($tmpproject);
11168 }
11169
11170 $possiblelinks = array(
11171 'propal' => array(
11172 'enabled' => isModEnabled('propal'),
11173 'perms' => 1,
11174 'label' => 'LinkToProposal',
11175 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "propal as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('propal') . ')'.($dontIncludeCompletedItems ? ' AND t.fk_statut < 4' : ''),
11176 ),
11177 'shipping' => array(
11178 'enabled' => isModEnabled('shipping'),
11179 'perms' => 1,
11180 'label' => 'LinkToExpedition',
11181 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "expedition as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('shipping') . ')'.($dontIncludeCompletedItems ? ' AND t.fk_statut < 2' : ''),
11182 ),
11183 'order' => array(
11184 'enabled' => isModEnabled('order'),
11185 'perms' => 1,
11186 'label' => 'LinkToOrder',
11187 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "commande as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('commande') . ')'.($dontIncludeCompletedItems ? ' AND t.facture < 1' : ''),
11188 'linkname' => 'commande',
11189 ),
11190 'subscription' => array(
11191 'enabled' => isModEnabled('member'),
11192 'perms' => 1,
11193 'label' => 'LinkToMemberSubscription',
11194 'sql' => "SELECT a.fk_soc as socid, CONCAT(a.firstname, ' ', a.lastname) as name, a.entity as client, sub.rowid, sub.note as ref, '' as ref_client, sub.subscription as total_ht FROM " . $this->db->prefix() . "adherent as a, " . $this->db->prefix() . "subscription as sub WHERE sub.fk_adherent = a.rowid AND a.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND a.entity IN (' . getEntity('subscription') . ')',
11195 'linkname' => 'subscription',
11196 ),
11197 'conferenceorboothattendee' => array(
11198 'enabled' => isModEnabled('eventorganization'),
11199 'perms' => 1,
11200 'label' => 'LinkToConferenceOrBoothAttendee',
11201 'sql' => "SELECT s.rowid as socid, CONCAT(a.firstname, ' ', a.lastname) as name, a.rowid as rowid, a.fk_project as fk_project, a.ref as ref, a.email as email, a.date_subscription as date_subscription FROM "
11202 .$this->db->prefix()."societe as s, "
11203 .$this->db->prefix()."eventorganization_conferenceorboothattendee as a WHERE a.fk_soc = s.rowid AND a.fk_soc IN ("
11204 .$this->db->sanitize($listofidcompanytoscan) . ') AND s.entity IN (' . getEntity('conferenceorboothattendee') . ')'
11205 . (empty($object->fk_project) ? '' : ' AND a.fk_project = ' . (int) $object->fk_project),
11206 'linkname' => 'attendee'
11207 ),
11208 'invoice' => array(
11209 'enabled' => isModEnabled('invoice'),
11210 'perms' => 1,
11211 'label' => 'LinkToInvoice',
11212 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_client, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "facture as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('invoice') . ')'.($dontIncludeCompletedItems ? ' AND t.paye < 1' : ''),
11213 'linkname' => 'facture',
11214 ),
11215 'invoice_template' => array(
11216 'enabled' => isModEnabled('invoice'),
11217 'perms' => 1,
11218 'label' => 'LinkToTemplateInvoice',
11219 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.titre as ref, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "facture_rec as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('invoice') . ')',
11220 ),
11221 'contrat' => array(
11222 'enabled' => isModEnabled('contract'),
11223 'perms' => 1,
11224 'label' => 'LinkToContract',
11225 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_customer as ref_client, t.ref_supplier, SUM(td.total_ht) as total_ht
11226 FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "contrat as t, " . $this->db->prefix() . "contratdet as td WHERE t.fk_soc = s.rowid AND td.fk_contrat = t.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('contract') . ') GROUP BY s.rowid, s.nom, s.client, t.rowid, t.ref, t.ref_customer, t.ref_supplier',
11227 ),
11228 'fichinter' => array(
11229 'enabled' => isModEnabled('intervention'),
11230 'perms' => 1,
11231 'label' => 'LinkToIntervention',
11232 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "fichinter as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('intervention') . ')',
11233 ),
11234 'supplier_proposal' => array(
11235 'enabled' => isModEnabled('supplier_proposal'),
11236 'perms' => 1,
11237 'label' => 'LinkToSupplierProposal',
11238 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, '' as ref_supplier, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "supplier_proposal as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('supplier_proposal') . ')'.($dontIncludeCompletedItems ? ' AND t.fk_statut < 4' : ''),
11239 ),
11240 'order_supplier' => array(
11241 'enabled' => isModEnabled("supplier_order"),
11242 'perms' => 1,
11243 'label' => 'LinkToSupplierOrder',
11244 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "commande_fournisseur as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('commande_fournisseur') . ')'.($dontIncludeCompletedItems ? ' AND t.billed < 1' : ''),
11245 ),
11246 'invoice_supplier' => array(
11247 'enabled' => isModEnabled("supplier_invoice"),
11248 'perms' => 1, 'label' => 'LinkToSupplierInvoice',
11249 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "facture_fourn as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('facture_fourn') . ')'.($dontIncludeCompletedItems ? ' AND t.paye < 1' : ''),
11250 ),
11251 'ticket' => array(
11252 'enabled' => isModEnabled('ticket'),
11253 'perms' => 1,
11254 'label' => 'LinkToTicket',
11255 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.track_id, '0' as total_ht FROM " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "ticket as t WHERE t.fk_soc = s.rowid AND t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('ticket') . ')'.($dontIncludeCompletedItems ? ' AND t.fk_statut < 8' : ''),
11256 ),
11257 'mo' => array(
11258 'enabled' => isModEnabled('mrp'),
11259 'perms' => 1,
11260 'label' => 'LinkToMo',
11261 'sql' => "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.rowid, '0' as total_ht FROM " . $this->db->prefix() . "societe as s INNER JOIN " . $this->db->prefix() . "mrp_mo as t ON t.fk_soc = s.rowid WHERE t.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND t.entity IN (' . getEntity('mo') . ')'.($dontIncludeCompletedItems ? ' AND t.status < 3' : ''),
11262 ),
11263 );
11264 }
11265
11266 if ($object->table_element == 'commande_fournisseur') {
11267 $possiblelinks['mo']['sql'] = "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.rowid, '0' as total_ht FROM ".$this->db->prefix()."societe as s INNER JOIN ".$this->db->prefix().'mrp_mo as t ON t.fk_soc = s.rowid WHERE t.entity IN ('.getEntity('mo').')'.($dontIncludeCompletedItems ? ' AND t.status < 3' : '');
11268 } elseif ($object->table_element == 'mrp_mo') {
11269 $possiblelinks['order_supplier']['sql'] = "SELECT s.rowid as socid, s.nom as name, s.client, t.rowid, t.ref, t.ref_supplier, t.total_ht FROM ".$this->db->prefix()."societe as s, ".$this->db->prefix().'commande_fournisseur as t WHERE t.fk_soc = s.rowid AND t.entity IN ('.getEntity('commande_fournisseur').')'.($dontIncludeCompletedItems ? ' AND t.billed < 1' : '');
11270 }
11271
11272 $reshook = 0; // Ensure $reshook is defined for static analysis
11273 if (!empty($listofidcompanytoscan)) { // If empty, we don't have criteria to scan the object we can link to
11274 // Can complete the possiblelink array
11275 $hookmanager->initHooks(array('commonobject'));
11276 $parameters = array('listofidcompanytoscan' => $listofidcompanytoscan, 'possiblelinks' => $possiblelinks);
11277 $reshook = $hookmanager->executeHooks('showLinkToObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
11278 }
11279
11280 if (empty($reshook)) {
11281 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
11282 $possiblelinks = array_merge($possiblelinks, $hookmanager->resArray);
11283 }
11284 } elseif ($reshook > 0) {
11285 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
11286 $possiblelinks = $hookmanager->resArray;
11287 }
11288 }
11289
11290 if (!empty($possiblelinks)) {
11291 $object->fetchObjectLinked();
11292 }
11293
11294 // Build the html part with possible suggested links
11295 $htmltoenteralink = '';
11296 foreach ($possiblelinks as $key => $possiblelink) {
11297 $num = 0;
11298 if (empty($possiblelink['enabled'])) {
11299 continue;
11300 }
11301
11302
11303 // If we ask a resource form external module (instead of default path)
11304 $module = '';
11305 if (preg_match('/^([^@]+)@([^@]+)$/i', $key, $regs)) { // 'myobject@mymodule'
11306 $key = $regs[1];
11307 $module = $regs[2];
11308 }
11309
11310 if (!empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || !in_array($key, $excludelinksto))) {
11311 $htmltoenteralink .= '<div id="' . $key . 'list"' . (empty($conf->use_javascript_ajax) ? '' : ' style="display:none"') . '>';
11312
11313 // Section for free ref input
11314 if (!getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11315 $htmltoenteralink .= '<br>'."\n";
11316 $htmltoenteralink .= '<!-- form to add a link from anywhere -->'."\n";
11317 $htmltoenteralink .= '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinkedbyref' . $key . '">';
11318 $htmltoenteralink .= '<input type="hidden" name="token" value="' . newToken() . '">';
11319 $htmltoenteralink .= '<input type="hidden" name="action" value="addlinkbyref">';
11320 $htmltoenteralink .= '<input type="hidden" name="id" value="' . $object->id . '">';
11321 $htmltoenteralink .= '<input type="hidden" name="addlink" value="' . $key .(!empty($module) ? '@'.$module : ''). '">';
11322 $htmltoenteralink .= '<table class="noborder">';
11323 $htmltoenteralink .= '<tr class="liste_titre">';
11324 //print '<td>' . $langs->trans("Ref") . '</td>';
11325 $htmltoenteralink .= '<td class="center"><input type="text" placeholder="'.dol_escape_htmltag($langs->trans("Ref")).'" name="reftolinkto" value="' . dol_escape_htmltag(GETPOST('reftolinkto', 'alpha')) . '">';
11326 $htmltoenteralink .= '<br>';
11327 $htmltoenteralink .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans('ToLink') . '">&nbsp;';
11328 $htmltoenteralink .= '<input type="submit" class="button smallpaddingimp" name="cancel" value="' . $langs->trans('Cancel') . '">';
11329 $htmltoenteralink .= '</td>';
11330 $htmltoenteralink .= '</tr>';
11331 $htmltoenteralink .= '</table>';
11332 $htmltoenteralink .= '</form>';
11333 }
11334
11335 $sql = $possiblelink['sql'];
11336
11337 $resqllist = $this->db->query($sql);
11338 if ($resqllist) {
11339 $num = $this->db->num_rows($resqllist);
11340
11341 if ($num > 0) {
11342 // Section for free predefined list
11343 if (getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11344 $htmltoenteralink .= '<br>';
11345 }
11346 $htmltoenteralink .= '<!-- form to add a link from object to same thirdparty -->'."\n";
11347 $htmltoenteralink .= '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinked' . $key . '">';
11348 $htmltoenteralink .= '<input type="hidden" name="token" value="' . newToken() . '">';
11349 $htmltoenteralink .= '<input type="hidden" name="action" value="addlink">';
11350 $htmltoenteralink .= '<input type="hidden" name="id" value="' . $object->id . '">';
11351 $htmltoenteralink .= '<input type="hidden" name="addlink" value="' . $key . (!empty($module) ? '@'.$module : ''). '">';
11352 $htmltoenteralink .= '<table class="noborder">';
11353
11354 switch ($key) {
11355 case 'conferenceorboothattendee':
11356 // Custom logic for linking to attendees
11357 $htmltoenteralink .= $this->makeAddLinkToAttendee($object, $key, $possiblelink, $num, $resqllist);
11358 break;
11359
11360 default:
11361 // Standard logic for all other object types
11362 $htmltoenteralink .= $this->makeAddLinkToObject($object, $key, $possiblelink, $num, $resqllist);
11363 break;
11364 }
11365
11366 $htmltoenteralink .= '</table>';
11367 $htmltoenteralink .= '<div class="center">';
11368 if ($num) {
11369 $htmltoenteralink .= '<input type="submit" class="button valignmiddle marginleftonly marginrightonly smallpaddingimp" value="' . $langs->trans('ToLink') . '">';
11370 }
11371 if (empty($conf->use_javascript_ajax)) {
11372 $htmltoenteralink .= '<input type="submit" class="button button-cancel marginleftonly marginrightonly smallpaddingimp" name="cancel" value="' . $langs->trans("Cancel") . '"></div>';
11373 } else {
11374 $htmltoenteralink .= '<input type="submit" onclick="jQuery(\'#' . $key . 'list\').toggle(); return false;" class="button button-cancel marginleftonly marginrightonly smallpaddingimp" name="cancel" value="' . $langs->trans("Cancel") . '"></div>';
11375 }
11376 $htmltoenteralink .= '</form>';
11377 }
11378
11379 $this->db->free($resqllist);
11380 } else {
11381 dol_print_error($this->db);
11382 }
11383 $htmltoenteralink .= '</div>';
11384
11385
11386 // Complete the list for the combo box
11387 if ($num > 0 || !getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11388 $linktoelemlist .= '<li><a href="#linkto' . $key . '" class="linkto dropdowncloseonclick" rel="' . $key . '">' . $langs->trans($possiblelink['label']) . ' (' . $num . ')</a></li>';
11389 // } else $linktoelem.=$langs->trans($possiblelink['label']);
11390 } else {
11391 $linktoelemlist .= '<li><span class="linktodisabled">' . $langs->trans($possiblelink['label']) . ' (0)</span></li>';
11392 }
11393 }
11394 }
11395
11396 if ($linktoelemlist) {
11397 $linktoelem = '
11398 <dl class="dropdown" id="linktoobjectname">
11399 ';
11400 if (!empty($conf->use_javascript_ajax)) {
11401 $linktoelem .= '<dt><a href="#linktoobjectname"><span class="fas fa-link paddingrightonly"></span>' . $langs->trans("LinkTo") . '...</a></dt>';
11402 }
11403 $linktoelem .= '<dd>
11404 <div class="multiselectlinkto">
11405 <ul class="ulselectedfields">' . $linktoelemlist . '
11406 </ul>
11407 </div>
11408 </dd>
11409 </dl>';
11410 } else {
11411 $linktoelem = '';
11412 }
11413
11414 if (!empty($conf->use_javascript_ajax)) {
11415 print '<!-- Add js to show linkto box -->
11416 <script nonce="' . getNonce() . '">
11417 jQuery(document).ready(function() {
11418 jQuery(".linkto").click(function() {
11419 console.log("We choose to show/hide links for rel="+jQuery(this).attr(\'rel\')+" so #"+jQuery(this).attr(\'rel\')+"list");
11420 jQuery("#"+jQuery(this).attr(\'rel\')+"list").toggle();
11421 });
11422 });
11423 </script>
11424 ';
11425 }
11426
11427 if ($nooutput) {
11428 return array('linktoelem' => $linktoelem, 'htmltoenteralink' => $htmltoenteralink);
11429 } else {
11430 print $htmltoenteralink;
11431 }
11432
11433 return $linktoelem;
11434 }
11435
11450 public function selectyesno($htmlname, $value = '', $option = 0, $disabled = false, $useempty = 0, $addjscombo = 0, $morecss = 'yesno width75', $labelyes = 'Yes', $labelno = 'No')
11451 {
11452 global $langs;
11453
11454 $yes = "yes";
11455 $no = "no";
11456 if ($option) {
11457 $yes = "1";
11458 $no = "0";
11459 }
11460
11461 $disabled = ($disabled ? ' disabled' : '');
11462
11463 $resultyesno = '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '"' . $disabled . '>' . "\n";
11464 if ($useempty) {
11465 $resultyesno .= '<option value="-1"' . (($value < 0) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
11466 }
11467 if (("$value" == 'yes') || ($value == 1)) {
11468 $resultyesno .= '<option value="' . $yes . '" selected>' . $langs->trans($labelyes) . '</option>' . "\n";
11469 $resultyesno .= '<option value="' . $no . '">' . $langs->trans($labelno) . '</option>' . "\n";
11470 } else {
11471 $selected = (($useempty && $value != '0' && $value != 'no') ? '' : ' selected');
11472 $resultyesno .= '<option value="' . $yes . '">' . $langs->trans($labelyes) . '</option>' . "\n";
11473 $resultyesno .= '<option value="' . $no . '"' . $selected . '>' . $langs->trans($labelno) . '</option>' . "\n";
11474 }
11475 $resultyesno .= '</select>' . "\n";
11476
11477 if ($addjscombo) {
11478 $resultyesno .= ajax_combobox($htmlname, array(), 0, 0, 'resolve', ($useempty < 0 ? (string) $useempty : '-1'), $morecss);
11479 }
11480
11481 return $resultyesno;
11482 }
11483
11484 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
11485
11495 public function select_export_model($selected = '', $htmlname = 'exportmodelid', $type = '', $useempty = 0)
11496 {
11497 // phpcs:enable
11498 $sql = "SELECT rowid, label";
11499 $sql .= " FROM " . $this->db->prefix() . "export_model";
11500 $sql .= " WHERE type = '" . $this->db->escape($type) . "'";
11501 $sql .= " ORDER BY rowid";
11502 $result = $this->db->query($sql);
11503 if ($result) {
11504 print '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
11505 if ($useempty) {
11506 print '<option value="-1">&nbsp;</option>';
11507 }
11508
11509 $num = $this->db->num_rows($result);
11510 $i = 0;
11511 while ($i < $num) {
11512 $obj = $this->db->fetch_object($result);
11513 if ($selected == $obj->rowid) {
11514 print '<option value="' . $obj->rowid . '" selected>';
11515 } else {
11516 print '<option value="' . $obj->rowid . '">';
11517 }
11518 print $obj->label;
11519 print '</option>';
11520 $i++;
11521 }
11522 print "</select>";
11523 } else {
11524 dol_print_error($this->db);
11525 }
11526 }
11527
11546 public function showrefnav($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $morehtmlright = '')
11547 {
11548 global $conf, $langs, $hookmanager, $extralanguages;
11549
11550 $ret = '';
11551 if (empty($fieldid)) {
11552 $fieldid = 'rowid';
11553 }
11554 if (empty($fieldref)) {
11555 $fieldref = 'ref';
11556 }
11557
11558 // Preparing gender's display if there is one
11559 $addgendertxt = '';
11560 if (property_exists($object, 'gender') && !empty($object->gender)) {
11561 $addgendertxt = ' ';
11562 switch ($object->gender) {
11563 case 'man':
11564 $addgendertxt .= '<i class="fas fa-mars valignmiddle"></i>';
11565 break;
11566 case 'woman':
11567 $addgendertxt .= '<i class="fas fa-venus valignmiddle"></i>';
11568 break;
11569 case 'other':
11570 $addgendertxt .= '<i class="fas fa-transgender valignmiddle"></i>';
11571 break;
11572 }
11573 }
11574
11575 // Add where from hooks
11576 if (is_object($hookmanager)) {
11577 $parameters = array('showrefnav' => true);
11578 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
11579 if (!empty($hookmanager->resPrint)) {
11580 if (empty($object->next_prev_filter) && preg_match('/^\s*AND/i', $hookmanager->resPrint)) {
11581 $object->next_prev_filter = (string) preg_replace('/^\s*AND\s*/i', '', $hookmanager->resPrint);
11582 } elseif (!empty($object->next_prev_filter) && !preg_match('/^\s*AND/i', $hookmanager->resPrint)) {
11583 $object->next_prev_filter .= ' AND '.$hookmanager->resPrint;
11584 } else {
11585 $object->next_prev_filter .= $hookmanager->resPrint;
11586 }
11587 }
11588 }
11589
11590 $previous_ref = $next_ref = '';
11591 if ($shownav) {
11592 //print "paramid=$paramid,morehtml=$morehtml,shownav=$shownav,fieldid=$fieldid,filedref=$fieldref,morehtmlref=$morehtmlref,moreparam=$moreparam";
11593 $object->load_previous_next_ref((isset($object->next_prev_filter) ? $object->next_prev_filter : ''), $fieldid, $nodbprefix);
11594
11595 $navurl = $_SERVER["PHP_SELF"];
11596
11597 // Special case for token card
11598 if ($paramid == 'api_token_card') {
11599 if (preg_match('/\/user\/api_token/', $navurl)) {
11600 $navurl = preg_replace('/card/', 'list', $navurl);
11601 $paramid = 'id';
11602 }
11603 }
11604
11605 // Special case for project/task page
11606 if ($paramid == 'project_ref') {
11607 if (preg_match('/\/tasks\/(task|contact|note|document)\.php/', $navurl)) { // TODO Remove this when nav with project_ref on task pages are ok
11608 $navurl = preg_replace('/\/tasks\/(task|contact|time|note|document)\.php/', '/tasks.php', $navurl);
11609 $paramid = 'ref';
11610 }
11611 }
11612
11613 $previous_ref = $object->ref_previous ? '<a accesskey="p" alt="'.dol_escape_htmltag($langs->trans("Previous")).'" title="' . $conf->browser->stringforfirstkey . ' p" class="classfortooltip reposition" href="' . $navurl . '?' . $paramid . '=' . urlencode($object->ref_previous) . $moreparam . '"><i class="fa fa-chevron-left"></i></a>' : '<span class="inactive"><i class="fa fa-chevron-left opacitymedium"></i></span>';
11614 $next_ref = $object->ref_next ? '<a accesskey="n" alt="'.dol_escape_htmltag($langs->trans("Next")).'" title="' . $conf->browser->stringforfirstkey . ' n" class="classfortooltip reposition" href="' . $navurl . '?' . $paramid . '=' . urlencode($object->ref_next) . $moreparam . '"><i class="fa fa-chevron-right"></i></a>' : '<span class="inactive"><i class="fa fa-chevron-right opacitymedium"></i></span>';
11615 }
11616
11617 //print "xx".$previous_ref."x".$next_ref;
11618 $ret .= '<!-- Start banner content --><div style="vertical-align: middle">';
11619
11620 // Right part of banner
11621 if ($morehtmlright) {
11622 $ret .= '<div class="inline-block floatleft">' . $morehtmlright . '</div>';
11623 }
11624
11625 if ($previous_ref || $next_ref || $morehtml) {
11626 $ret .= '<div class="pagination paginationref"><ul class="right">';
11627 }
11628 if ($morehtml && getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
11629 $ret .= '<!-- morehtml --><li class="noborder litext' . (($shownav && $previous_ref && $next_ref) ? ' clearbothonsmartphone' : '') . '">' . $morehtml . '</li>';
11630 }
11631 if ($shownav && ($previous_ref || $next_ref)) {
11632 $ret .= '<li class="pagination">' . $previous_ref . '</li>';
11633 $ret .= '<li class="pagination">' . $next_ref . '</li>';
11634 }
11635 if ($previous_ref || $next_ref || $morehtml) {
11636 $ret .= '</ul></div>';
11637 }
11638
11639 // Status
11640 $parameters = array('morehtmlstatus' => $morehtmlstatus);
11641 $reshook = $hookmanager->executeHooks('moreHtmlStatus', $parameters, $object); // Note that $action and $object may have been modified by hook
11642 if (empty($reshook)) {
11643 $morehtmlstatus .= $hookmanager->resPrint;
11644 } else {
11645 $morehtmlstatus = $hookmanager->resPrint;
11646 }
11647 if ($morehtmlstatus) {
11648 $ret .= '<!-- status --><div class="statusref">' . $morehtmlstatus . '</div>';
11649 }
11650
11651 $parameters = array();
11652 $reshook = $hookmanager->executeHooks('moreHtmlRef', $parameters, $object); // Note that $action and $object may have been modified by hook
11653 if (empty($reshook)) {
11654 $morehtmlref .= $hookmanager->resPrint;
11655 } elseif ($reshook > 0) {
11656 $morehtmlref = $hookmanager->resPrint;
11657 }
11658
11659 // Left part of banner
11660 if ($morehtmlleft) {
11661 if ($conf->browser->layout == 'phone') {
11662 $ret .= '<!-- morehtmlleft --><div class="floatleft">' . $morehtmlleft . '</div>';
11663 } else {
11664 $ret .= '<!-- morehtmlleft --><div class="inline-block floatleft">' . $morehtmlleft . '</div>';
11665 }
11666 }
11667
11668 //if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
11669 $ret .= '<!-- Ref or ID --><div class="inline-block floatleft valignmiddle maxwidth750 marginbottomonly refid' . (($shownav && ($previous_ref || $next_ref)) ? ' refidpadding' : '') . '">';
11670
11671 // For thirdparty, contact, user, member, the ref is the id, so we show something else
11672 if ($object->element == 'societe') {
11673 $ret .= '<span class="valignmiddle">'.dolPrintHTML((string) $object->name).'</span>';
11674
11675 // List of extra languages
11676 $arrayoflangcode = array();
11677 if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
11678 $arrayoflangcode[] = getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE');
11679 }
11680
11681 if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
11682 if (!is_object($extralanguages)) {
11683 include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
11684 $extralanguages = new ExtraLanguages($this->db);
11685 }
11686 $extralanguages->fetch_name_extralanguages('societe');
11687
11688 // Guard against PHP 8 'Undefined array key' when MAIN_USE_ALTERNATE_TRANSLATION_FOR
11689 // is not configured and fetch_name_extralanguages() leaves attributes empty (issue #34596).
11690 if (!empty($extralanguages->attributes['societe']) && !empty($extralanguages->attributes['societe']['name'])) {
11691 $object->fetchValuesForExtraLanguages();
11692
11693 $htmltext = '';
11694 // If there is extra languages
11695 foreach ($arrayoflangcode as $extralangcode) {
11696 $htmltext .= picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
11697 if ($object->array_languages['name'][$extralangcode]) {
11698 $htmltext .= $object->array_languages['name'][$extralangcode];
11699 } else {
11700 $htmltext .= '<span class="opacitymedium">' . $langs->trans("SwitchInEditModeToAddTranslation") . '</span>';
11701 }
11702 }
11703 $ret .= '<!-- Show translations of name -->' . "\n";
11704 $ret .= $this->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
11705 }
11706 }
11707 } elseif ($object->element == 'member') {
11708 '@phan-var-force Adherent $object';
11709 $ret .= $object->ref . '<br>';
11710 $fullname = $object->getFullName($langs);
11711 if ($object->morphy == 'mor' && $object->societe) {
11712 $ret .= '<span class="valignmiddle">'.dolPrintHTML((string) $object->societe) . ((!empty($fullname) && $object->societe != $fullname) ? ' (' . dol_htmlentities($fullname) . $addgendertxt . ')' : '').'</span>';
11713 } else {
11714 $ret .= '<span class="valignmiddle">'.dolPrintHTML($fullname) . $addgendertxt . ((!empty($object->societe) && $object->societe != $fullname) ? ' (' . dol_htmlentities((string) $object->societe) . ')' : '').'</span>';
11715 }
11716 } elseif (in_array($object->element, array('contact', 'user'))) {
11717 $ret .= '<span class="valignmiddle">'.dolPrintHTML($object->getFullName($langs)).'</span>'.$addgendertxt;
11718 } elseif ($object->element == 'usergroup') {
11719 $ret .= dol_htmlentities((string) $object->name);
11720 } elseif (in_array($object->element, array('action', 'agenda'))) {
11721 '@phan-var-force ActionComm $object';
11722 $ret .= $object->ref . '<br>' . $object->label;
11723 } elseif (in_array($object->element, array('adherent_type'))) {
11724 $ret .= $object->label;
11725 } elseif ($object->element == 'ecm_directories') {
11726 $ret .= '';
11727 } elseif ($object->element == 'accountingbookkeeping' && !empty($object->context['mode']) && $object->context['mode'] == '_tmp') {
11728 $ret .= '<span class="valignmiddle">'.$langs->trans("Draft").'</span>';
11729 } elseif ($object instanceof Ticket) {
11730 '@phan-var-force Ticket $object';
11731 $ret .= '<span class="valignmiddle">'.dolPrintHTML(!empty($object->$fieldref) ? $object->$fieldref : "").'</span>';
11732 $ret .= ' &nbsp; <span class="nobold small" title="'.dolPrintHTMLForAttribute($langs->trans("TicketTrackId")).'">('.$object->track_id.')</span>';
11733 } elseif ($fieldref != 'none') {
11734 // Generic case
11735 $ret .= '<span class="valignmiddle">'.dolPrintHTML(!empty($object->$fieldref) ? $object->$fieldref : "").'</span>';
11736 }
11737 if ($morehtmlref) {
11738 // don't add a additional space, when "$morehtmlref" starts with a HTML div tag
11739 if (substr($morehtmlref, 0, 4) != '<div') {
11740 $ret .= ' ';
11741 }
11742
11743 $ret .= '<!-- morehtmlref -->'.$morehtmlref;
11744 }
11745
11746 $ret .= '</div>';
11747
11748 $ret .= '</div><!-- End banner content -->';
11749
11750 return $ret;
11751 }
11752
11753
11762 public function showbarcode(&$object, $width = 100, $morecss = '')
11763 {
11764 //Check if barcode is filled in the card
11765 if (empty($object->barcode)) {
11766 return '';
11767 }
11768
11769 // Complete object if not complete
11770 if (empty($object->barcode_type_code) || empty($object->barcode_type_coder)) {
11771 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11772 $result = $object->fetchBarCode();
11773 //Check if fetchBarCode() failed
11774 if ($result < 1) {
11775 return '<!-- ErrorFetchBarcode -->';
11776 }
11777 }
11778
11779 // Barcode image @phan-suppress-next-line PhanUndeclaredProperty
11780 $url = DOL_URL_ROOT . '/viewimage.php?modulepart=barcode&generator=' . urlencode($object->barcode_type_coder) . '&code=' . urlencode($object->barcode) . '&encoding=' . urlencode($object->barcode_type_code);
11781 $out = '<!-- url barcode = ' . $url . ' -->';
11782 $out .= '<img src="' . $url . '"' . ($morecss ? ' class="' . $morecss . '"' : '') . '>';
11783
11784 return $out;
11785 }
11786
11805 public static function showphoto($modulepart, $object, $width = 100, $height = 0, $caneditfield = 0, $cssclass = 'photowithmargin', $imagesize = '', $addlinktofullsize = 1, $cache = 0, $forcecapture = '', $noexternsourceoverwrite = 0, $usesharelinkifavailable = 0)
11806 {
11807 global $conf, $db, $langs;
11808
11809 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
11810 $id = (empty($object->id) ? $object->rowid : $object->id); // @phan-suppress-current-line PhanUndeclaredProperty (->rowid)
11811
11812 $dir = '';
11813 $file = '';
11814 $originalfile = '';
11815 $altfile = '';
11816 $email = '';
11817 $capture = '';
11818 if ($modulepart == 'societe') {
11819 $dir = $conf->societe->multidir_output[$entity];
11820 if (!empty($object->logo)) {
11821 if (dolIsAllowedForPreview($object->logo)) {
11822 if ((string) $imagesize == 'mini') {
11823 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_mini'); // getImageFileNameForSize include the thumbs
11824 } elseif ((string) $imagesize == 'small') {
11825 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_small');
11826 } else {
11827 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
11828 }
11829 $originalfile = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
11830 }
11831 }
11832 $email = $object->email;
11833 } elseif ($modulepart == 'contact') {
11834 $dir = $conf->societe->multidir_output[$entity] . '/contact';
11835 $photo = $object->photo; // Copy to help static analysis
11836 if (!empty($photo)) {
11837 if (dolIsAllowedForPreview($photo)) {
11838 if ((string) $imagesize == 'mini') {
11839 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11840 } elseif ((string) $imagesize == 'small') {
11841 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($photo, '_small');
11842 } else {
11843 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $photo;
11844 }
11845 $originalfile = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $photo;
11846 }
11847 }
11848 $email = $object->email;
11849 $capture = 'user';
11850 } elseif ($modulepart == 'userphoto') {
11851 $dir = $conf->user->dir_output;
11852 $photo = $object->photo; // Copy to help static analysis
11853 if (!empty($photo)) {
11854 if (dolIsAllowedForPreview($photo)) {
11855 if ((string) $imagesize == 'mini') {
11856 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11857 } elseif ((string) $imagesize == 'small') {
11858 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($photo, '_small');
11859 } else {
11860 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $photo;
11861 }
11862 $originalfile = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $photo;
11863 }
11864 }
11865 if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {
11866 $altfile = $object->id . ".jpg"; // For backward compatibility
11867 }
11868 $email = $object->email;
11869 $capture = 'user';
11870 } elseif ($modulepart == 'memberphoto') {
11871 $dir = $conf->member->dir_output;
11872 $photo = $object->photo; // Copy to help static analysis
11873 if (!empty($photo)) {
11874 if (dolIsAllowedForPreview($photo)) {
11875 if ((string) $imagesize == 'mini') {
11876 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11877 } elseif ((string) $imagesize == 'small') {
11878 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($photo, '_small');
11879 } else {
11880 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $photo;
11881 }
11882 $originalfile = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $photo;
11883 }
11884 }
11885 if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {
11886 $altfile = $object->id . ".jpg"; // For backward compatibility
11887 }
11888 $email = $object->email;
11889 $capture = 'user';
11890 } else {
11891 // Generic case to show photos
11892 // TODO Implement this method in previous objects so we can always use this generic method.
11893 if ($modulepart != "unknown" && method_exists($object, 'getDataToShowPhoto')) {
11894 $tmpdata = $object->getDataToShowPhoto($modulepart, $imagesize);
11895
11896 $dir = $tmpdata['dir'];
11897 $file = $tmpdata['file'];
11898 $originalfile = $tmpdata['originalfile'];
11899 $altfile = $tmpdata['altfile'];
11900 $email = $tmpdata['email'];
11901 $capture = $tmpdata['capture'];
11902 }
11903 }
11904
11905 if ($forcecapture) {
11906 $capture = $forcecapture;
11907 }
11908
11909 $ret = '';
11910
11911 if ($dir) {
11912 if ($file && file_exists($dir . "/" . $file)) {
11913 if ($addlinktofullsize) {
11914 $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
11915 if ($urladvanced) {
11916 $ret .= '<a href="' . $urladvanced . '">';
11917 } else {
11918 $ret .= '<a href="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
11919 }
11920 }
11921
11922 $sharekey = '';
11923 if ($usesharelinkifavailable) {
11924 // $dir is a full path '/home/.../dolibarr_documents/module'
11925 $relativefileforecm = preg_replace('/^'.preg_quote(DOL_DATA_ROOT.'/', '/').'/', '', $dir.'/'.$originalfile);
11926 // $relativefileforecme = 'module/...'
11927 require_once DOL_DOCUMENT_ROOT . '/ecm/class/ecmfiles.class.php';
11928 $ecmfiles = new EcmFiles($db);
11929 $ecmfiles->fetch(0, '', $relativefileforecm);
11930
11931 $sharekey = (string) $ecmfiles->share;
11932 }
11933
11934 if (!empty($sharekey)) {
11935 $ret .= '<img alt="" class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . ' photologo' . (preg_replace('/[^a-z]/i', '_', $file)) . '" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . DOL_URL_ROOT . '/viewimage.php?hashp=' . urlencode($sharekey) . '&cache=' . urlencode((string) $cache) . '">';
11936 } else {
11937 $ret .= '<img alt="" class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . ' photologo' . (preg_replace('/[^a-z]/i', '_', $file)) . '" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . urlencode($modulepart) . '&entity=' . ((int) $entity) . '&file=' . urlencode($file) . '&cache=' . urlencode((string) $cache) . '">';
11938 }
11939 if ($addlinktofullsize) {
11940 $ret .= '</a>';
11941 }
11942 } elseif ($altfile && file_exists($dir . "/" . $altfile)) {
11943 if ($addlinktofullsize) {
11944 $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
11945 if ($urladvanced) {
11946 $ret .= '<a href="' . $urladvanced . '">';
11947 } else {
11948 $ret .= '<a href="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
11949 }
11950 }
11951 $ret .= '<img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="Photo alt" id="photologo' . (preg_replace('/[^a-z]/i', '_', $file)) . '" class="' . $cssclass . '" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . urlencode($modulepart) . '&entity=' . ((int) $entity) . '&file=' . urlencode($altfile) . '&cache=' . urlencode((string) $cache) . '">';
11952 if ($addlinktofullsize) {
11953 $ret .= '</a>';
11954 }
11955 } else {
11956 $nophoto = '/public/theme/common/nophoto.png';
11957 $defaultimg = 'identicon'; // For gravatar
11958 if (in_array($modulepart, array('societe', 'userphoto', 'contact', 'memberphoto'))) { // For modules that need a special image when photo not found
11959 if ($modulepart == 'societe' || ($modulepart == 'memberphoto' && !empty($object->morphy) && strpos($object->morphy, 'mor') !== false)) {
11960 $nophoto = 'company';
11961 } else {
11962 $nophoto = '/public/theme/common/user_anonymous.png';
11963 if (!empty($object->gender) && $object->gender == 'man') {
11964 $nophoto = '/public/theme/common/user_man.png';
11965 }
11966 if (!empty($object->gender) && $object->gender == 'woman') {
11967 $nophoto = '/public/theme/common/user_woman.png';
11968 }
11969 }
11970 }
11971
11972 if (isModEnabled('gravatar') && $email && empty($noexternsourceoverwrite)) {
11973 // see https://gravatar.com/site/implement/images/php/
11974 $ret .= '<!-- Put link to gravatar -->';
11975 $ret .= '<img class="gravatar photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" title="'.dolPrintHTMLForAttribute('Gravatar avatar - '.$email).'" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="https://www.gravatar.com/avatar/' . dol_hash(strtolower(trim($email)), 'sha256', 1) . '?s=' . $width . '&d=' . $defaultimg . '">'; // gravatar need md5 hash
11976 } else {
11977 if ($nophoto == 'company') {
11978 $ret .= '<div class="divforspanimg valignmiddle inline-block center photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . '>' . img_picto('', 'company') . '</div>';
11979 //$ret .= '<div class="difforspanimgright"></div>';
11980 } else {
11981 $ret .= '<img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . DOL_URL_ROOT . $nophoto . '">';
11982 }
11983 }
11984 }
11985
11986 if ($caneditfield) {
11987 if ($object->photo) {
11988 $ret .= "<br>\n";
11989 }
11990 $ret .= '<table class="nobordernopadding centpercent">';
11991 if ($object->photo) {
11992 $ret .= '<tr><td><input type="checkbox" class="flat photodelete" name="deletephoto" id="photodelete"> <label for="photodelete">' . $langs->trans("Delete") . '</label><br><br></td></tr>';
11993 }
11994 $ret .= '<tr><td class="tdoverflow">';
11995 $maxfilesizearray = getMaxFileSizeArray();
11996 $maxmin = $maxfilesizearray['maxmin'];
11997 if ($maxmin > 0) {
11998 $ret .= '<input type="hidden" name="MAX_FILE_SIZE" value="' . ($maxmin * 1024) . '">'; // MAX_FILE_SIZE must precede the field type=file
11999 }
12000 $ret .= '<input type="file" class="flat maxwidth200onsmartphone" name="photo" id="photoinput" accept="image/*"' . ($capture ? ' capture="' . dolPrintHTMLForAttribute($capture) . '"' : '') . '>';
12001 $ret .= '</td></tr>';
12002 $ret .= '</table>';
12003 }
12004 }
12005
12006 return $ret;
12007 }
12008
12009 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
12010
12027 public function select_dolgroups($selected = 0, $htmlname = 'groupid', $show_empty = 0, $exclude = '', $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0', $multiple = false, $morecss = 'minwidth200')
12028 {
12029 // phpcs:enable
12030 global $conf, $user, $langs;
12031
12032 // Allow excluding groups
12033 $excludeGroups = null;
12034 if (is_array($exclude)) {
12035 $excludeGroups = implode(",", $exclude);
12036 }
12037 // Allow including groups
12038 $includeGroups = null;
12039 if (is_array($include)) {
12040 $includeGroups = implode(",", $include);
12041 }
12042
12043 if (!is_array($selected)) {
12044 $selected = array($selected);
12045 }
12046
12047 $out = '';
12048
12049 // Build sql to search groups
12050 $sql = "SELECT ug.rowid, ug.nom as name";
12051 if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
12052 $sql .= ", e.label";
12053 }
12054 $sql .= " FROM " . $this->db->prefix() . "usergroup as ug ";
12055 if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
12056 $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid=ug.entity";
12057 if ($force_entity) {
12058 $sql .= " WHERE ug.entity IN (0, " . ((int) $force_entity) . ")";
12059 } else {
12060 $sql .= " WHERE ug.entity IS NOT NULL";
12061 }
12062 } else {
12063 $sql .= " WHERE ug.entity IN (0, " . ((int) $conf->entity) . ")";
12064 }
12065 if (is_array($exclude) && $excludeGroups) {
12066 $sql .= " AND ug.rowid NOT IN (" . $this->db->sanitize($excludeGroups) . ")";
12067 }
12068 if (is_array($include) && $includeGroups) {
12069 $sql .= " AND ug.rowid IN (" . $this->db->sanitize($includeGroups) . ")";
12070 }
12071 $sql .= " ORDER BY ug.nom ASC";
12072
12073 dol_syslog(get_class($this) . "::select_dolgroups", LOG_DEBUG);
12074 $resql = $this->db->query($sql);
12075 if ($resql) {
12076 // Enhance with select2
12077 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12078
12079 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
12080
12081 $num = $this->db->num_rows($resql);
12082 $i = 0;
12083 if ($num) {
12084 if ($show_empty && !$multiple) {
12085 $textforempty = '&nbsp;';
12086 if (!is_numeric($show_empty)) {
12087 $textforempty = dol_escape_htmltag($show_empty);
12088 }
12089 $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
12090 }
12091
12092 while ($i < $num) {
12093 $obj = $this->db->fetch_object($resql);
12094 $disableline = 0;
12095 if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
12096 $disableline = 1;
12097 }
12098
12099 $label = $obj->name;
12100 $labelhtml = $obj->name;
12101 if (isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1) {
12102 $label .= " (" . $obj->label . ")";
12103 $labelhtml .= ' <span class="opacitymedium">(' . $obj->label . ')</span>';
12104 }
12105
12106 $out .= '<option value="' . $obj->rowid . '"';
12107 if ($disableline) {
12108 $out .= ' disabled';
12109 }
12110 if ((isset($selected[0]) && is_object($selected[0]) && $selected[0]->id == $obj->rowid)
12111 || ((!isset($selected[0]) || !is_object($selected[0])) && !empty($selected) && in_array($obj->rowid, $selected))) {
12112 $out .= ' selected';
12113 }
12114 $out .= ' data-html="'.dol_escape_htmltag($labelhtml).'"';
12115 $out .= '>';
12116 $out .= $label;
12117 $out .= '</option>';
12118 $i++;
12119 }
12120 } else {
12121 if ($show_empty) {
12122 $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '></option>' . "\n";
12123 }
12124 $out .= '<option value="" disabled>' . $langs->trans("NoUserGroupDefined") . '</option>';
12125 }
12126 $out .= '</select>';
12127
12128 $out .= ajax_combobox($htmlname);
12129 } else {
12130 dol_print_error($this->db);
12131 }
12132
12133 return $out;
12134 }
12135
12136
12143 public function showFilterButtons($pos = '')
12144 {
12145 $out = '<div class="nowraponall">';
12146 $out .= '<button type="submit" class="liste_titre button_search reposition" name="button_search_x" value="x"><span class="fas fa-search"></span></button>';
12147 $out .= '<button type="submit" class="liste_titre button_removefilter reposition" name="button_removefilter_x" value="x"><span class="fas fa-times"></span></button>';
12148 $out .= '</div>';
12149
12150 return $out;
12151 }
12152
12161 public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
12162 {
12163 global $conf;
12164
12165 $out = '';
12166
12167 if (!empty($conf->use_javascript_ajax)) {
12168 $out .= '<div class="inline-block checkallactions"><input type="checkbox" id="' . $cssclass . 's" name="' . $cssclass . 's" class="checkallactions"></div>';
12169 }
12170 $out .= '<script nonce="' . getNonce() . '">
12171 $(document).ready(function() {
12172 $("#' . $cssclass . 's").click(function() {
12173 if($(this).is(\':checked\')){
12174 console.log("We check all ' . $cssclass . ' and trigger the change method");
12175 $(".' . $cssclass . '").prop(\'checked\', true).trigger(\'change\');
12176 }
12177 else
12178 {
12179 console.log("We uncheck all");
12180 $(".' . $cssclass . '").prop(\'checked\', false).trigger(\'change\');
12181 }' . "\n";
12182 if ($calljsfunction) {
12183 $out .= 'if (typeof initCheckForSelect == \'function\') { initCheckForSelect(0, "' . $massactionname . '", "' . $cssclass . '"); } else { console.log("No function initCheckForSelect found. Call won\'t be done."); }';
12184 }
12185 $out .= ' });
12186/*
12187 $(".' . $cssclass . '").change(function() {
12188 console.log("We check and change the tr class highlight after a change on .'.$cssclass.'");
12189 var $row = $(this).closest("tr");
12190 if ($row.length) {
12191 var anyChecked = $row.find(\'input[type="checkbox"].checkforselect:checked\').length > 0;
12192 console.log("anychecked="+anyChecked);
12193 if (!anyChecked) {
12194 $row.removeClass("highlight");
12195 } else {
12196 $row.addClass("highlight");
12197 }
12198 }
12199 });
12200*/
12201 });
12202 </script>';
12203
12204 return $out;
12205 }
12206
12216 public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
12217 {
12218 $out = $this->showFilterButtons();
12219 if ($addcheckuncheckall) {
12220 $out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname);
12221 }
12222 return $out;
12223 }
12224
12238 public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array(), $info_admin = 1)
12239 {
12240 global $langs, $user;
12241
12242 $out = '';
12243 $sql = "SELECT rowid, label FROM " . $this->db->prefix() . "c_exp_tax_cat WHERE active = 1";
12244 $sql .= " AND entity IN (0," . getEntity('exp_tax_cat') . ")";
12245 if (!empty($excludeid)) {
12246 $sql .= " AND rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeid)) . ")";
12247 }
12248 $sql .= " ORDER BY label";
12249
12250 $resql = $this->db->query($sql);
12251 if ($resql) {
12252 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp maxwidth200">';
12253 if ($useempty) {
12254 $out .= '<option value="0">&nbsp;</option>';
12255 }
12256
12257 while ($obj = $this->db->fetch_object($resql)) {
12258 $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . $langs->trans($obj->label) . '</option>';
12259 }
12260 $out .= '</select>';
12261 $out .= ajax_combobox('select_' . $htmlname);
12262
12263 if (!empty($htmlname) && $user->admin && $info_admin) {
12264 $out .= ' ' . info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
12265 }
12266
12267 if (!empty($target)) {
12268 $sql = "SELECT c.id FROM " . $this->db->prefix() . "c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1";
12269 $resql = $this->db->query($sql);
12270 if ($resql) {
12271 if ($this->db->num_rows($resql) > 0) {
12272 $obj = $this->db->fetch_object($resql);
12273 $out .= '<script nonce="' . getNonce() . '">
12274 $(function() {
12275 $("select[name=' . $target . ']").on("change", function() {
12276 var current_val = $(this).val();
12277 if (current_val == ' . $obj->id . ') {';
12278 if (!empty($default_selected) || !empty($selected)) {
12279 $out .= '$("select[name=' . $htmlname . ']").val("' . ($default_selected > 0 ? $default_selected : $selected) . '");';
12280 }
12281
12282 $out .= '
12283 $("select[name=' . $htmlname . ']").change();
12284 }
12285 });
12286
12287 $("select[name=' . $htmlname . ']").change(function() {
12288
12289 if ($("select[name=' . $target . ']").val() == ' . $obj->id . ') {
12290 // get price of kilometer to fill the unit price
12291 $.ajax({
12292 method: "POST",
12293 dataType: "json",
12294 data: { fk_c_exp_tax_cat: $(this).val(), token: \'' . currentToken() . '\' },
12295 url: "' . (DOL_URL_ROOT . '/expensereport/ajax/ajaxik.php?' . implode('&', $params)) . '",
12296 }).done(function( data, textStatus, jqXHR ) {
12297 console.log(data);
12298 if (typeof data.up != "undefined") {
12299 $("input[name=value_unit]").val(data.up);
12300 $("select[name=' . $htmlname . ']").attr("title", data.title);
12301 } else {
12302 $("input[name=value_unit]").val("");
12303 $("select[name=' . $htmlname . ']").attr("title", "");
12304 }
12305 });
12306 }
12307 });
12308 });
12309 </script>';
12310 }
12311 }
12312 }
12313 } else {
12314 dol_print_error($this->db);
12315 }
12316
12317 return $out;
12318 }
12319
12328 public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0)
12329 {
12330 global $conf, $langs;
12331
12332 $out = '';
12333 $sql = "SELECT rowid, range_ik FROM " . $this->db->prefix() . "c_exp_tax_range";
12334 $sql .= " WHERE entity = " . ((int) $conf->entity) . " AND active = 1";
12335
12336 $resql = $this->db->query($sql);
12337 if ($resql) {
12338 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
12339 if ($useempty) {
12340 $out .= '<option value="0"></option>';
12341 }
12342
12343 while ($obj = $this->db->fetch_object($resql)) {
12344 $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . price($obj->range_ik, 0, $langs, 1, 0) . '</option>';
12345 }
12346 $out .= '</select>';
12347 } else {
12348 dol_print_error($this->db);
12349 }
12350
12351 return $out;
12352 }
12353
12364 public function selectExpenseFees($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0)
12365 {
12366 global $langs;
12367
12368 $out = '';
12369 $sql = "SELECT id, code, label";
12370 $sql .= " FROM ".$this->db->prefix()."c_type_fees";
12371 $sql .= " WHERE active = 1";
12372
12373 $resql = $this->db->query($sql);
12374 if ($resql) {
12375 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
12376 if ($useempty) {
12377 $out .= '<option value="0"></option>';
12378 }
12379 if ($allchoice) {
12380 $out .= '<option value="-1">' . $langs->trans('AllExpenseReport') . '</option>';
12381 }
12382
12383 $field = 'code';
12384 if ($useid) {
12385 $field = 'id';
12386 }
12387
12388 while ($obj = $this->db->fetch_object($resql)) {
12389 $key = $langs->trans($obj->code);
12390 $out .= '<option ' . ($selected == $obj->{$field} ? 'selected="selected"' : '') . ' value="' . $obj->{$field} . '">' . ($key != $obj->code ? $key : $obj->label) . '</option>';
12391 }
12392 $out .= '</select>';
12393
12394 $out .= ajax_combobox('select_'.$htmlname);
12395 } else {
12396 dol_print_error($this->db);
12397 }
12398
12399 return $out;
12400 }
12401
12420 public function selectInvoiceForTimeProject($socid = -1, $selected = '', $htmlname = 'invoiceid', $maxlength = 24, $option_only = 0, $show_empty = '1', $discard_closed = 0, $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500', $projectsListId = '', $showproject = 'all', $usertofilter = null)
12421 {
12422 global $user, $conf, $langs;
12423
12424 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
12425
12426 if (is_null($usertofilter)) {
12427 $usertofilter = $user;
12428 }
12429
12430 $out = '';
12431
12432 $hideunselectables = false;
12433 if (getDolGlobalString('INVOICE_HIDE_UNSELECTABLES')) {
12434 $hideunselectables = true;
12435 }
12436
12437 if (empty($projectsListId)) {
12438 if (!$usertofilter->hasRight('projet', 'all', 'lire')) {
12439 $projectstatic = new Project($this->db);
12440 $projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
12441 }
12442 }
12443
12444 // Search all projects
12445 $sql = "SELECT f.rowid, f.ref as fref, 'nolabel' as flabel, p.rowid as pid, f.ref, p.title, p.fk_soc, p.fk_statut, p.public, s.nom as name";
12446 $sql .= " FROM " . $this->db->prefix() . "facture as f";
12447 $sql .= " INNER JOIN " . $this->db->prefix() . "projet as p ON p.entity IN (" . getEntity('project') . ") AND f.fk_projet = p.rowid";
12448 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON s.rowid = p.fk_soc";
12449 $sql .= " WHERE f.fk_statut = 0"; // Draft invoices only
12450 //if ($projectsListId) $sql.= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
12451 //if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
12452 //if ($socid > 0) $sql.= " AND (p.fk_soc=".((int) $socid)." OR p.fk_soc IS NULL)";
12453 $sql .= " ORDER BY p.ref, f.ref ASC";
12454
12455 $resql = $this->db->query($sql);
12456 if ($resql) {
12457 // Use select2 selector
12458 if (!empty($conf->use_javascript_ajax)) {
12459 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12460 $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus);
12461 $out .= $comboenhancement;
12462 $morecss = 'minwidth200imp maxwidth500';
12463 }
12464
12465 if (empty($option_only)) {
12466 $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
12467 }
12468 if (!empty($show_empty)) {
12469 $out .= '<option value="0" class="optiongrey">';
12470 if (!is_numeric($show_empty)) {
12471 $out .= $show_empty;
12472 } else {
12473 $out .= '&nbsp;';
12474 }
12475 $out .= '</option>';
12476 }
12477 $num = $this->db->num_rows($resql);
12478 $i = 0;
12479 if ($num) {
12480 while ($i < $num) {
12481 $obj = $this->db->fetch_object($resql);
12482 // If we ask to filter on a company and user has no permission to see all companies and project is linked to another company, we hide project.
12483 if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && !$usertofilter->hasRight('societe', 'lire')) {
12484 // Do nothing
12485 } else {
12486 if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED) {
12487 $i++;
12488 continue;
12489 }
12490
12491 $labeltoshow = '';
12492
12493 if ($showproject == 'all') {
12494 $labeltoshow .= dol_trunc($obj->ref, 18); // Invoice ref
12495 if ($obj->name) {
12496 $labeltoshow .= ' - ' . $obj->name; // Soc name
12497 }
12498
12499 $disabled = 0;
12500 if ($obj->fk_statut == Project::STATUS_DRAFT) {
12501 $disabled = 1;
12502 $labeltoshow .= ' - ' . $langs->trans("Draft");
12503 } elseif ($obj->fk_statut == Project::STATUS_CLOSED) {
12504 if ($discard_closed == 2) {
12505 $disabled = 1;
12506 }
12507 $labeltoshow .= ' - ' . $langs->trans("Closed");
12508 } elseif ($socid > 0 && (!empty($obj->fk_soc) && $obj->fk_soc != $socid)) {
12509 $disabled = 1;
12510 $labeltoshow .= ' - ' . $langs->trans("LinkedToAnotherCompany");
12511 }
12512 }
12513
12514 if (!empty($selected) && $selected == $obj->rowid) {
12515 $out .= '<option value="' . $obj->rowid . '" selected';
12516 //if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
12517 $out .= '>' . $labeltoshow . '</option>';
12518 } else {
12519 if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
12520 $resultat = '';
12521 } else {
12522 $resultat = '<option value="' . $obj->rowid . '"';
12523 if ($disabled) {
12524 $resultat .= ' disabled';
12525 }
12526 //if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
12527 //else $labeltoshow.=' ('.$langs->trans("Private").')';
12528 $resultat .= '>';
12529 $resultat .= $labeltoshow;
12530 $resultat .= '</option>';
12531 }
12532 $out .= $resultat;
12533 }
12534 }
12535 $i++;
12536 }
12537 }
12538 if (empty($option_only)) {
12539 $out .= '</select>';
12540 }
12541
12542 $this->db->free($resql);
12543
12544 return $out;
12545 } else {
12546 dol_print_error($this->db);
12547 return '';
12548 }
12549 }
12550
12565 public function selectInvoiceRec($selected = '', $htmlname = 'facrecid', $maxlength = 24, $option_only = 0, $show_empty = '1', $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500')
12566 {
12567 global $conf, $langs;
12568
12569 $out = '';
12570
12571 dol_syslog('FactureRec::fetch', LOG_DEBUG);
12572
12573 $sql = 'SELECT f.rowid, f.entity, f.titre as title, f.suspended, f.fk_soc';
12574 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_rec as f';
12575 $sql .= " WHERE f.entity IN (" . getEntity('invoice') . ")";
12576 $sql .= " ORDER BY f.titre ASC";
12577
12578 $resql = $this->db->query($sql);
12579 if ($resql) {
12580 // Use select2 selector
12581 if (!empty($conf->use_javascript_ajax)) {
12582 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12583 $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus);
12584 $out .= $comboenhancement;
12585 $morecss = 'minwidth200imp maxwidth500';
12586 }
12587
12588 if (empty($option_only)) {
12589 $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
12590 }
12591 if (!empty($show_empty)) {
12592 $out .= '<option value="0" class="optiongrey">';
12593 if (!is_numeric($show_empty)) {
12594 $out .= $show_empty;
12595 } else {
12596 $out .= '&nbsp;';
12597 }
12598 $out .= '</option>';
12599 }
12600 $num = $this->db->num_rows($resql);
12601 if ($num) {
12602 while ($obj = $this->db->fetch_object($resql)) {
12603 $labeltoshow = dol_trunc($obj->title, 18); // Invoice ref
12604
12605 $disabled = 0;
12606 if (!empty($obj->suspended)) {
12607 $disabled = 1;
12608 $labeltoshow .= ' - ' . $langs->trans("Closed");
12609 }
12610
12611
12612 if (!empty($selected) && $selected == $obj->rowid) {
12613 $out .= '<option value="' . $obj->rowid . '" selected';
12614 //if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
12615 $out .= '>' . $labeltoshow . '</option>';
12616 } else {
12617 if ($disabled && ($selected != $obj->rowid)) {
12618 $resultat = '';
12619 } else {
12620 $resultat = '<option value="' . $obj->rowid . '"';
12621 if ($disabled) {
12622 $resultat .= ' disabled';
12623 }
12624 $resultat .= '>';
12625 $resultat .= $labeltoshow;
12626 $resultat .= '</option>';
12627 }
12628 $out .= $resultat;
12629 }
12630 }
12631 }
12632 if (empty($option_only)) {
12633 $out .= '</select>';
12634 }
12635
12636 print $out;
12637
12638 $this->db->free($resql);
12639 return $num;
12640 } else {
12641 $this->errors[] = $this->db->lasterror;
12642 return -1;
12643 }
12644 }
12645
12646
12657 public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array(), $search_component_params_hidden = '', $arrayoffiltercriterias = array())
12658 {
12659 // TODO: Use $arrayoffiltercriterias param instead of $arrayofcriterias to include linked object fields in search
12660 global $langs, $form;
12661
12662 //require_once DOL_DOCUMENT_ROOT."/core/class/html.formother.class.php";
12663 //$formother = new FormOther($this->db);
12664
12665 if ($search_component_params_hidden != '' && !preg_match('/^\‍(.*\‍)$/', $search_component_params_hidden)) { // If $search_component_params_hidden does not start and end with ()
12666 $search_component_params_hidden = '(' . $search_component_params_hidden . ')';
12667 }
12668
12669 $ret = '<!-- searchComponent -->';
12670
12671 $ret .= '<div class="divadvancedsearchfieldcomp centpercent inline-block">';
12672 $ret .= '<a href="#" class="dropdownsearch-toggle unsetcolor">';
12673 $ret .= '<span class="fas fa-filter linkobject boxfilter paddingright pictofixedwidth" title="' . dol_escape_htmltag($langs->trans("Filters")) . '" id="idsubimgproductdistribution"></span>';
12674 $ret .= '</a>';
12675
12676 $ret .= '<div class="divadvancedsearchfieldcompinput inline-block minwidth500 maxwidth300onsmartphone">';
12677
12678 // Show select fields as tags.
12679 $ret .= '<div id="divsearch_component_params" name="divsearch_component_params" class="noborderbottom search_component_params inline-block valignmiddle">';
12680
12681 if ($search_component_params_hidden) {
12682 // Split the criteria on each AND
12683 //var_dump($search_component_params_hidden);
12684
12685 $arrayofandtags = dolForgeExplodeAnd($search_component_params_hidden);
12686
12687 // $arrayofandtags is now array( '...' , '...', ...)
12688 // Show each AND part
12689 foreach ($arrayofandtags as $tmpkey => $tmpval) {
12690 $errormessage = '';
12691 $searchtags = forgeSQLFromUniversalSearchCriteria($tmpval, $errormessage, 1, 1);
12692 if ($errormessage) {
12693 $this->error = 'ERROR in parsing search string: '.$errormessage;
12694 }
12695 // Remove first and last parenthesis but only if first is the opening and last the closing of the same group
12696 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
12697 $searchtags = removeGlobalParenthesis($searchtags);
12698
12699 $ret .= '<span class="marginleftonlyshort valignmiddle tagsearch" data-ufilterid="'.($tmpkey + 1).'" data-ufilter="'.dol_escape_htmltag($tmpval).'">';
12700 $ret .= '<span class="tagsearchdelete select2-selection__choice__remove" data-ufilterid="'.($tmpkey + 1).'">x</span> ';
12701 $ret .= dol_escape_htmltag($searchtags);
12702 $ret .= '</span>';
12703 }
12704 }
12705
12706 //$ret .= '<button type="submit" class="liste_titre button_search paddingleftonly" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
12707
12708 //$ret .= search_component_params
12709 //$texttoshow = '<div class="opacitymedium inline-block search_component_searchtext">'.$langs->trans("Search").'</div>';
12710 //$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
12711
12712 $show_search_component_params_hidden = 1;
12713 if ($show_search_component_params_hidden) {
12714 $ret .= '<input type="hidden" name="show_search_component_params_hidden" value="1">';
12715 }
12716 $ret .= "<!-- We store the full Universal Search String into this field. For example: (t.ref:like:'SO-%') AND ((t.ref:like:'CO-%') OR (t.ref:like:'AA%')) -->";
12717 $ret .= '<input type="hidden" id="search_component_params_hidden" name="search_component_params_hidden" value="' . dol_escape_htmltag($search_component_params_hidden) . '">';
12718 // $ret .= "<!-- sql= ".forgeSQLFromUniversalSearchCriteria($search_component_params_hidden, $errormessage)." -->";
12719
12720 // TODO : Use $arrayoffiltercriterias instead of $arrayofcriterias
12721 // For compatibility with forms that show themself the search criteria in addition of this component, we output these fields
12722 foreach ($arrayofcriterias as $criteria) {
12723 foreach ($criteria as $criteriafamilykey => $criteriafamilyval) {
12724 if (in_array('search_' . $criteriafamilykey, $arrayofinputfieldsalreadyoutput)) {
12725 continue;
12726 }
12727 if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) {
12728 continue;
12729 }
12730 if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) {
12731 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_start">';
12732 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startyear">';
12733 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startmonth">';
12734 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startday">';
12735 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_end">';
12736 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endyear">';
12737 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endmonth">';
12738 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endday">';
12739 } else {
12740 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '">';
12741 }
12742 }
12743 }
12744
12745 $ret .= '</div>';
12746
12747 $ret .= "<!-- Field to enter a generic filter string: t.ref:like:'SO-%', t.date_creation:>:'20160101', t.date_creation:<:'2016-01-01 12:30:00', t.nature:is:NULL, t.field2:isnot:NULL -->\n";
12748 $ret .= '<input type="text" placeholder="' . $langs->trans("Filters") . '" id="search_component_params_input" name="search_component_params_input" class="noborderall search_component_input" value="">';
12749
12750 $ret .= '</div>';
12751 $ret .= '</div>';
12752
12753 $ret .= '<script>
12754 jQuery(".tagsearchdelete").click(function(e) {
12755 var filterid = $(this).parents().attr("data-ufilterid");
12756 console.log("We click to delete the criteria nb "+filterid);
12757
12758 // Regenerate the search_component_params_hidden with all data-ufilter except the one to delete, and post the page
12759 var newparamstring = \'\';
12760 $(\'.tagsearch\').each(function(index, element) {
12761 tmpfilterid = $(this).attr("data-ufilterid");
12762 if (tmpfilterid != filterid) {
12763 // We keep this criteria
12764 if (newparamstring == \'\') {
12765 newparamstring = $(this).attr("data-ufilter");
12766 } else {
12767 newparamstring = newparamstring + \' AND \' + $(this).attr("data-ufilter");
12768 }
12769 }
12770 });
12771 console.log("newparamstring = "+newparamstring);
12772
12773 jQuery("#search_component_params_hidden").val(newparamstring);
12774
12775 // We repost the form
12776 $(this).closest(\'form\').submit();
12777 });
12778
12779 jQuery("#search_component_params_input").keydown(function(e) {
12780 console.log("We press a key on the filter field that is "+jQuery("#search_component_params_input").val());
12781 console.log(e.which);
12782 if (jQuery("#search_component_params_input").val() == "" && e.which == 8) {
12783 /* We click on back when the input field is already empty */
12784 event.preventDefault();
12785 jQuery("#divsearch_component_params .tagsearch").last().remove();
12786 /* Regenerate content of search_component_params_hidden from remaining .tagsearch */
12787 var s = "";
12788 jQuery("#divsearch_component_params .tagsearch").each(function( index ) {
12789 if (s != "") {
12790 s = s + " AND ";
12791 }
12792 s = s + $(this).attr("data-ufilter");
12793 });
12794 console.log("New value for search_component_params_hidden = "+s);
12795 jQuery("#search_component_params_hidden").val(s);
12796 }
12797 });
12798
12799 </script>
12800 ';
12801
12802 // Convert $arrayoffiltercriterias into a json object that can be used in jquery to build the search component dynamically
12803 $arrayoffiltercriterias_json = json_encode($arrayoffiltercriterias);
12804 $ret .= '<script>
12805 var arrayoffiltercriterias = ' . $arrayoffiltercriterias_json . ';
12806 </script>';
12807
12808
12809 $arrayoffilterfieldslabel = array();
12810 foreach ($arrayoffiltercriterias as $key => $val) {
12811 $arrayoffilterfieldslabel[$key]['label'] = $val['label'];
12812 $arrayoffilterfieldslabel[$key]['data-type'] = $val['type'];
12813 }
12814
12815 // Adding the div for search assistance
12816 $ret .= '<div class="search-component-assistance">';
12817 $ret .= '<div>';
12818
12819 $ret .= '<p class="assistance-title">' . img_picto('', 'filter') . ' ' . $langs->trans('FilterAssistance') . ' </p>';
12820
12821 $ret .= '<p class="assistance-errors error" style="display:none">' . $langs->trans('AllFieldsRequired') . ' </p>';
12822
12823 $ret .= '<div class="operand">';
12824 $ret .= $form->selectarray('search_filter_field', $arrayoffilterfieldslabel, '', $langs->trans("Fields"), 0, 0, '', 0, 0, 0, '', 'width200 combolargeelem', 1);
12825 $ret .= '</div>';
12826
12827 $ret .= '<span class="separator"></span>';
12828
12829 // Operator selector (will be populated dynamically)
12830 $ret .= '<div class="operator">';
12831 $ret .= '<select class="operator-selector width150" id="operator-selector"">';
12832 $ret .= '</select>';
12833 $ret .= '<script>$(document).ready(function() {';
12834 $ret .= ' $(".operator-selector").select2({';
12835 $ret .= ' placeholder: \'' . dol_escape_js($langs->transnoentitiesnoconv('Operator')) . '\'';
12836 $ret .= ' });';
12837 $ret .= '});</script>';
12838 $ret .= '</div>';
12839
12840 $ret .= '<span class="separator"></span>';
12841
12842 $ret .= '<div class="value">';
12843 // Input field for entering values
12844 $ret .= '<input type="text" class="flat width100 value-input" placeholder="' . dolPrintHTML($langs->trans('Value')) . '">';
12845
12846 // Date selector
12847 $dateOne = '';
12848 $ret .= '<span class="date-one" style="display:none">';
12849 $ret .= $form->selectDate(($dateOne ? $dateOne : -1), 'dateone', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '');
12850 $ret .= '</span>';
12851
12852 // Value selector (will be populated dynamically) based on search_filter_field value if a selected value has an array of values
12853 $ret .= '<select class="value-selector width150" id="value-selector" style="display:none">';
12854 $ret .= '</select>';
12855 $ret .= '<script>
12856 $(document).ready(function() {
12857 $("#value-selector").select2({
12858 placeholder: "' . dol_escape_js($langs->trans('Value')) . '"
12859 });
12860 $("#value-selector").hide();
12861 $("#value-selector").next(".select2-container").hide();
12862 });
12863 </script>';
12864
12865 $ret .= '</div>';
12866
12867 $ret .= '<div class="btn-div">';
12868 $ret .= '<button class="button buttongen button-save add-filter-btn" type="button">' . $langs->trans("addToFilter") . '</button>';
12869 $ret .= '</div>';
12870
12871 $ret .= '</div>';
12872 //$ret .= '</tbody></table>';
12873
12874 // End of the assistance div
12875 $ret .= '</div>';
12876
12877 // Script jQuery to show/hide the floating assistance
12878 $ret .= '<script>
12879 $(document).ready(function() {
12880 $("#search_component_params_input").on("click", function() {
12881 const inputPosition = $(this).offset();
12882 const inputHeight = $(this).outerHeight();
12883 $(".search-component-assistance").css({
12884 top: inputPosition.top + inputHeight + 5 + "px",
12885 left: $("#divsearch_component_params").position().left
12886 }).slideToggle(200);
12887 });
12888 $(document).on("click", function(e) {
12889 if (!$(e.target).closest("#search_component_params_input, .search-component-assistance, #ui-datepicker-div").length) {
12890 $(".search-component-assistance").hide();
12891 }
12892 });
12893 });
12894 </script>';
12895
12896 $ret .= '<script>
12897 $(document).ready(function() {
12898 $(".search_filter_field").on("change", function() {
12899 console.log("We change search_filter_field");
12900
12901 let maybenull = 0;
12902 const selectedField = $(this).find(":selected");
12903 let fieldType = selectedField.data("type");
12904 const selectedFieldValue = selectedField.val();
12905
12906 // If the selected field has an array of values then ask toshow the value selector instead of the value input
12907 if (arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"] !== undefined) {
12908 fieldType = "select";
12909 }
12910
12911 // If the selected field may be null then ask to append the "IsDefined" and "IsNotDefined" operators
12912 if (arrayoffiltercriterias[selectedFieldValue]["maybenull"] !== undefined) {
12913 maybenull = 1;
12914 }
12915 const operators = getOperatorsForFieldType(fieldType, maybenull);
12916 const operatorSelector = $(".operator-selector");
12917
12918 // Clear existing options
12919 operatorSelector.empty();
12920
12921 // Populate operators
12922 Object.entries(operators).forEach(function([operator, label]) {
12923 operatorSelector.append("<option value=\'" + operator + "\'>" + label + "</option>");
12924 });
12925
12926 operatorSelector.trigger("change.select2");
12927
12928 // Clear and hide all input elements initially
12929 $(".value-input, .dateone, .datemonth, .dateyear").val("").hide();
12930 $("#datemonth, #dateyear").val(null).trigger("change.select2");
12931 $("#dateone").datepicker("setDate", null);
12932 $(".date-one, .date-month, .date-year").hide();
12933 $("#value-selector").val("").hide();
12934 $("#value-selector").next(".select2-container").hide();
12935 $("#value-selector").val(null).trigger("change.select2");
12936
12937 if (fieldType === "date" || fieldType === "datetime" || fieldType === "timestamp") {
12938 $(".date-one").show();
12939 } else if (arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"] !== undefined) {
12940 var arrayofkeyval = arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"];
12941 var valueSelector = $("#value-selector");
12942 valueSelector.empty();
12943 Object.entries(arrayofkeyval).forEach(function([key, val]) {
12944 valueSelector.append("<option value=\'" + key + "\'>" + val + "</option>");
12945 });
12946 valueSelector.trigger("change.select2");
12947
12948 $("#value-selector").show();
12949 $("#value-selector").next(".select2-container").show();
12950 } else {
12951 $(".value-input").show();
12952 }
12953 });
12954
12955 $("#operator-selector").on("change", function() {
12956 console.log("We change operator-selector");
12957
12958 const selectedOperator = $(this).find(":selected").val();
12959 if (selectedOperator === "IsDefined" || selectedOperator === "IsNotDefined") {
12960 // Disable all value input elements
12961 $(".value-input, .dateone, .datemonth, .dateyear").val("").prop("disabled", true);
12962 $("#datemonth, #dateyear").val(null).trigger("change.select2");
12963 $("#dateone").datepicker("setDate", null).datepicker("option", "disabled", true);
12964 $(".date-one, .date-month, .date-year").prop("disabled", true);
12965 $("#value-selector").val("").prop("disabled", true);
12966 $("#value-selector").val(null).trigger("change.select2");
12967 } else {
12968 // Enable all value input elements
12969 $(".value-input, .dateone, .datemonth, .dateyear").prop("disabled", false);
12970 $(".date-one, .date-month, .date-year").prop("disabled", false);
12971 $("#dateone").datepicker("option", "disabled", false);
12972 $("#value-selector").prop("disabled", false);
12973 }
12974 });
12975
12976 $(".add-filter-btn").on("click", function(event) {
12977 console.log("We click on add-filter-btn");
12978
12979 event.preventDefault();
12980
12981 const field = $(".search_filter_field").val();
12982 const operator = $(".operator-selector").val();
12983 let value = $(".value-input").val();
12984 const fieldType = $(".search_filter_field").find(":selected").data("type");
12985
12986 if (["date", "datetime", "timestamp"].includes(fieldType)) {
12987 const year = $("#dateoneyear").val().toString().padStart(4, "0");;
12988 const month = $("#dateonemonth").val().toString().padStart(2, "0");
12989 const day = $("#dateoneday").val().toString().padStart(2, "0");
12990 value = `${year}-${month}-${day}`;
12991 console.log("value="+value);
12992 }
12993
12994 // If the selected field has an array of values then take the selected value
12995 if (arrayoffiltercriterias[field]["arrayofkeyval"] !== undefined) {
12996 value = $("#value-selector").val();
12997 }
12998
12999 // If the operator is "IsDefined" or "IsNotDefined" then set the value to 1 (it will not be used)
13000 if (operator === "IsDefined" || operator === "IsNotDefined") {
13001 value = "1";
13002 }
13003
13004 const filterString = generateFilterString(field, operator, value, fieldType);
13005
13006 // Submit the form
13007 if (filterString !== "" && field !== "" && operator !== "" && value !== "") {
13008 $("#search_component_params_input").val($("#search_component_params_input").val() + " " + filterString);
13009 $("#search_component_params_input").closest("form").submit();
13010 } else {
13011 $(".assistance-errors").show();
13012 }
13013 });
13014 });
13015 </script>';
13016
13017 return $ret;
13018 }
13019
13031 public function selectModelMail($prefix, $modelType = '', $default = 0, $addjscombo = 0, $selected = 0, $morecss = '')
13032 {
13033 global $langs, $user;
13034
13035 $retstring = '';
13036
13037 $TModels = array();
13038
13039 include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php';
13040 $formmail = new FormMail($this->db);
13041 $result = $formmail->fetchAllEMailTemplate($modelType, $user, $langs);
13042
13043 if ($default) {
13044 $TModels[0] = $langs->trans('DefaultMailModel');
13045 }
13046 if ($result > 0) {
13047 foreach ($formmail->lines_model as $model) {
13048 $TModels[(int) $model->id] = $model->label;
13049 }
13050 }
13051
13052 $retstring .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $prefix . 'model_mail" name="' . $prefix . 'model_mail">';
13053
13054 foreach ($TModels as $id_model => $label_model) {
13055 $retstring .= '<option value="' . $id_model . '"';
13056 if (!empty($selected) && ((int) $selected) == $id_model) {
13057 $retstring .= "selected";
13058 }
13059 $retstring .= ">" . $label_model . "</option>";
13060 }
13061
13062 $retstring .= "</select>";
13063
13064 if ($addjscombo) {
13065 $retstring .= ajax_combobox('select_' . $prefix . 'model_mail');
13066 }
13067
13068 return $retstring;
13069 }
13070
13082 public function buttonsSaveCancel($save_label = 'Save', $cancel_label = 'Cancel', $morebuttons = array(), $withoutdiv = false, $morecss = '', $dol_openinpopup = '')
13083 {
13084 global $langs;
13085
13086 $buttons = array();
13087
13088 $save = array(
13089 'name' => 'save',
13090 'label_key' => $save_label,
13091 );
13092
13093 if ($save_label == 'Create' || $save_label == 'Add') {
13094 $save['name'] = 'add';
13095 } elseif ($save_label == 'Modify') {
13096 $save['name'] = 'edit';
13097 }
13098
13099 $cancel = array(
13100 'name' => 'cancel',
13101 'label_key' => 'Cancel',
13102 );
13103
13104 // If MAIN_BUTTON_POSITION_FIRST_OR_LEFT not set, default is to have main action first, then complementary, then cancel at end
13105 if (!getDolGlobalInt('MAIN_BUTTON_POSITION_FIRST_OR_LEFT')) {
13106 !empty($save_label) ? $buttons[] = $save : '';
13107 if (!empty($morebuttons)) {
13108 $buttons[] = $morebuttons;
13109 }
13110 !empty($cancel_label) ? $buttons[] = $cancel : '';
13111 } else {
13112 if (!empty($morebuttons)) {
13113 $buttons[] = $morebuttons;
13114 }
13115 !empty($cancel_label) ? $buttons[] = $cancel : '';
13116 !empty($save_label) ? $buttons[] = $save : '';
13117 }
13118
13119 $retstring = $withoutdiv ? '' : '<div class="center">';
13120
13121 foreach ($buttons as $button) {
13122 $addclass = empty($button['addclass']) ? '' : $button['addclass'];
13123 $retstring .= '<input type="submit" class="button marginleftonly marginrightonly button-' . $button['name'] . ($morecss ? ' ' . $morecss : '') . ' ' . $addclass . '" name="' . $button['name'] . '" value="' . dol_escape_htmltag($langs->transnoentities($button['label_key'])) . '">';
13124 }
13125 $retstring .= $withoutdiv ? '' : '</div>';
13126
13127 if ($dol_openinpopup) {
13128 $retstring .= '<!-- buttons are shown into a $dol_openinpopup=' . dol_escape_htmltag($dol_openinpopup) . ' context, so we enable the close of dialog on cancel -->' . "\n";
13129 $retstring .= '<script nonce="' . getNonce() . '">';
13130 $retstring .= 'jQuery(".button-cancel").click(function(e) {
13131 e.preventDefault(); console.log(\'We click on cancel in iframe popup ' . dol_escape_js($dol_openinpopup) . '\');
13132 window.parent.jQuery(\'#idfordialog' . dol_escape_js($dol_openinpopup) . '\').dialog(\'close\');
13133 });';
13134 $retstring .= '</script>';
13135 }
13136
13137 return $retstring;
13138 }
13139
13140
13141 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
13142
13149 {
13150 // phpcs:enable
13151 global $langs;
13152
13153 $num = count($this->cache_invoice_subtype);
13154 if ($num > 0) {
13155 return 0; // Cache already loaded
13156 }
13157
13158 dol_syslog(__METHOD__, LOG_DEBUG);
13159
13160 $sql = "SELECT rowid, code, label as label";
13161 $sql .= " FROM " . MAIN_DB_PREFIX . 'c_invoice_subtype';
13162 $sql .= " WHERE active = 1";
13163
13164 $resql = $this->db->query($sql);
13165 if ($resql) {
13166 $num = $this->db->num_rows($resql);
13167 $i = 0;
13168 while ($i < $num) {
13169 $obj = $this->db->fetch_object($resql);
13170
13171 // If translation exists, we use it, otherwise we take the default wording
13172 $label = ($langs->trans("InvoiceSubtype" . $obj->rowid) != "InvoiceSubtype" . $obj->rowid) ? $langs->trans("InvoiceSubtype" . $obj->rowid) : (($obj->label != '-') ? $obj->label : '');
13173 $this->cache_invoice_subtype[$obj->rowid]['rowid'] = $obj->rowid;
13174 $this->cache_invoice_subtype[$obj->rowid]['code'] = $obj->code;
13175 $this->cache_invoice_subtype[$obj->rowid]['label'] = $label;
13176 $i++;
13177 }
13178
13179 $this->cache_invoice_subtype = dol_sort_array($this->cache_invoice_subtype, 'code', 'asc', 0, 0, 1);
13180
13181 return $num;
13182 } else {
13183 dol_print_error($this->db);
13184 return -1;
13185 }
13186 }
13187
13188
13199 public function getSelectInvoiceSubtype($selected = 0, $htmlname = 'subtypeid', $addempty = 0, $noinfoadmin = 0, $morecss = '')
13200 {
13201 global $langs, $user;
13202
13203 $out = '';
13204 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
13205
13206 $this->load_cache_invoice_subtype();
13207
13208 $out .= '<select id="' . $htmlname . '" class="flat selectsubtype' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
13209 if ($addempty) {
13210 $out .= '<option value="0">&nbsp;</option>';
13211 }
13212
13213 foreach ($this->cache_invoice_subtype as $rowid => $subtype) {
13214 $label = $subtype['label'];
13215 $out .= '<option value="' . $subtype['rowid'] . '"';
13216 if ($selected == $subtype['rowid']) {
13217 $out .= ' selected="selected"';
13218 }
13219 $out .= '>';
13220 $out .= $label;
13221 $out .= '</option>';
13222 }
13223
13224 $out .= '</select>';
13225 if ($user->admin && empty($noinfoadmin)) {
13226 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
13227 }
13228 $out .= ajax_combobox($htmlname);
13229
13230 return $out;
13231 }
13232
13242 public function getSearchFilterToolInput($dataTarget, $htmlName = 'search-tools-input', $value = '', $params = [])
13243 {
13244 global $langs;
13245
13246 $attr = array(
13247 'type' => 'search',
13248 'name' => $htmlName,
13249 'value' => $value,
13250 'class' => "search-tool-input",
13251 'placeholder' => $langs->trans('Search'),
13252 'autocomplete' => 'off'
13253 );
13254
13255 // Optional data attr
13256 // 'autofocus' : will set auto focus on field ,
13257 // data-counter-target : will get count results
13258 // data-no-item-target : will be display if count results is 0
13259
13260 if ($dataTarget !== false) {
13261 $attr['data-search-tool-target'] = $dataTarget;
13262 }
13263
13264 // Override attr
13265 if (!empty($params['attr']) && is_array($params['attr'])) {
13266 foreach ($params['attr'] as $key => $value) {
13267 if ($key == 'class') {
13268 $attr['class'] .= ' '.$value;
13269 } elseif ($key == 'classOverride') {
13270 $attr['class'] = $value;
13271 } else {
13272 $attr[$key] = $value;
13273 }
13274 }
13275 }
13276
13277 // automatic add tooltip when title is detected
13278 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
13279 $attr['class'] .= ' classfortooltip';
13280 }
13281
13282 $TCompiledAttr = [];
13283 foreach ($attr as $key => $value) {
13284 if (in_array($key, ['data-target'])
13285 || (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr']))) { // Not recommended
13286 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
13287 } else {
13288 $value = dolPrintHTMLForAttribute($value);
13289 }
13290
13291 $TCompiledAttr[] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
13292 }
13293
13294 $compiledAttributes = implode(' ', $TCompiledAttr);
13295
13296
13297 return '<div class="search-tool-container"><input '.$compiledAttributes.'></div>';
13298 }
13299
13313 public function inputType($type, $name, $value = '', $id = '', $morecss = '', $moreparam = '', $label = '', $addInputLabel = '')
13314 {
13315 $out = '';
13316 if ($label != '') {
13317 $out .= '<label for="' . dolPrintHTMLForAttribute($id) . '">';
13318 }
13319 $out .= '<input type="' . dolPrintHTMLForAttribute($type) . '"';
13320 $out .= ' class="flat valignmiddle maxwidthonsmartphone ' . dolPrintHTMLForAttribute($morecss) . '"';
13321 if ($id != '') {
13322 $out .= ' id="' . dolPrintHTMLForAttribute($id) . '"';
13323 }
13324 $out .= ' name="' . dolPrintHTMLForAttribute($name) . '"';
13325 $out .= ' value="' . dolPrintHTMLForAttribute($value) . '" ';
13326 $out .= ($moreparam ? ' ' . $moreparam : '');
13327 $out .= ' />' . $addInputLabel;
13328 if ($label != '') {
13329 $out .= $label . '</label>';
13330 }
13331
13332 return $out;
13333 }
13334
13347 public function inputSelectAjax($htmlName, $array, $id, $ajaxUrl, $ajaxData = [], $morecss = 'minwidth75', $moreparam = '')
13348 {
13349 $out = "
13350 <script>
13351 $(document).ready(function () {
13352 $('#" . $htmlName . "').select2({
13353 ajax: {
13354 url: '" . $ajaxUrl . "',
13355 dataType: 'json',
13356 delay: 250, // wait 250 milliseconds before triggering the request
13357 data: function (params) {
13358 var query = {
13359 search: params.term,
13360 page: params.page || 1";
13361 if (!empty($ajaxData) && is_array($ajaxData)) {
13362 foreach ($ajaxData as $key => $value) {
13363 $out .= ", " . $key . ": '" . $value . "'";
13364 }
13365 }
13366 $out .= "
13367 }
13368 return query;
13369 }
13370 }
13371 })
13372 });
13373 </script>";
13374
13375 $out .= $this->selectarray($htmlName, $array, $id, 0, 0, 0, $moreparam, 0, 0, 0, '', $morecss);
13376
13377 return $out;
13378 }
13379
13389 public function inputHtml($htmlName, $value, $morecss = '', $moreparam = '')
13390 {
13391 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
13392 $doleditor = new DolEditor($htmlName, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_5, '90%');
13393
13394 return (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
13395 }
13396
13407 public function inputText($htmlName, $value, $morecss = '', $moreparam = '', $options = array())
13408 {
13409 global $langs;
13410
13411 $out = '';
13412 if (!empty($options)) {
13413 // If the textarea field has a list of arrayofkeyval into its definition, we suggest a combo with possible values to fill the textarea.
13414 $out .= $this->selectarray($htmlName . "_multiinput", $options, '', 1, 0, 0, $moreparam, 0, 0, 0, '', "flat maxwidthonphone" . $morecss);
13415 $out .= '<input id="' . $htmlName . '_multiinputadd" type="button" class="button" value="' . $langs->trans("Add") . '">';
13416 $out .= "<script>";
13417 $out .= '
13418 function handlemultiinputdisabling(htmlname){
13419 console.log("We handle the disabling of used options for "+htmlname+"_multiinput");
13420 multiinput = $("#"+htmlname+"_multiinput");
13421 multiinput.find("option").each(function(){
13422 tmpval = $("#"+htmlname).val();
13423 tmpvalarray = tmpval.split("\n");
13424 valtotest = $(this).val();
13425 if(tmpvalarray.includes(valtotest)){
13426 $(this).prop("disabled",true);
13427 } else {
13428 if($(this).prop("disabled") == true){
13429 console.log(valtotest)
13430 $(this).prop("disabled", false);
13431 }
13432 }
13433 });
13434 }
13435
13436 $(document).ready(function () {
13437 $("#' . $htmlName . '_multiinputadd").on("click",function() {
13438 tmpval = $("#' . $htmlName . '").val();
13439 tmpvalarray = tmpval.split(",");
13440 valtotest = $("#' . $htmlName . '_multiinput").val();
13441 if(valtotest != -1 && !tmpvalarray.includes(valtotest)){
13442 console.log("We add the selected value to the text area ' . $htmlName . '");
13443 if(tmpval == ""){
13444 tmpval = valtotest;
13445 } else {
13446 tmpval = tmpval + "\n" + valtotest;
13447 }
13448 $("#' . $htmlName . '").val(tmpval);
13449 handlemultiinputdisabling("' . $htmlName . '");
13450 $("#' . $htmlName . '_multiinput").val(-1);
13451 } else {
13452 console.log("We add nothing the text area ' . $htmlName . '");
13453 }
13454 });
13455 $("#' . $htmlName . '").on("change",function(){
13456 handlemultiinputdisabling("' . $htmlName . '");
13457 });
13458 handlemultiinputdisabling("' . $htmlName . '");
13459 })';
13460 $out .= "</script>";
13461 $value = str_replace(',', "\n", $value);
13462 }
13463
13464 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
13465 $doleditor = new DolEditor($htmlName, (string) $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
13466 $out .= (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
13467
13468 return $out;
13469 }
13470
13481 public function inputRadio($htmlName, $options, $selectedValue, $morecss = '', $moreparam = '')
13482 {
13483 $out = '';
13484 foreach ($options as $optionKey => $optionLabel) {
13485 $selected = ((string) $selectedValue) === ((string) $optionKey) ? ' checked="checked"' : '';
13486 $optionId = $htmlName . '_' . $optionKey;
13487 $out .= '<input class="flat' . $morecss . '" type="radio" name="' . $htmlName . '" id="' . $optionId . '" value="' . dolPrintHTMLForAttribute((string) $optionKey) . '"' . $selected . $moreparam . '/><label for="' . $optionId . '">' . $optionLabel . '</label><br>';
13488 }
13489
13490 return $out;
13491 }
13492
13503 public function inputStars($htmlName, $size, $value, $morecss = '', $moreparam = '')
13504 {
13505 $out = '<input type="hidden" class="flat ' . $morecss . '" name="' . $htmlName . '" id="' . $htmlName . '" value="' . dolPrintHTMLForAttribute((string) $value) . '"' . $moreparam . '>';
13506 $out .= '<div class="star-selection" id="' . $htmlName . '_selection">';
13507 for ($i = 1; $i <= $size; $i++) {
13508 $out .= '<span class="star" data-value="' . $i . '">' . img_picto('', 'fontawesome_star_fas') . '</span>';
13509 }
13510 $out .= '</div>';
13511 $out .= '<script>
13512 jQuery(function($) { /* commonobject.class.php 1 */
13513 let container = $("#' . $htmlName . '_selection");
13514 let selectedStars = parseInt($("#' . $htmlName . '").val()) || 0;
13515 container.find(".star").each(function() {
13516 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13517 });
13518 container.find(".star").on("mouseover", function() {
13519 let selectedStar = $(this).data("value");
13520 container.find(".star").each(function() {
13521 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
13522 });
13523 });
13524 container.on("mouseout", function() {
13525 container.find(".star").each(function() {
13526 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13527 });
13528 });
13529 container.find(".star").off("click").on("click", function() {
13530 selectedStars = $(this).data("value");
13531 if (selectedStars === 1 && $("#' . $htmlName . '").val() == 1) {
13532 selectedStars = 0;
13533 }
13534 $("#' . $htmlName . '").val(selectedStars);
13535 container.find(".star").each(function() {
13536 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13537 });
13538 });
13539 });
13540 </script>';
13541
13542 return $out;
13543 }
13544
13554 public function inputIcon($htmlName, $value, $morecss = '', $moreparam = '')
13555 {
13556 global $langs;
13557
13558 /* External lib inclusion are not allowed in backoffice. Also lib is included several time if there is several icon file.
13559 Some code must be added into main when MAIN_ADD_ICONPICKER_JS is set to add of lib in html header
13560 $out ='<link rel="stylesheet" href="'.dol_buildpath('/myfield/css/fontawesome-iconpicker.min.css', 1).'">';
13561 $out.='<script src="'.dol_buildpath('/myfield/js/fontawesome-iconpicker.min.js', 1).'"></script>';
13562 */
13563 $out = '<input type="text" class="form-control icp icp-auto iconpicker-element iconpicker-input flat ' . $morecss . ' maxwidthonsmartphone"';
13564 $out .= ' name="' . $htmlName . '" id="' . $htmlName . '" value="' . dolPrintHTMLForAttribute((string) $value) . '" ' . ((string) $moreparam) . '>';
13565 if (getDolGlobalInt('MAIN_ADD_ICONPICKER_JS')) {
13566 $out .= '<script>';
13567 $options = "{ title: '<b>" . $langs->trans("IconFieldSelector") . "</b>', placement: 'right', showFooter: false, templates: {";
13568 $options .= "iconpicker: '<div class=\"iconpicker\"><div style=\"background-color:#EFEFEF;\" class=\"iconpicker-items\"></div></div>',";
13569 $options .= "iconpickerItem: '<a role=\"button\" href=\"#\" class=\"iconpicker-item\" style=\"background-color:#DDDDDD;\"><i></i></a>',";
13570 // $options.="buttons: '<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm\">".$langs->trans("Cancel")."</button>";
13571 // $options.="<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm\">".$langs->trans("Save")."</button>',";
13572 $options .= "footer: '<div class=\"popover-footer\" style=\"background-color:#EFEFEF;\"></div>',";
13573 $options .= "search: '<input type=\"search\" class\"form-control iconpicker-search\" placeholder=\"" . $langs->trans("TypeToFilter") . "\" />',";
13574 $options .= "popover: '<div class=\"iconpicker-popover popover\">";
13575 $options .= " <div class=\"arrow\" ></div>";
13576 $options .= " <div class=\"popover-title\" style=\"text-align:center;background-color:#EFEFEF;\"></div>";
13577 $options .= " <div class=\"popover-content \" ></div>";
13578 $options .= "</div>'}}";
13579 $out .= "$('#" . $htmlName . "').iconpicker(" . $options . ");";
13580 $out .= '</script>';
13581 }
13582
13583 return $out;
13584 }
13585
13594 public function inputGeoPoint($htmlName, $value, $type = '')
13595 {
13596 require_once DOL_DOCUMENT_ROOT . '/core/class/dolgeophp.class.php';
13597 require_once DOL_DOCUMENT_ROOT . '/core/class/geomapeditor.class.php';
13598 $dolgeophp = new DolGeoPHP($this->db);
13599 $geomapeditor = new GeoMapEditor();
13600
13601 $geojson = '{}';
13602 $centroidjson = getDolGlobalString('MAIN_INFO_SOCIETE_GEO_COORDINATES', '{}');
13603 if (!empty($value)) {
13604 $tmparray = $dolgeophp->parseGeoString($value);
13605 $geojson = $tmparray['geojson'];
13606 $centroidjson = $tmparray['centroidjson'];
13607 }
13608
13609 return $geomapeditor->getHtml($htmlName, $geojson, $centroidjson, $type);
13610 }
13611
13618 public function outputMultiValues($values)
13619 {
13620 $out = '';
13621 $toPrint = array();
13622 $values = is_array($values) ? $values : array();
13623
13624 foreach ($values as $value) {
13625 $toPrint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $value . '</li>';
13626 }
13627 if (!empty($toPrint)) {
13628 $out = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toPrint) . '</ul></div>';
13629 }
13630
13631 return $out;
13632 }
13633
13641 public function outputStars($size, $value)
13642 {
13643 $out = '<div class="star-selection" data-value="' . dolPrintHTMLForAttribute((string) $value) . '">';
13644 for ($i = 1; $i <= $size; $i++) {
13645 $out .= '<span class="star' . ($i <= $value ? ' active' : '') . '" data-value="' . $i . '">' . img_picto('', 'fontawesome_star_fas') . '</span>';
13646 }
13647 $out .= '</div>';
13648
13649 return $out;
13650 }
13651
13658 public function outputIcon($value)
13659 {
13660 $out = '<span class="' . dolPrintHTMLForAttribute((string) $value) . '"></span>';
13661
13662 return $out;
13663 }
13664
13672 public function outputGeoPoint($value, $type)
13673 {
13674 $out = '';
13675
13676 if (!empty($value)) {
13677 require_once DOL_DOCUMENT_ROOT . '/core/class/dolgeophp.class.php';
13678 $dolgeophp = new DolGeoPHP($this->db);
13679 if ($type == 'point') {
13680 $out = $dolgeophp->getXYString($value);
13681 } else { // multipts, linestrg, polygon
13682 $out = $dolgeophp->getPointString($value);
13683 }
13684 }
13685
13686 return $out;
13687 }
13688
13703 public function getNomUrl(&$object, $withpicto = 0, $option = '', $maxlength = 0, $save_lastsearch_value = -1, $notooltip = 0, $morecss = '', $add_label = 0, $sep = ' - ')
13704 {
13705 if (is_object($object) && method_exists($object, 'getNomUrl')) {
13706 $out = $object->getNomUrl($withpicto, $option, $maxlength, $save_lastsearch_value, $notooltip, $morecss, $add_label, $sep);
13707 return $out;
13708 } else {
13709 return '';
13710 }
13711 }
13712}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
ajax_autocompleter($selected, $htmlname, $url, $urloption='', $minLength=2, $autoselect=0, $ajaxoptions=array(), $moreparams='')
Generic function that return javascript to add to transform a common input text or select field into ...
Definition ajax.lib.php:50
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:476
ajax_multiautocompleter($htmlname, $fields, $url, $option='', $minLength=2, $autoselect=0)
Generic function that return javascript to add to a page to transform a common input text field into ...
Definition ajax.lib.php:325
ajax_event($htmlname, $events)
Add event management script.
Definition ajax.lib.php:599
$c
Definition line.php:334
Class to manage bank accounts.
Class to manage members of a foundation.
Class to manage categories.
Class to manage bank accounts description of third parties.
Class for ConferenceOrBoothAttendee.
Class to manage contact/addresses.
Class to manage a WYSIWYG editor.
Class to manage Geo processing Usage: $dolgeophp=new DolGeoPHP($db);.
DAO Resource object.
Class to manage ECM files.
const STATUS_OPEN_INTERNAL
Warehouse open and only operations for stock transfers/corrections allowed (not for customer shipping...
const STATUS_OPEN_ALL
Warehouse open and any operations are allowed (customer shipping, supplier dispatch,...
const STATUS_CLOSED
Warehouse closed, inactive.
Class to manage standard extra languages.
Class to manage invoices.
Class to manage invoice templates.
Class to manage generation of HTML components Only common components must be here.
showLinkToObjectBlock($object, $restrictlinksto=array(), $excludelinksto=array(), $nooutput=0)
Show block with links "to link to" other objects.
inputSelectAjax($htmlName, $array, $id, $ajaxUrl, $ajaxData=[], $morecss='minwidth75', $moreparam='')
Html for select with get options by AJAX.
selectMultiCurrency($selected='', $htmlname='multicurrency_code', $useempty=0, $filter='', $excludeConfCurrency=false, $morecss='maxwidth200 widthcentpercentminusx')
Return array of currencies in user language.
showFilterButtons($pos='')
Return HTML to show the search and clear search button.
select_dolusers_forevent($action='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include=array(), $enableonly=array(), $force_entity='0', $maxlength=0, $showstatus=0, $morefilter='', $showproperties=0, $listofuserid=array(), $listofcontactid=array(), $listofotherid=array(), $canremoveowner=1)
Return select list of users.
load_cache_vatrates($country_code)
Load into the cache ->cache_vatrates, all the vat rates of a country.
inputRadio($htmlName, $options, $selectedValue, $morecss='', $moreparam='')
Html for input radio.
select_comptes($selected='', $htmlname='accountid', $status=0, $filtre='', $useempty=0, $moreattrib='', $showcurrency=0, $morecss='', $nooutput=0, $addentrynone=0)
Return a HTML select list of bank accounts.
static selectArrayAjax($htmlname, $url, $id='', $moreparam='', $moreparamtourl='', $disabled=0, $minimumInputLength=1, $morecss='', $callurlonselect=0, $placeholder='', $acceptdelayedhtml=0)
Return a HTML select string, built from an array of key+value, but content returned into select come ...
inputGeoPoint($htmlName, $value, $type='')
Html for input geo point.
editfieldval($text, $htmlname, $value, $object, $perm, $typeofdata='string', $editvalue='', $extObject=null, $custommsg=null, $moreparam='', $notabletag=1, $formatfunc='', $paramid='id', $gm='auto', $moreoptions=array(), $editaction='')
Output value of a field for an editable field.
form_availability($page, $selected='', $htmlname='availability', $addempty=0)
Show a form to select a delivery delay.
showLinkedObjectBlock($object, $morehtmlright='', $compatibleImportElementsList=array(), $title='RelatedObjects')
Show linked object block.
select_dolusers($userselected='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include='', $enableonly='', $force_entity='', $maxlength=0, $showstatus=0, $morefilter='', $showalso=0, $enableonlytext='', $morecss='', $notdisabled=0, $outputmode=0, $multiple=false, $forcecombo=0)
Return select list of users.
selectMassAction($selected, $arrayofaction, $alwaysvisible=0, $name='massaction', $cssclass='checkforselect')
Generate select HTML to choose massaction.
getSelectRuleForLinesDates($selected='', $htmlname='rule_for_lines_dates', $addempty=0)
Returns select with rule for lines dates.
select_dolresources_forevent($action='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include=array(), $enableonly=array(), $force_entity='0', $maxlength=0, $showstatus=0, $morefilter='', $showproperties=0, $listofresourceid=array())
Return select list of resources.
form_multicurrency_code($page, $selected='', $htmlname='multicurrency_code')
Show form with multicurrency code.
static radio($htmlName, $radioItems, $selected='', $moreGlobalParams=[])
Generates a set of HTML radio inputs from an array of key-value items.
formRib($page, $selected='', $htmlname='ribcompanyid', $filtre='', $addempty=0, $showibanbic=0)
Display form to select bank customer account.
showFilterAndCheckAddButtons($addcheckuncheckall=0, $cssclass='checkforaction', $calljsfunction=0, $massactionname="massaction")
Return HTML to show the search and clear search button.
select_company($selected='', $htmlname='socid', $filter='', $showempty='', $showtype=0, $forcecombo=0, $events=array(), $limit=0, $morecss='minwidth100', $moreparam='', $selected_input_value='', $hidelabel=1, $ajaxoptions=array(), $multiple=false, $excludeids=array(), $showcode=0)
Output html form to select a third party This call select_thirdparty_list() or ajax depending on setu...
select_produits($selected=0, $htmlname='productid', $filtertype='', $limit=0, $price_level=0, $status=1, $finished=2, $selected_input_value='', $hidelabel=0, $ajaxoptions=array(), $socid=0, $showempty='1', $forcecombo=0, $morecss='', $hidepriceinlabel=0, $warehouseStatus='', $selected_combinations=null, $nooutput=0, $status_purchase=-1, $warehouseId=0)
Return list of products.
inputType($type, $name, $value='', $id='', $morecss='', $moreparam='', $label='', $addInputLabel='')
Html for input with label.
select_dolgroups($selected=0, $htmlname='groupid', $show_empty=0, $exclude='', $disabled=0, $include='', $enableonly=array(), $force_entity='0', $multiple=false, $morecss='minwidth200')
Return select list of user groups.
selectInputReason($selected='', $htmlname='demandreasonid', $exclude='', $addempty=0, $morecss='', $notooltip=0)
Return list of input reason (events that triggered an object creation, like after sending an emailing...
select_contact($socid, $selected='', $htmlname='contactid', $showempty=0, $exclude='', $limitto='', $showfunction=0, $morecss='', $nokeyifsocid=true, $showsoc=0, $forcecombo=0, $events=array(), $moreparam='', $htmlid='', $selected_input_value='', $filter='')
Output html form to select a contact This call select_contacts() or ajax depending on setup.
select_incoterms($selected='', $location_incoterms='', $page='', $htmlname='incoterm_id', $htmloption='', $forcecombo=1, $events=array(), $disableautocomplete=0)
Return select list of incoterms.
select_type_of_lines($selected='', $htmlname='type', $showempty=0, $hidetext=0, $forceall=0, $morecss="", $useajaxcombo=1)
Return list of types of lines (product or service) Example: 0=product, 1=service, 9=other (for extern...
selectModelMail($prefix, $modelType='', $default=0, $addjscombo=0, $selected=0, $morecss='')
selectModelMail
select_types_paiements($selected='', $htmlname='paiementtype', $filtertype='', $format=0, $empty=1, $noadmininfo=0, $maxlength=0, $active=1, $morecss='', $nooutput=0)
Return list of payment methods Constant MAIN_DEFAULT_PAYMENT_TYPE_ID can used to set default value bu...
select_currency($selected='', $htmlname='currency_id')
Returns the list of currencies in the user's language.
inputStars($htmlName, $size, $value, $morecss='', $moreparam='')
Html for input stars.
formSelectTransportMode($page, $selected='', $htmlname='transport_mode_id', $active=1, $addempty=0)
Show form with transport mode.
selectShippingMethod($selected='', $htmlname='shipping_method_id', $filtre='', $useempty=0, $moreattrib='', $noinfoadmin=0, $morecss='')
Return a HTML select list of shipping mode.
select_produits_list($selected=0, $htmlname='productid', $filtertype='', $limit=1000, $price_level=0, $filterkey='', $status=1, $finished=2, $outputmode=0, $socid=0, $showempty='1', $forcecombo=0, $morecss='maxwidth500', $hidepriceinlabel=0, $warehouseStatus='', $status_purchase=-1, $warehouseId=0)
Return list of products for a customer.
selectRib($selected='', $htmlname='ribcompanyid', $filtre='', $useempty=0, $moreattrib='', $showibanbic=0, $morecss='', $nooutput=0)
Return a HTML select list of bank accounts customer.
formSelectShippingMethod($page, $selected='', $htmlname='shipping_method_id', $addempty=0)
Display form to select shipping mode.
static multiselectarray($htmlname, $array, $selected=array(), $key_in_label=0, $value_as_key=0, $morecss='', $translate=0, $width=0, $moreattrib='', $nu='', $placeholder='', $addjscombo=-1)
Show a multiselect form from an array.
getSelectInvoiceSubtype($selected=0, $htmlname='subtypeid', $addempty=0, $noinfoadmin=0, $morecss='')
Return list of invoice subtypes.
form_contacts($page, $societe, $selected='', $htmlname='contactid')
Show forms to select a contact.
load_tva($htmlname='tauxtva', $selectedrate='', $societe_vendeuse=null, $societe_acheteuse=null, $idprod=0, $info_bits=0, $type='', $options_only=false, $mode=0, $type_vat=0)
Output an HTML select vat rate.
load_cache_availability()
Load int a cache property the list of possible delivery delays.
select_bom($selected='', $htmlname='bom_id', $limit=0, $status=1, $type=0, $showempty='1', $morecss='', $nooutput='', $forcecombo=0, $TProducts=[])
Return list of BOM for customer in Ajax if Ajax activated or go to select_produits_list.
form_rule_for_lines_dates($page, $selected='', $htmlname='rule_for_lines_dates', $addempty=0, $nooutput=0)
Form select for rule for lines dates.
selectcontacts($socid, $selected=array(), $htmlname='contactid', $showempty=0, $exclude='', $limitto='', $showfunction=0, $morecss='', $options_only=0, $showsoc=0, $forcecombo=0, $events=array(), $moreparam='', $htmlid='', $multiple=false, $disableifempty=0, $filter='')
Return HTML code of the SELECT of list of all contacts (for a third party or all).
static selectarray($htmlname, $array, $id='', $show_empty=0, $key_in_label=0, $value_as_key=0, $moreparam='', $translate=0, $maxlen=0, $disabled=0, $sort='', $morecss='minwidth75', $addjscombo=1, $moreparamonempty='', $disablebademail=0, $nohtmlescape=0)
Return a HTML select string, built from an array of key+value.
select_type_fees($selected='', $htmlname='type', $showempty=0)
Return list of types of notes.
selectInvoiceRec($selected='', $htmlname='facrecid', $maxlength=24, $option_only=0, $show_empty='1', $forcefocus=0, $disabled=0, $morecss='maxwidth500')
Output a combo list with invoices qualified for a third party TODO Bad method.
inputIcon($htmlName, $value, $morecss='', $moreparam='')
Html for input icon.
form_multicurrency_rate($page, $rate=0.0, $htmlname='multicurrency_tx', $currency='', $rate_direct=0.0)
Show form with multicurrency rate.
editInPlace($object, $value, $htmlname, $condition, $inputType='textarea', $editvalue=null, $extObject=null, $custommsg=null)
Output edit in place form.
getPhoneInputSharedJs($countrySelectorId)
Return inline JS for country-selector → phone code sync (output once per page).
showPhoneInput($phoneValue, $htmlname, $country_id_hint=0, $picto='object_phoning', $morecss='maxwidth150', $maxlength=0, $countrySelectorId='selectcountry_id')
Show a self-contained phone input: hidden field + country code dropdown + number text field + JS.
selectUnits($selected='', $htmlname='units', $showempty=0, $unit_type='')
Creates HTML units selector (code => label)
static showphoto($modulepart, $object, $width=100, $height=0, $caneditfield=0, $cssclass='photowithmargin', $imagesize='', $addlinktofullsize=1, $cache=0, $forcecapture='', $noexternsourceoverwrite=0, $usesharelinkifavailable=0)
Return HTML code to output a photo.
form_modes_reglement($page, $selected='', $htmlname='mode_reglement_id', $filtertype='', $active=1, $addempty=0, $type='', $nooutput=0)
Show form with payment mode.
constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel=0, $filterkey='', $novirtualstock=0)
Function to forge the string with OPTIONs of SELECT.
buttonsSaveCancel($save_label='Save', $cancel_label='Cancel', $morebuttons=array(), $withoutdiv=false, $morecss='', $dol_openinpopup='')
Output the buttons to submit a creation/edit form.
selectTransportMode($selected='', $htmlname='transportmode', $format=0, $empty=1, $noadmininfo=0, $maxlength=0, $active=1, $morecss='')
Return list of transport mode for intracomm report.
form_conditions_reglement($page, $selected='', $htmlname='cond_reglement_id', $addempty=0, $type='', $filtertype=-1, $deposit_percent=-1, $nooutput=0)
Show a form to select payment conditions.
selectSituationInvoices($selected='', $socid=0)
Creates HTML last in cycle situation invoices selector.
selectPriceBaseType($selected='', $htmlname='price_base_type', $addjscombo=0)
Selection HT or TTC.
load_cache_transport_mode()
getSearchFilterToolInput($dataTarget, $htmlName='search-tools-input', $value='', $params=[])
select_conditions_paiements($selected=0, $htmlname='condid', $filtertype=-1, $addempty=0, $noinfoadmin=0, $morecss='', $deposit_percent=-1, $noprint=0)
print list of payment modes.
select_remises($selected, $htmlname, $filter, $socid, $maxvalue=0)
Return HTML combo list of absolute discounts.
showbarcode(&$object, $width=100, $morecss='')
Return HTML code to output a barcode.
load_cache_rule_for_lines_dates()
Loads into a cache property the list of possible rules for line dates.
form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter='', $maxvalue=0, $more='', $hidelist=0, $discount_type=0, $filterabsolutediscount=0, $filtercreditnote=0)
Show a select box with available absolute discounts.
getPhoneInputFieldJs($htmlname, $codename)
Return inline JS that syncs the hidden phone field from select + text input.
form_confirm($page, $title, $question, $action, $formquestion=array(), $selectedchoice="", $useajax=0, $height=170, $width=500)
load_cache_conditions_paiements()
Load into cache list of payment terms.
selectExpenseCategories($selected='', $htmlname='fk_c_exp_tax_cat', $useempty=0, $excludeid=array(), $target='', $default_selected=0, $params=array(), $info_admin=1)
Return HTML to show the select of expense categories.
selectPhoneCode($selected='', $htmlname='phone_code', $morecss='maxwidth150', $showempty=0, $country_id_hint=0)
Return a select list of country phone calling codes.
select_product_fourn_price($productid, $htmlname='productfournpriceid', $selected_supplier=0)
Return list of suppliers prices for a product.
getNomUrl(&$object, $withpicto=0, $option='', $maxlength=0, $save_lastsearch_value=-1, $notooltip=0, $morecss='', $add_label=0, $sep=' - ')
Return link of object.
form_project($page, $socid, $selected='', $htmlname='projectid', $discard_closed=0, $maxlength=20, $forcefocus=0, $nooutput=0, $textifnoproject='', $morecss='', $option='')
Show a form to select a project.
selectAvailabilityDelay($selected='', $htmlname='availid', $filtertype='', $addempty=0, $morecss='', $noouput=0)
Return the list of type of delay available.
outputGeoPoint($value, $type)
Html for show geo point.
form_date($page, $selected, $htmlname, $displayhour=0, $displaymin=0, $nooutput=0, $type='')
Show a form + html select a date.
showCheckAddButtons($cssclass='checkforaction', $calljsfunction=0, $massactionname="massaction")
Return HTML to show the search and clear search button.
__construct($db)
Constructor.
select_thirdparty_list($selected='', $htmlname='socid', $filter='', $showempty='', $showtype=0, $forcecombo=0, $events=array(), $filterkey='', $outputmode=0, $limit=0, $morecss='minwidth100', $moreparam='', $multiple=false, $excludeids=array(), $showcode=0)
Output html form to select a third party.
select_users($selected='', $htmlname='userid', $show_empty=0, $exclude=null, $disabled=0, $include='', $enableonly=array(), $force_entity='0')
Return the HTML select list of users.
selectDate($set_time='', $prefix='re', $h=0, $m=0, $empty=0, $form_name="", $d=1, $addnowlink=0, $disabled=0, $fullday='', $addplusone='', $adddateof='', $openinghours='', $stepminutes=1, $labeladddateof='', $placeholder='', $gm='auto', $calendarpicto='')
Show a HTML widget to input a date or combo list for day, month, years and optionally hours and minut...
inputText($htmlName, $value, $morecss='', $moreparam='', $options=array())
Html for HTML area.
select_all_categories($type, $selected='', $htmlname="parent", $maxlength=64, $fromid=0, $outputmode=0, $include=0, $morecss='', $useempty=1)
Return list of categories having chosen type.
textwithpicto($text, $htmltooltip, $direction=1, $type='help', $extracss='valignmiddle', $noencodehtmltext=0, $notabs=3, $tooltiptrigger='', $forcenowrap=0)
Show a text with a picto and a tooltip on picto.
select_date($set_time='', $prefix='re', $h=0, $m=0, $empty=0, $form_name="", $d=1, $addnowlink=0, $nooutput=0, $disabled=0, $fullday=0, $addplusone='', $adddateof='')
Show a HTML widget to input a date or combo list for day, month, years and optionally hours and minut...
load_cache_invoice_subtype()
Load into cache list of invoice subtypes.
makeAddLinkToObject($object, $key, $possiblelink, $num, $resqllist)
Generate HTML table rows for standard object linking (invoices, orders, proposals,...
select_export_model($selected='', $htmlname='exportmodelid', $type='', $useempty=0)
Return list of export templates.
selectDateToDate($set_time='', $set_time_end='', $prefix='re', $empty=0, $forcenewline=0)
Show 2 HTML widget to input a date or combo list for day, month, years and optionally hours and minut...
outputMultiValues($values)
Html for show selected multiple values.
textwithtooltip($text, $htmltext, $tooltipon=1, $direction=0, $img='', $extracss='', $notabs=3, $incbefore='', $noencodehtmltext=0, $tooltiptrigger='', $forcenowrap=0)
Show a text and picto with tooltip on text or picto.
selectCategories($categtype, $htmlname, $object=null)
Return HTML component to select a category.
formconfirm($page, $title, $question, $action, $formquestion='', $selectedchoice='', $useajax=0, $height=0, $width=600, $disableformtag=0, $labelbuttonyes='Yes', $labelbuttonno='No', $helpContent='')
Show a confirmation HTML form or AJAX popup.
outputIcon($value)
Html for show icon.
getHelpBlock($content, $icon='fa-question-circle')
Generate a collapsible help block with a standard '?' icon.
getSelectConditionsPaiements($selected=0, $htmlname='condid', $filtertype=-1, $addempty=0, $noinfoadmin=0, $morecss='', $deposit_percent=-1)
Return list of payment modes.
widgetForTranslation($fieldname, $object, $perm, $typeofdata='string', $check='', $morecss='')
Output edit in place form.
load_cache_types_fees()
Load into cache cache_types_fees, array of types of fees.
outputStars($size, $value)
Html for show stars.
getDurationTypes(Translate $langs, $plurial=true, $reverse=false)
Return an array of Duration Types.
form_thirdparty($page, $selected='', $htmlname='socid', $filter='', $showempty=0, $showtype=0, $forcecombo=0, $events=array(), $nooutput=0, $excludeids=array(), $textifnothirdparty='')
Output html select to select thirdparty.
selectEstablishments($selected='', $htmlname='entity', $status=0, $filtre='', $useempty=0, $moreattrib='')
Return a HTML select list of establishment.
formInputReason($page, $selected='', $htmlname='demandreason', $addempty=0, $morecss='')
Output HTML form to select list of input reason (events that triggered an object creation,...
formSelectAccount($page, $selected='', $htmlname='fk_account', $addempty=0)
Display form to select bank account.
form_users($page, $selected='', $htmlname='userid', $exclude=array(), $include=array())
Show a select form to choose a user.
editfieldkey($text, $htmlname, $preselected, $object, $perm, $typeofdata='string', $moreparam='', $fieldrequired=0, $notabletag=0, $paramid='id', $help='')
Output key field for an editable field.
showCategories($id, $type, $rendermode=0, $nolink=0)
Render list of categories linked to object with id $id and type $type.
load_cache_types_paiements()
Load into the cacha array all possible payment modes.
select_produits_fournisseurs_list($socid, $selected='', $htmlname='productid', $filtertype='', $notused='', $filterkey='', $statut=-1, $outputmode=0, $limit=100, $alsoproductwithnosupplierprice=0, $morecss='', $showstockinlist=0, $placeholder='')
Return list of suppliers products.
makeAddLinkToAttendee($object, $key, $possiblelink, $num, $resqllist)
Generate HTML table rows for conference/booth attendee linking.
selectCurrency($selected='', $htmlname='currency_id', $mode=0, $useempty='')
Returns the list of currencies in the user's language.
selectyesno($htmlname, $value='', $option=0, $disabled=false, $useempty=0, $addjscombo=0, $morecss='yesno width75', $labelyes='Yes', $labelno='No')
Return an html string with a select combo box to choose yes or no.
inputHtml($htmlName, $value, $morecss='', $moreparam='')
Html for HTML area.
select_produits_fournisseurs($socid, $selected='', $htmlname='productid', $filtertype='', $notused='', $ajaxoptions=array(), $hidelabel=0, $alsoproductwithnosupplierprice=0, $morecss='', $placeholder='', $nooutput=0)
Return list of products for customer (in Ajax if Ajax activated or go to select_produits_fournisseurs...
showrefnav($object, $paramid, $morehtml='', $shownav=1, $fieldid='rowid', $fieldref='ref', $morehtmlref='', $moreparam='', $nodbprefix=0, $morehtmlleft='', $morehtmlstatus='', $morehtmlright='')
Return a HTML area with the reference of object and a navigation bar for a business object Note: To c...
searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput=array(), $search_component_params_hidden='', $arrayoffiltercriterias=array())
Output the component to make advanced search criteria.
selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty='', $searchkey='', $placeholder='', $morecss='', $moreparams='', $forcecombo=0, $outputmode=0, $disabled=0, $sortfield='', $filter='', $sortorder='ASC')
Output html form to select an object.
Class to manage a HTML form to send a unitary email Usage: $formail = new FormMail($db) $formmail->pr...
Class to manage building of HTML components.
Class to manage forms for the module resource.
Class to manage a Leaflet map width geometrics objects.
Class to manage hooks.
Class for MyObject.
Class to parse product price expressions.
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_PRODUCT
Regular product.
const TYPE_SERVICE
Service.
Class to manage projects.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage subscriptions of foundation members.
Class to manage translations.
Class to manage Dolibarr users.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:169
getCountry($searchkey, $withcode='', $dbtouse=null, $outputlangs=null, $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
currency_name($code_iso, $withcode=0, $outputlangs=null)
Return label of currency or code+label.
isInEEC($object)
Return if a country of an object is inside the EEC (European Economic Community)
global $mysoc
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition date.lib.php:87
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
removeGlobalParenthesis($string)
Remove first and last parenthesis but only if first is the opening and last the closing of the same g...
dol_now($mode='gmt')
Return date for now.
dol_print_email($email, $contactid=0, $socid=0, $addlink=0, $max=0, $showinvalid=2, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='', $useCache=true)
Return an id or code from a code or id.
dolForgeExplodeAnd($sqlfilters)
Explode an universal search string with AND parts.
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formatted for view output Used into pdf and HTML pages.
dol_print_phone($phone, $countrycode='', $contactid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='paddingright')
Format phone numbers according to country.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
currentToken()
Return the value of token currently saved into session with name 'token'.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='')
Show Url link.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolUserString($key, $default='', $tmpuser=null)
Return Dolibarr user constant string value.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
natural_search($fields, $value, $mode=0, $nofirstand=0, $sqltoadd='')
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getElementProperties($elementType)
Get an array with properties of an element.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into JavaScript code.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags=array('textarea'), $cleanalsosomestyles=0)
Clean a string from some undesirable HTML tags.
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that returns whether VAT must be recoverable collected VAT (e.g.: VAT NPR in France)
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0, $forbiddenfields=array())
forgeSQLFromUniversalSearchCriteria
getImageFileNameForSize($file, $extName, $extImgTarget='')
Return the filename of file to get the thumbs.
colorIsLight($stringcolor)
Return true if the color is light.
dolIsAllowedForPreview($file)
Return if a file is qualified for preview.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
getNonce()
Return a random string to be used as a nonce value for js.
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
dol_htmlwithnojs($stringtoencode, $nouseofiframesandbox=0, $check='restricthtml')
Sanitize a HTML to remove js, dangerous content and external links.
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0, $allowed_tags=array(), $allowlink=0, $allowscript=0, $allowstyle=0, $allowphp=0)
Clean a string to keep only desirable HTML tags.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_sanitizeKeyCode($str)
Clean a string to use it as a key or code.
isModEnabled($module)
Is Dolibarr module enabled.
array_merge_recursive_distinct(array $array1, array $array2)
Recursively merges two arrays while preserving keys and replacing existing values.
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_nboflines_bis($text, $maxlinesize=0, $charset='UTF-8')
Return nb of lines of a formatted text with and (WARNING: string must not have mixed and br sep...
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
a disabled
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
commonHtmlAttributeBuilder($attr, array $unescapedAttr=[])
Builds an array of safe and properly escaped HTML attributes from a key-value pair list.
img_help($usehelpcursor=1, $usealttitle=1)
Show help logo with cursor "?".
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
dolPrintHTML($s, $allowiframe=0, $moreallowedtags=array())
Return a string (that can be on several lines) ready to be output on a HTML page.
Definition html.lib.php:73
dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled='', $morecss='classlink button bordertransp', $jsonopen='', $jsonclose='', $accesskey='')
Return HTML code to output a button to open a dialog popup box.
Definition html.lib.php:415
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
Definition html.lib.php:99
getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
Return URL we can use for advanced preview links.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='', $cssfordropdown='info_admin')
Show information in HTML for admin users or standard users.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
Definition html.lib.php:172
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
Class to generate the form for creating a new ticket.
dol_get_trunk_prefix($db, $phone_code)
Get the national trunk prefix for a phone code.
dol_parse_phone($phone)
Parse a stored phone number into country code and number parts.
Definition phone.lib.php:33
dol_get_phone_code_from_country($db, $country_id)
Get the phone calling code for a country.
measuringUnitString($unitid, $measuring_style='', $unitscale=null, $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition repair.php:133
getMaxFileSizeArray()
Return the max allowed for file upload.
dol_hash($chain, $type='0', $nosalt=0, $mode=0)
Returns a hash (non reversible encryption) of a string.
dolDecrypt($chain, $key='', $patterntotest='')
Decode a string with a symmetric encryption.
testSqlAndScriptInject($val, $type)
Security: WAF layer for SQL Injection and XSS Injection (scripts) protection (Filters on GET,...
Definition waf.inc.php:103