dolibarr 24.0.1
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 // phone_code is an integer column, comparing it to an empty string fails on PostgreSQL
1155 $sql .= " WHERE active > 0 AND phone_code IS NOT NULL AND phone_code != 0";
1156
1157 dol_syslog(get_class($this)."::selectPhoneCode", LOG_DEBUG);
1158 $resql = $this->db->query($sql);
1159 if ($resql) {
1160 $num = $this->db->num_rows($resql);
1161 $i = 0;
1162 while ($i < $num) {
1163 $obj = $this->db->fetch_object($resql);
1164
1165 $translabel = ($obj->code && $langs->transnoentitiesnoconv("Country".$obj->code) != "Country".$obj->code) ? $langs->transnoentitiesnoconv("Country".$obj->code) : $obj->label;
1166
1167 $codeArray[$i]['rowid'] = $obj->rowid;
1168 $codeArray[$i]['code'] = $obj->code;
1169 $codeArray[$i]['label'] = $translabel;
1170 $codeArray[$i]['phone_code'] = '+'.$obj->phone_code;
1171 $codeArray[$i]['favorite'] = $obj->favorite;
1172 $codeArray[$i]['trunk_prefix'] = $obj->trunk_prefix;
1173 $favorite[$i] = $obj->favorite;
1174 $label[$i] = dol_string_unaccent($translabel);
1175 $i++;
1176 }
1177
1178 $array1_sort_order = SORT_DESC;
1179 $array2_sort_order = SORT_ASC;
1180 array_multisort($favorite, $array1_sort_order, $label, $array2_sort_order, $codeArray);
1181
1182 $out .= '<select id="select'.$htmlname.'" class="flat selectphonecode'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
1183
1184 if ($showempty) {
1185 $out .= '<option value="">&nbsp;</option>'."\n";
1186 }
1187
1188 // Determine which row index to select: prefer country_id_hint match, fallback to first phone_code match
1189 $selectedIdx = -1;
1190 $firstMatchIdx = -1;
1191 if ($selected !== '') {
1192 foreach ($codeArray as $idx => $row) {
1193 if ($row['phone_code'] == $selected) {
1194 if ($firstMatchIdx < 0) {
1195 $firstMatchIdx = $idx;
1196 }
1197 if ($country_id_hint > 0 && $row['rowid'] == $country_id_hint) {
1198 $selectedIdx = $idx;
1199 break;
1200 }
1201 }
1202 }
1203 if ($selectedIdx < 0 && $firstMatchIdx >= 0) {
1204 $selectedIdx = $firstMatchIdx;
1205 }
1206 }
1207
1208 foreach ($codeArray as $idx => $row) {
1209 if (empty($row['code'])) {
1210 continue;
1211 }
1212
1213 if ($row['favorite']) {
1214 $atleastonefavorite++;
1215 }
1216 if (empty($row['favorite']) && $atleastonefavorite) {
1217 $atleastonefavorite = 0;
1218 $out .= '<option value="" disabled class="selectoptiondisabledwhite">------------</option>';
1219 }
1220
1221 $tmpflag = picto_from_langcode($row['code'], 'class="saturatemedium paddingrightonly"', 1);
1222
1223 // Short label for selected display: flag + country code
1224 $selectlabel = ($tmpflag ? $tmpflag.' ' : '').$row['code'];
1225
1226 // Detailed label for dropdown list: flag + country name + phone code
1227 $labeltoshow = ($tmpflag ? $tmpflag.' ' : '').$row['label'].' '.$row['phone_code'];
1228
1229 $out .= '<option value="'.dol_escape_htmltag($row['phone_code']).'"';
1230 if ($idx === $selectedIdx) {
1231 $out .= ' selected';
1232 }
1233 $out .= ' data-html="'.dol_escape_htmltag($labeltoshow).'"';
1234 $out .= ' data-select-html="'.dol_escape_htmltag($selectlabel).'"';
1235 $out .= ' data-country-id="'.((int) $row['rowid']).'"';
1236 $out .= ' data-trunk-prefix="'.dol_escape_htmltag((string) $row['trunk_prefix']).'"';
1237 $out .= '>';
1238 $out .= dol_string_nohtmltag($labeltoshow);
1239 $out .= '</option>'."\n";
1240 }
1241 $out .= '</select>';
1242 } else {
1243 dol_print_error($this->db);
1244 }
1245
1246 // Make select dynamic
1247 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
1248 $out .= ajax_combobox('select'.$htmlname, array(), 0, 0, 'resolve');
1249
1250 return $out;
1251 }
1252
1269 public function showPhoneInput($phoneValue, $htmlname, $country_id_hint = 0, $picto = 'object_phoning', $morecss = 'maxwidth150', $maxlength = 0, $countrySelectorId = 'selectcountry_id')
1270 {
1271 global $mysoc;
1272
1273 include_once DOL_DOCUMENT_ROOT.'/core/lib/phone.lib.php';
1274
1275 $codename = $htmlname.'_code';
1276
1277 // Fallback country_id: use caller hint, else main company country
1278 if (empty($country_id_hint) && !empty($mysoc->country_id)) {
1279 $country_id_hint = $mysoc->country_id;
1280 }
1281
1282 // On POST re-display, read the hidden field (which contains the full phone string)
1283 if (GETPOSTISSET($htmlname)) {
1284 $fullPhone = (string) GETPOST($htmlname);
1285 } else {
1286 $fullPhone = (string) $phoneValue;
1287 }
1288
1289 // Split into code + number
1290 $parsed = dol_parse_phone($fullPhone);
1291
1292 // Resolve default phone code: parsed code if set, else from country hint
1293 $phonecode = !empty($parsed['code']) ? $parsed['code'] : dol_get_phone_code_from_country($this->db, $country_id_hint);
1294
1295 $selectedCode = $phonecode;
1296 $numberValue = $parsed['number'];
1297
1298 // Add back trunk prefix for display (e.g. "644986885" → "0644986885" for France)
1299 if ($numberValue !== '' && $selectedCode !== '') {
1300 $trunkPrefix = dol_get_trunk_prefix($this->db, $selectedCode);
1301 if ($trunkPrefix !== '' && strpos($numberValue, $trunkPrefix) !== 0) {
1302 $numberValue = $trunkPrefix.$numberValue;
1303 }
1304 }
1305
1306 // Build output: hidden field (POSTed value)
1307 $out = '<input type="hidden" name="'.dol_escape_htmltag($htmlname).'" id="'.dol_escape_htmltag($htmlname).'" value="'.dol_escape_htmltag($fullPhone).'">';
1308
1309 // Picto
1310 $out .= img_picto('', $picto, 'class="pictofixedwidth"');
1311
1312 // Phone code select (display-only name, not submitted as separate POST param)
1313 $out .= $this->selectPhoneCode($selectedCode, $codename, 'maxwidth75 phone_code_select', 0, $country_id_hint);
1314
1315 // Visible number input (no name — not POSTed)
1316 $out .= '<input type="tel" inputmode="numeric" pattern="[0-9]*" id="'.dol_escape_htmltag($htmlname).'_input" class="'.dol_escape_htmltag($morecss).'"';
1317 if ($maxlength > 0) {
1318 $out .= ' maxlength="'.$maxlength.'"';
1319 }
1320 $out .= ' value="'.dol_escape_htmltag($numberValue).'">';
1321
1322 // Per-field JS to sync hidden field
1323 $out .= $this->getPhoneInputFieldJs($htmlname, $codename);
1324
1325 // Shared JS for country-sync (output once per page)
1326 $out .= $this->getPhoneInputSharedJs($countrySelectorId);
1327
1328 return $out;
1329 }
1330
1341 private function getPhoneInputFieldJs($htmlname, $codename)
1342 {
1343 $hiddenId = dol_escape_js($htmlname);
1344 $inputId = dol_escape_js($htmlname).'_input';
1345 $selectId = 'select'.dol_escape_js($codename);
1346
1347 $out = "\n".'<script type="text/javascript">'."\n";
1348 $out .= 'jQuery(document).ready(function() {'."\n";
1349 $out .= ' function syncPhoneField_'.$hiddenId.'() {'."\n";
1350 $out .= ' var selectEl = jQuery("#'.$selectId.'");'."\n";
1351 $out .= ' var code = selectEl.val() || "";'."\n";
1352 $out .= ' var number = (jQuery("#'.$inputId.'").val() || "").replace(/[^0-9]/g, "");'."\n";
1353 $out .= ' if (code && number) {'."\n";
1354 $out .= ' var selOpt = selectEl[0] && selectEl[0].selectedOptions && selectEl[0].selectedOptions[0];'."\n";
1355 $out .= ' var trunkPrefix = selOpt ? (selOpt.getAttribute("data-trunk-prefix") || "") : "";'."\n";
1356 $out .= ' if (trunkPrefix !== "" && number.indexOf(trunkPrefix) === 0) {'."\n";
1357 $out .= ' number = number.substring(trunkPrefix.length);'."\n";
1358 $out .= ' }'."\n";
1359 $out .= ' jQuery("#'.$hiddenId.'").val(code + " " + number);'."\n";
1360 $out .= ' } else if (number) {'."\n";
1361 $out .= ' jQuery("#'.$hiddenId.'").val(number);'."\n";
1362 $out .= ' } else {'."\n";
1363 $out .= ' jQuery("#'.$hiddenId.'").val("");'."\n";
1364 $out .= ' }'."\n";
1365 $out .= ' }'."\n";
1366 $out .= ' jQuery("#'.$selectId.'").on("change", function() { syncPhoneField_'.$hiddenId.'(); });'."\n";
1367 $out .= ' jQuery("#'.$inputId.'").on("input change", function() { syncPhoneField_'.$hiddenId.'(); });'."\n";
1368 $out .= '});'."\n";
1369 $out .= '</script>'."\n";
1370
1371 return $out;
1372 }
1373
1383 private function getPhoneInputSharedJs($countrySelectorId)
1384 {
1385 if ($this->phoneInputSharedJsLoaded) {
1386 return '';
1387 }
1388 $this->phoneInputSharedJsLoaded = true;
1389
1390 $out = "\n".'<script type="text/javascript">'."\n";
1391 $out .= 'jQuery(document).ready(function() {'."\n";
1392 $out .= ' jQuery("#'.dol_escape_js($countrySelectorId).'").on("change", function() {'."\n";
1393 $out .= ' var country_id = jQuery(this).val();'."\n";
1394 $out .= ' if (country_id) {'."\n";
1395 $out .= ' jQuery.getJSON("'.DOL_URL_ROOT.'/core/ajax/getphonecode.php", {country_id: country_id, token: "'.currentToken().'"}, function(data) {'."\n";
1396 $out .= ' if (data.phone_code) {'."\n";
1397 $out .= ' jQuery(".phone_code_select").each(function() {'."\n";
1398 $out .= ' jQuery(this).val(data.phone_code).trigger("change");'."\n";
1399 $out .= ' });'."\n";
1400 $out .= ' }'."\n";
1401 $out .= ' });'."\n";
1402 $out .= ' }'."\n";
1403 $out .= ' });'."\n";
1404 $out .= '});'."\n";
1405 $out .= '</script>'."\n";
1406
1407 return $out;
1408 }
1409
1423 private function makeAddLinkToObject($object, $key, $possiblelink, $num, $resqllist)
1424 {
1425 dol_syslog(__METHOD__, LOG_DEBUG);
1426 global $langs, $form;
1427 if (empty($form)) {
1428 $form = new Form($this->db);
1429 }
1430 $htmltoenteralink = '';
1431 $i = 0;
1432
1433 // headers
1434 $htmltoenteralink .= '<tr class="liste_titre">';
1435 $htmltoenteralink .= '<td class="nowrap"></td>';
1436 $htmltoenteralink .= '<td>' . $langs->trans("Ref") . '</td>';
1437 $htmltoenteralink .= '<td>' . $langs->trans("RefCustomer") . '</td>';
1438 $htmltoenteralink .= '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
1439 $htmltoenteralink .= '<td>' . $langs->trans("Company") . '</td>';
1440 $htmltoenteralink .= '</tr>';
1441
1442 // rows with data
1443 while ($i < $num) {
1444 $objp = $this->db->fetch_object($resqllist);
1445 $alreadylinked = false;
1446 if (!empty($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key])) {
1447 if (in_array($objp->rowid, array_values($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key]))) {
1448 $alreadylinked = true;
1449 }
1450 }
1451 $htmltoenteralink .= '<tr class="oddeven">';
1452 $htmltoenteralink .= '<td>';
1453 if ($alreadylinked) {
1454 $htmltoenteralink .= img_picto('', 'link');
1455 } else {
1456 $htmltoenteralink .= '<input type="checkbox" name="idtolinkto[' . $key . '_' . $objp->rowid . ']" id="' . $key . '_' . $objp->rowid . '" value="' . $objp->rowid . '">';
1457 }
1458 $htmltoenteralink .= '</td>';
1459 $htmltoenteralink .= '<td>';
1460 if (!$alreadylinked) {
1461 $htmltoenteralink .= '<label for="' . $key . '_' . $objp->rowid . '">';
1462 }
1463 $htmltoenteralink .= $objp->ref;
1464 if (!$alreadylinked) {
1465 $htmltoenteralink .= '</label>';
1466 }
1467 $htmltoenteralink .= '</td>';
1468 $htmltoenteralink .= '<td>' . (!empty($objp->ref_client) ? $objp->ref_client : (!empty($objp->ref_supplier) ? $objp->ref_supplier : '')) . '</td>';
1469 $htmltoenteralink .= '<td class="right">';
1470 if ($possiblelink['label'] == 'LinkToContract') {
1471 $htmltoenteralink .= $form->textwithpicto('', $langs->trans("InformationOnLinkToContract")) . ' ';
1472 }
1473 $htmltoenteralink .= '<span class="amount">' . (isset($objp->total_ht) ? price($objp->total_ht) : '') . '</span>';
1474 $htmltoenteralink .= '</td>';
1475 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1476 $htmltoenteralink .= '</tr>';
1477 $i++;
1478 }
1479
1480 return $htmltoenteralink;
1481 }
1482
1497 private function makeAddLinkToAttendee($object, $key, $possiblelink, $num, $resqllist)
1498 {
1499 dol_syslog(__METHOD__, LOG_DEBUG);
1500 global $langs, $form;
1501 require_once DOL_DOCUMENT_ROOT . '/eventorganization/class/conferenceorboothattendee.class.php';
1502 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1503 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
1504 $attendeestatic = new ConferenceOrBoothAttendee($this->db);
1505 $companystatic = new Societe($this->db);
1506 $projectstatic = new Project($this->db);
1507 if (empty($form)) {
1508 $form = new Form($this->db);
1509 }
1510 $htmltoenteralink = '';
1511 $i = 0;
1512
1513 // headers
1514 $htmltoenteralink .= '<tr class="liste_titre">';
1515 $htmltoenteralink .= '<td class="nowrap"></td>';
1516 $htmltoenteralink .= '<td>' . $langs->trans("Ref") . '</td>';
1517 $htmltoenteralink .= '<td>' . $langs->trans("Name") . '</td>';
1518 $htmltoenteralink .= '<td>' . $langs->trans("Email") . '</td>';
1519 $htmltoenteralink .= '<td>' . $langs->trans("Company") . '</td>';
1520 $htmltoenteralink .= '<td>' . $langs->trans("Project") . '</td>';
1521 $htmltoenteralink .= '<td>' . $langs->trans("DateOfRegistration") . '</td>';
1522 $htmltoenteralink .= '</tr>';
1523
1524 // rows with data
1525 while ($i < $num) {
1526 $objp = $this->db->fetch_object($resqllist);
1527 $alreadylinked = false;
1528 if (!empty($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key])) {
1529 if (in_array($objp->rowid, array_values($object->linkedObjectsIds[$possiblelink['linkname'] ?? $key]))) {
1530 $alreadylinked = true;
1531 }
1532 }
1533 $htmltoenteralink .= '<tr class="oddeven">';
1534 $htmltoenteralink .= '<td>';
1535 if ($alreadylinked) {
1536 $htmltoenteralink .= img_picto('', 'link');
1537 } else {
1538 $htmltoenteralink .= '<input type="checkbox" name="idtolinkto[' . $key . '_' . $objp->rowid . ']" id="' . $key . '_' . $objp->rowid . '" value="' . $objp->rowid . '">';
1539 }
1540 $htmltoenteralink .= '</td>';
1541 $fetchattendee = $attendeestatic->fetch($objp->rowid);
1542 if ($fetchattendee) {
1543 $htmltoenteralink .= '<td>' . $attendeestatic->getNomUrl(0). '</td>';
1544 } else {
1545 $htmltoenteralink .= '<td><label for="' . $key . '_' . $objp->rowid . '">' . $objp->ref . '</label></td>';
1546 }
1547 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1548 $htmltoenteralink .= '<td>' . $objp->email . '</td>';
1549 $fetchcompany = $companystatic->fetch($objp->socid);
1550 if ($fetchcompany) {
1551 $htmltoenteralink .= '<td>' . $companystatic->getNomUrl(0). '</td>';
1552 } else {
1553 $htmltoenteralink .= '<td>' . $objp->name . '</td>';
1554 }
1555 $fetchcproject = $projectstatic->fetch($objp->fk_project);
1556 if ($fetchcproject) {
1557 $htmltoenteralink .= '<td>' . $projectstatic->getNomUrl(0). '</td>';
1558 } else {
1559 $htmltoenteralink .= '<td>' . $objp->fk_project . '</td>';
1560 }
1561 $htmltoenteralink .= '<td>' . $objp->date_subscription . '</td>';
1562 $htmltoenteralink .= '</tr>';
1563 $i++;
1564 }
1565
1566 return $htmltoenteralink;
1567 }
1568
1569 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1570
1584 public function select_incoterms($selected = '', $location_incoterms = '', $page = '', $htmlname = 'incoterm_id', $htmloption = '', $forcecombo = 1, $events = array(), $disableautocomplete = 0)
1585 {
1586 // phpcs:enable
1587 global $conf, $langs;
1588
1589 $langs->load("dict");
1590
1591 $out = '';
1592 //$moreattrib = '';
1593 $incotermArray = array();
1594
1595 $sql = "SELECT rowid, code";
1596 $sql .= " FROM " . $this->db->prefix() . "c_incoterms";
1597 $sql .= " WHERE active > 0";
1598 $sql .= " ORDER BY code ASC";
1599
1600 dol_syslog(get_class($this) . "::select_incoterm", LOG_DEBUG);
1601 $resql = $this->db->query($sql);
1602 if ($resql) {
1603 if ($conf->use_javascript_ajax && !$forcecombo) {
1604 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1605 $out .= ajax_combobox($htmlname, $events);
1606 }
1607
1608 if (!empty($page)) {
1609 $out .= '<form method="post" action="' . $page . '">';
1610 $out .= '<input type="hidden" name="action" value="set_incoterms">';
1611 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
1612 }
1613
1614 $out .= '<select id="' . $htmlname . '" class="flat selectincoterm width75" name="' . $htmlname . '" ' . $htmloption . '>';
1615 $out .= '<option value="0">&nbsp;</option>';
1616 $num = $this->db->num_rows($resql);
1617 $i = 0;
1618 if ($num) {
1619 while ($i < $num) {
1620 $obj = $this->db->fetch_object($resql);
1621 $incotermArray[$i]['rowid'] = $obj->rowid;
1622 $incotermArray[$i]['code'] = $obj->code;
1623 $i++;
1624 }
1625
1626 foreach ($incotermArray as $row) {
1627 if ($selected && ($selected == $row['rowid'] || $selected == $row['code'])) {
1628 $out .= '<option value="' . $row['rowid'] . '" selected>';
1629 } else {
1630 $out .= '<option value="' . $row['rowid'] . '">';
1631 }
1632
1633 if ($row['code']) {
1634 $out .= $row['code'];
1635 }
1636
1637 $out .= '</option>';
1638 }
1639 }
1640 $out .= '</select>';
1641 $out .= ajax_combobox($htmlname);
1642
1643 if ($conf->use_javascript_ajax && empty($disableautocomplete)) {
1644 $out .= ajax_multiautocompleter('location_incoterms', array(), DOL_URL_ROOT . '/core/ajax/locationincoterms.php') . "\n";
1645 //$moreattrib .= ' autocomplete="off"';
1646 }
1647 $out .= '<input id="location_incoterms" class="maxwidthonsmartphone heightofcombo" type="text" name="location_incoterms" value="' . $location_incoterms . '">' . "\n";
1648
1649 if (!empty($page)) {
1650 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp nomargintop nomarginbottom" value="' . $langs->trans("Modify") . '"></form>';
1651 }
1652 } else {
1653 dol_print_error($this->db);
1654 }
1655
1656 return $out;
1657 }
1658
1659 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1660
1674 public function select_type_of_lines($selected = '', $htmlname = 'type', $showempty = 0, $hidetext = 0, $forceall = 0, $morecss = "", $useajaxcombo = 1)
1675 {
1676 // phpcs:enable
1677 global $langs;
1678
1679 // If product & services are enabled or both disabled.
1680 if ($forceall == 1 || (empty($forceall) && isModEnabled("product") && isModEnabled("service"))
1681 || (empty($forceall) && !isModEnabled('product') && !isModEnabled('service'))) {
1682 if (empty($hidetext)) {
1683 print $langs->trans("Type").'...';
1684 }
1685
1686 print '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $htmlname . '" name="' . $htmlname . '">';
1687 if ($showempty) {
1688 print '<option value="-1" class="opacitymedium"'.($useajaxcombo ? '' : ' disabled="disabled"');
1689 if ($selected == -1) {
1690 print ' selected';
1691 }
1692 print '>';
1693 if (is_numeric($showempty)) {
1694 print '&nbsp;';
1695 } else {
1696 print $showempty;
1697 }
1698 print '</option>';
1699 }
1700
1701 print '<option value="0"';
1702 if (0 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'product')) {
1703 print ' selected';
1704 }
1705 print '>' . $langs->trans("Product");
1706 print '</option>';
1707
1708 print '<option value="1"';
1709 if (1 == $selected || ($selected == -1 && getDolGlobalString('MAIN_FREE_PRODUCT_CHECKED_BY_DEFAULT') == 'service')) {
1710 print ' selected';
1711 }
1712 print '>' . $langs->trans("Service");
1713 print '</option>';
1714
1715 print '</select>';
1716
1717 if ($useajaxcombo) {
1718 print ajax_combobox('select_' . $htmlname);
1719 }
1720 //if ($user->admin) print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"),1);
1721 }
1722 if ((empty($forceall) && !isModEnabled('product') && isModEnabled("service")) || $forceall == 3) {
1723 print $langs->trans("Service");
1724 print '<input type="hidden" name="' . $htmlname . '" value="1">';
1725 }
1726 if ((empty($forceall) && isModEnabled("product") && !isModEnabled('service')) || $forceall == 2) {
1727 print $langs->trans("Product");
1728 print '<input type="hidden" name="' . $htmlname . '" value="0">';
1729 }
1730 if ($forceall < 0) { // This should happened only for contracts when both predefined product and service are disabled.
1731 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
1732 }
1733 }
1734
1735 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1736
1742 public function load_cache_types_fees()
1743 {
1744 // phpcs:enable
1745 global $langs;
1746
1747 $num = count($this->cache_types_fees);
1748 if ($num > 0) {
1749 return 0; // Cache already loaded
1750 }
1751
1752 dol_syslog(__METHOD__, LOG_DEBUG);
1753
1754 $langs->load("trips");
1755
1756 $sql = "SELECT c.code, c.label";
1757 $sql .= " FROM " . $this->db->prefix() . "c_type_fees as c";
1758 $sql .= " WHERE active > 0";
1759
1760 $resql = $this->db->query($sql);
1761 if ($resql) {
1762 $num = $this->db->num_rows($resql);
1763 $i = 0;
1764
1765 while ($i < $num) {
1766 $obj = $this->db->fetch_object($resql);
1767
1768 // If a translation exists, we use is, otherwise, we take the label by default
1769 $label = ($obj->code != $langs->trans($obj->code) ? $langs->trans($obj->code) : $langs->trans($obj->label));
1770 $this->cache_types_fees[$obj->code] = $label;
1771 $i++;
1772 }
1773
1774 asort($this->cache_types_fees);
1775
1776 return $num;
1777 } else {
1778 dol_print_error($this->db);
1779 return -1;
1780 }
1781 }
1782
1783 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1784
1793 public function select_type_fees($selected = '', $htmlname = 'type', $showempty = 0)
1794 {
1795 // phpcs:enable
1796 global $user, $langs;
1797
1798 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
1799
1800 $this->load_cache_types_fees();
1801
1802 print '<select id="select_' . $htmlname . '" class="flat" name="' . $htmlname . '">';
1803 if ($showempty) {
1804 print '<option value="-1"';
1805 if ($selected == -1) {
1806 print ' selected';
1807 }
1808 print '>&nbsp;</option>';
1809 }
1810
1811 foreach ($this->cache_types_fees as $key => $value) {
1812 print '<option value="' . $key . '"';
1813 if ($key == $selected) {
1814 print ' selected';
1815 }
1816 print '>';
1817 print $value;
1818 print '</option>';
1819 }
1820
1821 print '</select>';
1822 if ($user->admin) {
1823 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1824 }
1825 }
1826
1827
1828 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1829
1852 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)
1853 {
1854 // phpcs:enable
1855 global $conf, $langs;
1856
1857 $out = '';
1858
1859 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1860 if (is_null($ajaxoptions)) {
1861 $ajaxoptions = array();
1862 }
1863
1864 require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1865
1866 // No immediate load of all database
1867 $placeholder = '';
1868 if ($selected && empty($selected_input_value)) {
1869 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1870 $societetmp = new Societe($this->db);
1871 $societetmp->fetch($selected);
1872 $selected_input_value = $societetmp->name;
1873 unset($societetmp);
1874 }
1875
1876 // mode 1
1877 $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 : '');
1878
1879 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1880 if (empty($hidelabel)) {
1881 $out .= $langs->trans("RefOrLabel") . ' : ';
1882 } elseif ($hidelabel == 1 && !is_numeric($showempty)) {
1883 $placeholder = $langs->trans($showempty);
1884 } elseif ($hidelabel > 1) {
1885 $placeholder = $langs->trans("RefOrLabel");
1886 if ($hidelabel == 2) {
1887 $out .= img_picto($langs->trans("Search"), 'search');
1888 }
1889 }
1890 $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" />';
1891 if ($hidelabel == 3) {
1892 $out .= img_picto($langs->trans("Search"), 'search');
1893 }
1894
1895 $out .= ajax_event($htmlname, $events);
1896
1897 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/societe/ajax/company.php', $urloption, getDolGlobalInt('COMPANY_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
1898 } else {
1899 // Immediate load of all database
1900 $out .= $this->select_thirdparty_list($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, '', 0, $limit, $morecss, $moreparam, $multiple, $excludeids, $showcode);
1901 }
1902
1903 return $out;
1904 }
1905
1906
1907 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1908
1934 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 = '')
1935 {
1936 // phpcs:enable
1937
1938 global $conf, $langs;
1939
1940 $out = '';
1941
1942 $sav = getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT');
1943 if ($nokeyifsocid && $socid > 0) {
1944 $conf->global->CONTACT_USE_SEARCH_TO_SELECT = 0;
1945 }
1946
1947 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('CONTACT_USE_SEARCH_TO_SELECT') && !$forcecombo) {
1948 $ajaxoptions = array();
1949
1950 require_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
1951
1952 // No immediate load of all database
1953 $placeholder = '';
1954 if ($selected && empty($selected_input_value)) {
1955 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
1956 $contacttmp = new Contact($this->db);
1957 $contacttmp->fetch($selected);
1958 $selected_input_value = $contacttmp->getFullName($langs);
1959 unset($contacttmp);
1960 }
1961 if (!is_numeric($showempty)) {
1962 $placeholder = $showempty;
1963 }
1964
1965 // mode 1
1966 $urloption = 'htmlname=' . urlencode((string) (str_replace('.', '_', $htmlname))) . '&outjson=1&filter=' . urlencode((string) ($filter)) . (empty($exclude) ? '' : '&exclude=' . urlencode($exclude)) . ($showsoc ? '&showsoc=' . urlencode((string) ($showsoc)) : '');
1967
1968 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
1969
1970 $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" />';
1971
1972 $out .= ajax_event($htmlname, $events);
1973
1974 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT.'/contact/ajax/contact.php', $urloption, getDolGlobalInt('CONTACT_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
1975 } else {
1976 // Immediate load of all database
1977 $multiple = false;
1978 $disableifempty = 0;
1979 $options_only = 0;
1980 $limitto = '';
1981
1982 $out .= $this->selectcontacts($socid, $selected, $htmlname, $showempty, $exclude, $limitto, $showfunction, $morecss, $options_only, $showsoc, $forcecombo, $events, $moreparam, $htmlid, $multiple, $disableifempty);
1983 }
1984
1985 $conf->global->CONTACT_USE_SEARCH_TO_SELECT = $sav;
1986
1987 return $out;
1988 }
1989
1990
1991 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1992
2016 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)
2017 {
2018 // phpcs:enable
2019 global $user, $langs;
2020 global $hookmanager;
2021
2022 $langs->loadLangs(array("companies", "suppliers"));
2023
2024 $out = '';
2025 $num = 0;
2026 $outarray = array();
2027
2028 if ($selected === '') {
2029 $selected = array();
2030 } elseif (!is_array($selected)) {
2031 $selected = array($selected);
2032 }
2033
2034 // Clean $filter that may contains sql conditions so sql code
2035 if (function_exists('testSqlAndScriptInject')) {
2036 if (testSqlAndScriptInject($filter, 3) > 0) {
2037 $filter = '';
2038 return 'SQLInjectionTryDetected';
2039 }
2040 }
2041
2042 if ($filter != '') { // If a filter was provided
2043 $errormsg = '';
2044 $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
2045
2046 // Redo clean $filter that may contains sql conditions so sql code
2047 if (function_exists('testSqlAndScriptInject')) {
2048 if (testSqlAndScriptInject($filter, 3) > 0) {
2049 $filter = '';
2050 return 'SQLInjectionTryDetected';
2051 }
2052 }
2053 }
2054
2055 // We search companies
2056 $sql = "SELECT s.rowid, s.nom as name, s.name_alias, s.tva_intra, s.client, s.fournisseur, s.code_client, s.code_fournisseur";
2057 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2058 $sql .= ", s.address, s.zip, s.town";
2059 $sql .= ", dictp.code as country_code";
2060 }
2061 $sql .= " FROM " . $this->db->prefix() . "societe as s";
2062 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2063 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_country as dictp ON dictp.rowid = s.fk_pays";
2064 }
2065 if (!$user->hasRight('societe', 'client', 'voir')) {
2066 $sql .= ", " . $this->db->prefix() . "societe_commerciaux as sc";
2067 }
2068 $sql .= " WHERE s.entity IN (" . getEntity('societe') . ")";
2069 if (!empty($user->socid)) {
2070 $sql .= " AND s.rowid = " . ((int) $user->socid);
2071 }
2072 if ($filter) {
2073 // $filter is safe because, it has been tested by testSqlAndScriptInject() and sanitized by forgeSQLFromUniversalSearchCriteria()
2074 $sqlwhere = $filter; // @phan-suppress-current-line SqlInjection
2075 $sql .= " AND (" . $sqlwhere . ")";
2076 }
2077 if (!$user->hasRight('societe', 'client', 'voir')) {
2078 $sql .= " AND s.rowid = sc.fk_soc AND sc.fk_user = " . ((int) $user->id);
2079 }
2080 if (getDolGlobalString('COMPANY_HIDE_INACTIVE_IN_COMBOBOX')) {
2081 $sql .= " AND s.status <> 0";
2082 }
2083 if (!empty($excludeids)) {
2084 $sql .= " AND s.rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeids)) . ")";
2085 }
2086 // Add where from hooks
2087 $parameters = array();
2088 $reshook = $hookmanager->executeHooks('selectThirdpartyListWhere', $parameters); // Note that $action and $object may have been modified by hook
2089 $sql .= $hookmanager->resPrint;
2090 // Add criteria
2091 if ($filterkey && $filterkey != '') {
2092 $sql .= " AND (";
2093 $prefix = !getDolGlobalString('COMPANY_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if COMPANY_DONOTSEARCH_ANYWHERE is on
2094 // For natural search
2095 $search_crit = explode(' ', $filterkey);
2096 $i = 0;
2097 if (count($search_crit) > 1) {
2098 $sql .= "(";
2099 }
2100 foreach ($search_crit as $crit) {
2101 if ($i > 0) {
2102 $sql .= " AND ";
2103 }
2104 $sql .= "(s.nom LIKE '" . $this->db->escape($prefix . $crit) . "%')";
2105 $i++;
2106 }
2107 if (count($search_crit) > 1) {
2108 $sql .= ")";
2109 }
2110 if (isModEnabled('barcode')) {
2111 $sql .= " OR s.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2112 }
2113 $sql .= " OR s.code_client LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.code_fournisseur LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2114 $sql .= " OR s.name_alias LIKE '" . $this->db->escape($prefix . $filterkey) . "%' OR s.tva_intra LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
2115 $sql .= ")";
2116 }
2117 $sql .= $this->db->order("nom", "ASC");
2118 $sql .= $this->db->plimit($limit, 0);
2119
2120 // Build output string
2121 dol_syslog(get_class($this)."::select_thirdparty_list", LOG_DEBUG);
2122 $resql = $this->db->query($sql);
2123 if ($resql) {
2124 // Construct $out and $outarray
2125 $out .= '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($moreparam ? ' ' . $moreparam : '') . ' name="' . $htmlname . ($multiple ? '[]' : '') . '"' . ($multiple ? ' multiple' : '') . '>' . "\n";
2126
2127 $textifempty = (($showempty && !is_numeric($showempty)) ? $langs->trans($showempty) : '');
2128 if (getDolGlobalString('COMPANY_USE_SEARCH_TO_SELECT')) {
2129 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
2130 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
2131 if ($showempty && !is_numeric($showempty)) {
2132 $textifempty = $langs->trans($showempty);
2133 } else {
2134 $textifempty .= $langs->trans("All");
2135 }
2136 }
2137 if ($showempty) {
2138 $out .= '<option value="-1" data-html="' . dol_escape_htmltag('<span class="opacitymedium">' . ($textifempty ? $textifempty : '&nbsp;') . '</span>') . '">' . $textifempty . '</option>' . "\n";
2139 }
2140
2141 $companytemp = new Societe($this->db);
2142
2143 $num = $this->db->num_rows($resql);
2144 $i = 0;
2145 if ($num) {
2146 while ($i < $num) {
2147 $obj = $this->db->fetch_object($resql);
2148 $label = '';
2149 if ($showcode || getDolGlobalString('SOCIETE_ADD_REF_IN_LIST')) {
2150 if (($obj->client) && (!empty($obj->code_client))) {
2151 $label = $obj->code_client . ' - ';
2152 }
2153 if (($obj->fournisseur) && (!empty($obj->code_fournisseur))) {
2154 $label .= $obj->code_fournisseur . ' - ';
2155 }
2156 $label .= ' ' . $obj->name;
2157 } else {
2158 $label = $obj->name;
2159 }
2160
2161 if (!empty($obj->name_alias)) {
2162 $label .= ' (' . $obj->name_alias . ')';
2163 }
2164
2165 if (getDolGlobalString('SOCIETE_SHOW_VAT_IN_LIST') && !empty($obj->tva_intra)) {
2166 $label .= ' - '.$obj->tva_intra;
2167 }
2168
2169 $labelhtml = $label;
2170
2171 if ($showtype) {
2172 $companytemp->id = $obj->rowid;
2173 $companytemp->client = $obj->client;
2174 $companytemp->fournisseur = $obj->fournisseur;
2175 $tmptype = $companytemp->getTypeUrl(1, '', 0, 'span');
2176 if ($tmptype) {
2177 $labelhtml .= ' ' . $tmptype;
2178 }
2179
2180 if ($obj->client || $obj->fournisseur) {
2181 $label .= ' (';
2182 }
2183 if ($obj->client == 1 || $obj->client == 3) {
2184 $label .= $langs->trans("Customer");
2185 }
2186 if ($obj->client == 2 || $obj->client == 3) {
2187 $label .= ($obj->client == 3 ? ', ' : '') . $langs->trans("Prospect");
2188 }
2189 if ($obj->fournisseur) {
2190 $label .= ($obj->client ? ', ' : '') . $langs->trans("Supplier");
2191 }
2192 if ($obj->client || $obj->fournisseur) {
2193 $label .= ')';
2194 }
2195 }
2196
2197 if (getDolGlobalString('COMPANY_SHOW_ADDRESS_SELECTLIST')) {
2198 $s = ($obj->address ? ' - ' . $obj->address : '') . ($obj->zip ? ' - ' . $obj->zip : '') . ($obj->town ? ' ' . $obj->town : '');
2199 if (!empty($obj->country_code)) {
2200 $s .= ', ' . $langs->trans('Country' . $obj->country_code);
2201 }
2202 $label .= $s;
2203 $labelhtml .= $s;
2204 }
2205
2206 if (empty($outputmode)) {
2207 if (in_array($obj->rowid, $selected)) {
2208 $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>';
2209 } else {
2210 $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
2211 }
2212 } else {
2213 array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label, 'labelhtml' => $labelhtml));
2214 }
2215
2216 $i++;
2217 if (($i % 10) == 0) {
2218 $out .= "\n";
2219 }
2220 }
2221 }
2222 $out .= '</select>' . "\n";
2223 if (!$forcecombo) {
2224 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2225 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("COMPANY_USE_SEARCH_TO_SELECT"));
2226 }
2227 } else {
2228 dol_print_error($this->db);
2229 }
2230
2231 $this->result = array('nbofthirdparties' => $num);
2232
2233 if ($outputmode) {
2234 return $outarray;
2235 }
2236 return $out;
2237 }
2238
2239
2265 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 = '')
2266 {
2267 global $conf, $user, $langs, $hookmanager, $action;
2268
2269 $langs->load('companies');
2270
2271 if (empty($htmlid)) {
2272 $htmlid = $htmlname;
2273 }
2274 $num = 0;
2275 $out = '';
2276 $outarray = array();
2277
2278 if ($selected === '') {
2279 $selected = array();
2280 } elseif (!is_array($selected)) {
2281 $selected = array((int) $selected);
2282 }
2283
2284 // Clean $filter that may contains sql conditions so sql code
2285 if (function_exists('testSqlAndScriptInject')) {
2286 if (testSqlAndScriptInject($filter, 3) > 0) {
2287 $filter = '';
2288 return 'SQLInjectionTryDetected';
2289 }
2290 }
2291
2292 if ($filter != '') { // If a filter was provided
2293 if (preg_match('/[\‍(\‍)]/', $filter)) {
2294 // If there is one parenthesis inside the criteria, we assume it is an Universal Filter Syntax.
2295 $errormsg = '';
2296 $filter = forgeSQLFromUniversalSearchCriteria($filter, $errormsg, 1);
2297
2298 // Redo clean $filter that may contains sql conditions so sql code
2299 if (function_exists('testSqlAndScriptInject')) {
2300 if (testSqlAndScriptInject($filter, 3) > 0) {
2301 $filter = '';
2302 return 'SQLInjectionTryDetected';
2303 }
2304 }
2305 } else {
2306 // If not, we do nothing. We already know that there is no parenthesis
2307 // TODO Disallow this case in a future by returning an error here.
2308 dol_syslog("Warning, select_thirdparty_list was called with a filter criteria not using the Universal Search Filter Syntax.", LOG_WARNING);
2309 }
2310 }
2311
2312 if (!is_object($hookmanager)) {
2313 include_once DOL_DOCUMENT_ROOT . '/core/class/hookmanager.class.php';
2314 $hookmanager = new HookManager($this->db);
2315 }
2316
2317 // We search third parties
2318 $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";
2319 if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2320 $sql .= ", s.nom as company, s.town AS company_town";
2321 }
2322 $sql .= " FROM " . $this->db->prefix() . "socpeople as sp";
2323 if ($showsoc > 0 || getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2324 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON s.rowid = sp.fk_soc";
2325 }
2326 $sql .= " WHERE sp.entity IN (" . getEntity('contact') . ")";
2327 $sql .= " AND ((sp.fk_user_creat = ".((int) $user->id)." AND sp.priv = 1) OR sp.priv = 0)"; // check if this is a private contact
2328 if ($socid > 0 || $socid == -1) {
2329 $sql .= " AND sp.fk_soc = " . ((int) $socid);
2330 }
2331 if (getDolGlobalString('CONTACT_HIDE_INACTIVE_IN_COMBOBOX')) {
2332 $sql .= " AND sp.statut <> 0";
2333 }
2334 // filter user access
2335 if (!$user->hasRight('societe', 'client', 'voir') && !$user->socid) {
2336 $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 .")";
2337 }
2338 if ($user->socid > 0) {
2339 $sql .= " AND sp.fk_soc = ".((int) $user->socid);
2340 }
2341 if ($filter) {
2342 // $filter is safe because, if it contains '(' or ')', it has been sanitized by testSqlAndScriptInject() and forgeSQLFromUniversalSearchCriteria()
2343 // if not, by testSqlAndScriptInject() only.
2344 $sanitizedfilter = $filter; // @phan-suppress-current-line SqlInjection
2345 $sql .= " AND (" . $sanitizedfilter . ")";
2346 }
2347 // Add where from hooks
2348 $parameters = array();
2349 $reshook = $hookmanager->executeHooks('selectContactListWhere', $parameters); // Note that $action and $object may have been modified by hook
2350 $sql .= $hookmanager->resPrint;
2351 $sql .= " ORDER BY sp.lastname ASC";
2352
2353 dol_syslog(get_class($this) . "::selectcontacts", LOG_DEBUG);
2354 $resql = $this->db->query($sql);
2355 if ($resql) {
2356 $num = $this->db->num_rows($resql);
2357
2358 if ($htmlname != 'none' && !$options_only) {
2359 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlid . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . (($num || empty($disableifempty)) ? '' : ' disabled') . ($multiple ? 'multiple' : '') . ' ' . (!empty($moreparam) ? $moreparam : '') . '>';
2360 }
2361
2362 if ($showempty && !is_numeric($showempty)) {
2363 $textforempty = $showempty;
2364 $out .= '<option class="optiongrey" value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>' . $textforempty . '</option>';
2365 } else {
2366 if (($showempty == 1 || ($showempty == 3 && $num > 1)) && !$multiple) {
2367 $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>&nbsp;</option>';
2368 }
2369 if ($showempty == 2) {
2370 $out .= '<option value="0"' . (in_array(0, $selected) ? ' selected' : '') . '>-- ' . $langs->trans("Internal") . ' --</option>';
2371 }
2372 }
2373
2374 $i = 0;
2375 if ($num) {
2376 include_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
2377 $contactstatic = new Contact($this->db);
2378
2379 while ($i < $num) {
2380 $obj = $this->db->fetch_object($resql);
2381
2382 // Set email (or phones) and town extended infos
2383 $extendedInfos = '';
2384 if (getDolGlobalString('CONTACT_SHOW_EMAIL_PHONE_TOWN_SELECTLIST')) {
2385 $extendedInfos = array();
2386 $email = trim($obj->email);
2387 if (!empty($email)) {
2388 $extendedInfos[] = $email;
2389 } else {
2390 $phone = trim($obj->phone);
2391 $phone_perso = trim($obj->phone_perso);
2392 $phone_mobile = trim($obj->phone_mobile);
2393 if (!empty($phone)) {
2394 $extendedInfos[] = $phone;
2395 }
2396 if (!empty($phone_perso)) {
2397 $extendedInfos[] = $phone_perso;
2398 }
2399 if (!empty($phone_mobile)) {
2400 $extendedInfos[] = $phone_mobile;
2401 }
2402 }
2403 $contact_town = trim($obj->contact_town);
2404 $company_town = trim($obj->company_town);
2405 if (!empty($contact_town)) {
2406 $extendedInfos[] = $contact_town;
2407 } elseif (!empty($company_town)) {
2408 $extendedInfos[] = $company_town;
2409 }
2410 $extendedInfos = implode(' - ', $extendedInfos);
2411 if (!empty($extendedInfos)) {
2412 $extendedInfos = ' - ' . $extendedInfos;
2413 }
2414 }
2415
2416 $contactstatic->id = $obj->rowid;
2417 $contactstatic->lastname = $obj->lastname;
2418 $contactstatic->firstname = $obj->firstname;
2419 if ($obj->statut == 1) {
2420 $tmplabel = '';
2421 if ($htmlname != 'none') {
2422 $disabled = 0;
2423 if (is_array($exclude) && count($exclude) && in_array($obj->rowid, $exclude)) {
2424 $disabled = 1;
2425 }
2426 if (is_array($limitto) && count($limitto) && !in_array($obj->rowid, $limitto)) {
2427 $disabled = 1;
2428 }
2429 if (!empty($selected) && in_array($obj->rowid, $selected)) {
2430 $out .= '<option value="' . $obj->rowid . '"';
2431 if ($disabled) {
2432 $out .= ' disabled';
2433 }
2434 $out .= ' selected>';
2435
2436 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2437 if ($showfunction && $obj->poste) {
2438 $tmplabel .= ' (' . $obj->poste . ')';
2439 }
2440 if (($showsoc > 0) && $obj->company) {
2441 $tmplabel .= ' - (' . $obj->company . ')';
2442 }
2443
2444 $out .= $tmplabel;
2445 $out .= '</option>';
2446 } else {
2447 $out .= '<option value="' . $obj->rowid . '"';
2448 if ($disabled) {
2449 $out .= ' disabled';
2450 }
2451 $out .= '>';
2452
2453 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2454 if ($showfunction && $obj->poste) {
2455 $tmplabel .= ' (' . $obj->poste . ')';
2456 }
2457 if (($showsoc > 0) && $obj->company) {
2458 $tmplabel .= ' - (' . $obj->company . ')';
2459 }
2460
2461 $out .= $tmplabel;
2462 $out .= '</option>';
2463 }
2464 } else {
2465 if (in_array($obj->rowid, $selected)) {
2466 $tmplabel = $contactstatic->getFullName($langs) . $extendedInfos;
2467 if ($showfunction && $obj->poste) {
2468 $tmplabel .= ' (' . $obj->poste . ')';
2469 }
2470 if (($showsoc > 0) && $obj->company) {
2471 $tmplabel .= ' - (' . $obj->company . ')';
2472 }
2473
2474 $out .= $tmplabel;
2475 }
2476 }
2477
2478 if ($tmplabel != '') {
2479 array_push($outarray, array('key' => $obj->rowid, 'value' => $tmplabel, 'label' => $tmplabel, 'labelhtml' => $tmplabel));
2480 }
2481 }
2482 $i++;
2483 }
2484 } else {
2485 $labeltoshow = ($socid != -1) ? ($langs->trans($socid ? "NoContactDefinedForThirdParty" : "NoContactDefined")) : $langs->trans('SelectAThirdPartyFirst');
2486 $out .= '<option class="disabled" value="-1"' . (($showempty == 2 || $multiple) ? '' : ' selected') . ' disabled="disabled">';
2487 $out .= $labeltoshow;
2488 $out .= '</option>';
2489 }
2490
2491 $parameters = array(
2492 'socid' => $socid,
2493 'htmlname' => $htmlname,
2494 'resql' => $resql,
2495 'out' => &$out,
2496 'showfunction' => $showfunction,
2497 'showsoc' => $showsoc,
2498 );
2499
2500 $reshook = $hookmanager->executeHooks('afterSelectContactOptions', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2501
2502 if ($htmlname != 'none' && !$options_only) {
2503 $out .= '</select>';
2504 }
2505
2506 if ($conf->use_javascript_ajax && !$forcecombo && !$options_only) {
2507 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2508 $out .= ajax_combobox($htmlid, $events, getDolGlobalInt("CONTACT_USE_SEARCH_TO_SELECT"));
2509 }
2510
2511 $this->num = $num;
2512
2513 if ($options_only === 2) {
2514 // Return array of options
2515 return $outarray;
2516 } else {
2517 return $out;
2518 }
2519 } else {
2520 dol_print_error($this->db);
2521 return -1;
2522 }
2523 }
2524
2525
2526 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2527
2538 public function select_remises($selected, $htmlname, $filter, $socid, $maxvalue = 0)
2539 {
2540 // phpcs:enable
2541 global $langs, $conf;
2542
2543 // On recherche les remises
2544 $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
2545 $sql .= " re.description, re.fk_facture_source";
2546 $sql .= " FROM " . $this->db->prefix() . "societe_remise_except as re";
2547 $sql .= " WHERE re.fk_soc = " . (int) $socid;
2548 $sql .= " AND re.entity = " . ((int) $conf->entity);
2549 if ($filter) {
2550 $sanitizedfilter = $filter; // @phan-suppress-current-line SqlInjection
2551 $sql .= " AND " . $sanitizedfilter;
2552 }
2553 $sql .= " ORDER BY re.description ASC";
2554
2555 dol_syslog(get_class($this) . "::select_remises", LOG_DEBUG);
2556 $resql = $this->db->query($sql);
2557 if ($resql) {
2558 print '<select id="select_' . $htmlname . '" class="flat maxwidth200onsmartphone" name="' . $htmlname . '">';
2559 $num = $this->db->num_rows($resql);
2560
2561 $qualifiedlines = $num;
2562
2563 $i = 0;
2564 if ($num) {
2565 print '<option value="0">&nbsp;</option>';
2566 while ($i < $num) {
2567 $obj = $this->db->fetch_object($resql);
2568 $desc = dol_trunc($obj->description, 40);
2569 if (preg_match('/\‍(CREDIT_NOTE\‍)/', $desc)) {
2570 $desc = preg_replace('/\‍(CREDIT_NOTE\‍)/', $langs->trans("CreditNote"), $desc);
2571 }
2572 if (preg_match('/\‍(DEPOSIT\‍)/', $desc)) {
2573 $desc = preg_replace('/\‍(DEPOSIT\‍)/', $langs->trans("Deposit"), $desc);
2574 }
2575 if (preg_match('/\‍(EXCESS RECEIVED\‍)/', $desc)) {
2576 $desc = preg_replace('/\‍(EXCESS RECEIVED\‍)/', $langs->trans("ExcessReceived"), $desc);
2577 }
2578 if (preg_match('/\‍(EXCESS PAID\‍)/', $desc)) {
2579 $desc = preg_replace('/\‍(EXCESS PAID\‍)/', $langs->trans("ExcessPaid"), $desc);
2580 }
2581
2582 $selectstring = '';
2583 if ($selected > 0 && $selected == $obj->rowid) {
2584 $selectstring = ' selected';
2585 }
2586
2587 $disabled = '';
2588 if ($maxvalue > 0 && $obj->amount_ttc > $maxvalue) {
2589 $qualifiedlines--;
2590 $disabled = ' disabled';
2591 }
2592
2593 if (getDolGlobalString('MAIN_SHOW_FACNUMBER_IN_DISCOUNT_LIST') && !empty($obj->fk_facture_source)) {
2594 $tmpfac = new Facture($this->db);
2595 if ($tmpfac->fetch($obj->fk_facture_source) > 0) {
2596 $desc = $desc . ' - ' . $tmpfac->ref;
2597 }
2598 }
2599
2600 print '<option value="' . $obj->rowid . '"' . $selectstring . $disabled . '>' . $desc . ' (' . price($obj->amount_ht) . ' ' . $langs->trans("HT") . ' - ' . price($obj->amount_ttc) . ' ' . $langs->trans("TTC") . ')</option>';
2601 $i++;
2602 }
2603 }
2604 print '</select>';
2605 print ajax_combobox('select_' . $htmlname);
2606
2607 return $qualifiedlines;
2608 } else {
2609 dol_print_error($this->db);
2610 return -1;
2611 }
2612 }
2613
2614
2615 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2616
2632 public function select_users($selected = '', $htmlname = 'userid', $show_empty = 0, $exclude = null, $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0')
2633 {
2634 // phpcs:enable
2635 print $this->select_dolusers($selected, $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity);
2636 }
2637
2638 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2639
2664 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)
2665 {
2666 // phpcs:enable
2667 global $conf, $user, $langs, $hookmanager;
2668 global $action;
2669
2670 // Convert $selected into an int (in case it is an object)
2671 if (is_object($userselected)) {
2672 $selected = (int) $userselected->id;
2673 } elseif (is_numeric($userselected)) {
2674 $selected = (int) $userselected;
2675 } elseif (is_array($userselected)) {
2676 $selected = $userselected;
2677 } else {
2678 $selected = -1;
2679 }
2680
2681 // If no preselected user defined, we take current user
2682 if ((is_numeric($selected) && ((int) $selected < -4 || empty($selected))) && !getDolGlobalString('SOCIETE_DISABLE_DEFAULT_SALESREPRESENTATIVE')) {
2683 $selected = $user->id;
2684 }
2685
2686 // Convert selected int into an array
2687 if (!is_array($selected)) {
2688 if ($selected === -1 || $selected === '') {
2689 $selected = array();
2690 } else {
2691 $selected = array($selected);
2692 }
2693 }
2694
2695 // Exclude some users in $excludeUsers string
2696 $excludeUsers = null;
2697 if (is_array($exclude)) {
2698 $excludeUsers = implode(",", $exclude);
2699 }
2700
2701 // Include some users in $includeUsers string
2702 $includeUsers = null;
2703 $includeUsersArray = array();
2704 if (is_array($include)) {
2705 $includeUsersArray = $include;
2706 } elseif ($include == 'hierarchy') {
2707 // Build list includeUsersArray to have only hierarchy
2708 $includeUsersArray = $user->getAllChildIds(0);
2709 } elseif ($include == 'hierarchyme') {
2710 // Build list includeUsersArray to have only hierarchy and current user
2711 $includeUsersArray = $user->getAllChildIds(1);
2712 }
2713 // Get list of allowed users
2714 /* 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
2715 * see all other users and element in other. For example in agenda, we can have permission to read all event of otherusers.
2716 * So we disable this.
2717 if (!$user->hasRight('user', 'user', 'lire')) {
2718 if (empty($includeUsersArray)) {
2719 $includeUsers = implode(",", $user->getAllChildIds(1));
2720 } else {
2721 $includeUsers = implode(",", array_intersect($includeUsersArray, $user->getAllChildIds(1)));
2722 }
2723 } else {
2724 $includeUsers = implode(",", $includeUsersArray);
2725 } */
2726 $includeUsers = implode(",", $includeUsersArray);
2727
2728 $num = 0;
2729
2730 $out = '';
2731 $outarray = array();
2732 $outarray2 = array();
2733
2734 // Do we want to show the label of entity into the combo list ?
2735 $showlabelofentity = isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1 && !empty($user->admin) && empty($user->entity) && !preg_match('/^search_/', $htmlname);
2736 $userissuperadminentityone = isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && empty($user->entity);
2737
2738 // Forge request to select users
2739 $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";
2740 if ($showlabelofentity) {
2741 $sql .= ", e.label";
2742 }
2743 $sql .= " FROM " . $this->db->prefix() . "user as u";
2744 if ($showlabelofentity) {
2745 $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid = u.entity";
2746 }
2747 // Condition here should be the same than into societe->getSalesRepresentatives().
2748 if ($userissuperadminentityone && $force_entity !== 'default') {
2749 if (!empty($force_entity)) {
2750 $sql .= " WHERE u.entity IN (0, " . $this->db->sanitize($force_entity) . ")";
2751 } else {
2752 $sql .= " WHERE u.entity IS NOT NULL";
2753 }
2754 } else {
2755 if (isModEnabled('multicompany') && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2756 $sql .= " WHERE u.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
2757 } else {
2758 $sql .= " WHERE u.entity IN (" . getEntity('user') . ")";
2759 }
2760 }
2761
2762 if (!empty($user->socid)) {
2763 $sql .= " AND u.fk_soc = " . ((int) $user->socid);
2764 }
2765 if (is_array($exclude) && $excludeUsers) {
2766 $sql .= " AND u.rowid NOT IN (" . $this->db->sanitize($excludeUsers) . ")";
2767 }
2768 if ($includeUsers) {
2769 $sql .= " AND u.rowid IN (" . $this->db->sanitize($includeUsers) . ")";
2770 }
2771 if (getDolGlobalString('USER_HIDE_INACTIVE_IN_COMBOBOX') || $notdisabled) {
2772 $sql .= " AND (u.statut <> 0";
2773 if (!empty($selected)) {
2774 $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
2775 }
2776 $sql .= ")";
2777 }
2778 if (getDolGlobalString('USER_HIDE_NONEMPLOYEE_IN_COMBOBOX')) {
2779 $sql .= " AND u.employee <> 0";
2780 }
2781 if (getDolGlobalString('USER_HIDE_EXTERNAL_IN_COMBOBOX')) {
2782 $sql .= " AND u.fk_soc IS NULL";
2783 }
2784 if (!empty($morefilter)) {
2785 $errormessage = '';
2786 $sql .= forgeSQLFromUniversalSearchCriteria($morefilter, $errormessage);
2787 if ($errormessage) {
2788 $this->errors[] = $errormessage;
2789 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
2790 if ($outputmode == 0) {
2791 return 'Error bad param $morefilter';
2792 } else {
2793 return array();
2794 }
2795 }
2796 }
2797
2798 //Add hook to filter on user (for example on usergroup define in custom modules)
2799 $reshook = $hookmanager->executeHooks('addSQLWhereFilterOnSelectUsers', array(), $this, $action);
2800 if (!empty($reshook)) {
2801 $sql .= $hookmanager->resPrint;
2802 }
2803
2804 if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) { // MAIN_FIRSTNAME_NAME_POSITION is 0 means firstname+lastname
2805 $sql .= " ORDER BY u.statut DESC, u.firstname ASC, u.lastname ASC";
2806 } else {
2807 $sql .= " ORDER BY u.statut DESC, u.lastname ASC, u.firstname ASC";
2808 }
2809
2810 dol_syslog(get_class($this) . "::select_dolusers", LOG_DEBUG);
2811
2812 $resql = $this->db->query($sql);
2813 if ($resql) {
2814 $num = $this->db->num_rows($resql);
2815 $i = 0;
2816 if ($num) {
2817 // do not use maxwidthonsmartphone by default. Set it by caller so auto size to 100% will work when not defined
2818 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : ' minwidth200') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
2819 if ($show_empty && !$multiple) {
2820 $textforempty = ' ';
2821 if (!empty($conf->use_javascript_ajax)) {
2822 $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
2823 }
2824 if (!is_numeric($show_empty)) {
2825 $textforempty = $show_empty;
2826 }
2827 $out .= '<option class="optiongrey" value="' . ($show_empty < 0 ? $show_empty : -1) . '"' . ((empty($selected) || in_array(-1, $selected)) ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
2828
2829 $outarray[($show_empty < 0 ? $show_empty : -1)] = $textforempty;
2830 $outarray2[($show_empty < 0 ? $show_empty : -1)] = array(
2831 'id' => ($show_empty < 0 ? $show_empty : -1),
2832 'label' => $textforempty,
2833 'labelhtml' => $textforempty,
2834 'color' => '',
2835 'picto' => ''
2836 );
2837 }
2838 if ($showalso == 2 || $showalso == 3) {
2839 $out .= '<option value="-3"' . ((in_array(-3, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("MyTeam") . ' --</option>' . "\n";
2840
2841 $hasAtLeastOneSubordinate = (count($user->getAllChildIds(1)) > 1);
2842 if ($hasAtLeastOneSubordinate) {
2843 //$sql = "SELECT rowid FROM".MAIN_DB_PREFIX."user "
2844 $outarray[-3] = '-- ' . $langs->trans("MyTeam") . ' --';
2845 $outarray2[-3] = array(
2846 'id' => -3,
2847 'label' => '-- ' . $langs->trans("MyTeam") . ' --',
2848 'labelhtml' => '-- ' . $langs->trans("MyTeam") . ' --',
2849 'color' => '',
2850 'picto' => ''
2851 );
2852 }
2853 }
2854 if ($showalso == 1 || $showalso == 3) {
2855 $out .= '<option value="-2"' . ((in_array(-2, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("Everybody") . ' --</option>' . "\n";
2856
2857 $outarray[-2] = '-- ' . $langs->trans("Everybody") . ' --';
2858 $outarray2[-2] = array(
2859 'id' => -2,
2860 'label' => '-- ' . $langs->trans("Everybody") . ' --',
2861 'labelhtml' => '-- ' . $langs->trans("Everybody") . ' --',
2862 'color' => '',
2863 'picto' => ''
2864 );
2865 }
2866 if ($showalso == 4) {
2867 $out .= '<option value="-4"' . ((in_array(-4, $selected)) ? ' selected' : '') . '>-- ' . $langs->trans("AllProjectContacts") . ' --</option>' . "\n";
2868
2869 $outarray[-4] = '-- ' . $langs->trans("AllProjectContacts") . ' --';
2870 $outarray2[-4] = array(
2871 'id' => -4,
2872 'label' => '-- ' . $langs->trans("AllProjectContacts") . ' --',
2873 'labelhtml' => '-- ' . $langs->trans("AllProjectContacts") . ' --',
2874 'color' => '',
2875 'picto' => ''
2876 );
2877 }
2878
2879 $userstatic = new User($this->db);
2880
2881 while ($i < $num) {
2882 $obj = $this->db->fetch_object($resql);
2883
2884 $userstatic->id = $obj->rowid;
2885 $userstatic->lastname = $obj->lastname;
2886 $userstatic->firstname = $obj->firstname;
2887 $userstatic->photo = $obj->photo;
2888 $userstatic->status = $obj->status;
2889 $userstatic->entity = $obj->entity;
2890 $userstatic->admin = $obj->admin;
2891 $userstatic->gender = $obj->gender;
2892
2893 $disableline = '';
2894 if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
2895 $disableline = ($enableonlytext ? $enableonlytext : '1');
2896 }
2897
2898 $labeltoshow = '';
2899 $labeltoshowhtml = '';
2900
2901 // $fullNameMode is 0=Lastname+Firstname (MAIN_FIRSTNAME_NAME_POSITION=1), 1=Firstname+Lastname (MAIN_FIRSTNAME_NAME_POSITION=0)
2902 $fullNameMode = 0;
2903 if (!getDolGlobalString('MAIN_FIRSTNAME_NAME_POSITION')) {
2904 $fullNameMode = 1; //Firstname+lastname
2905 }
2906 $labeltoshow .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2907 $labeltoshowhtml .= $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength);
2908 if (empty($obj->firstname) && empty($obj->lastname)) {
2909 $labeltoshow .= $obj->login;
2910 $labeltoshowhtml .= $obj->login;
2911 }
2912
2913 // Complete name with a more info string like: ' (info1 - info2 - ...)'
2914 $moreinfo = '';
2915 $moreinfohtml = '';
2916 if (getDolGlobalString('MAIN_SHOW_LOGIN')) {
2917 $moreinfo .= ($moreinfo ? ' - ' : ' (');
2918 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(');
2919 $moreinfo .= $obj->login;
2920 $moreinfohtml .= $obj->login;
2921 }
2922 if ($showstatus >= 0) {
2923 if ($obj->status == 1 && $showstatus == 1) {
2924 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Enabled');
2925 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Enabled');
2926 }
2927 if ($obj->status == 0 && $showstatus == 1) {
2928 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans('Disabled');
2929 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans('Disabled');
2930 }
2931 }
2932 if ($showlabelofentity) {
2933 if (empty($obj->entity)) {
2934 $moreinfo .= ($moreinfo ? ' - ' : ' (') . $langs->trans("AllEntities");
2935 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(') . $langs->trans("AllEntities");
2936 } else {
2937 if ($obj->entity != $conf->entity) {
2938 $moreinfo .= ($moreinfo ? ' - ' : ' (') . ($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2939 $moreinfohtml .= ($moreinfohtml ? ' - ' : ' <span class="opacitymedium">(').($obj->label ? $obj->label : $langs->trans("EntityNameNotDefined"));
2940 }
2941 }
2942 }
2943 $moreinfo .= (!empty($moreinfo) ? ')' : '');
2944 $moreinfohtml .= (!empty($moreinfohtml) ? ')</span>' : '');
2945 if (!empty($disableline) && $disableline != '1') {
2946 // Add text from $enableonlytext parameter
2947 $moreinfo .= ' - ' . $disableline;
2948 $moreinfohtml .= ' - ' . $disableline;
2949 }
2950 $labeltoshow .= $moreinfo;
2951 $labeltoshowhtml .= $moreinfohtml;
2952
2953 $out .= '<option value="' . $obj->rowid . '"';
2954 if (!empty($disableline)) {
2955 $out .= ' disabled';
2956 }
2957 if (in_array($obj->rowid, $selected)) {
2958 $out .= ' selected';
2959 }
2960 $out .= ' data-html="';
2961
2962 $outhtml = $userstatic->getNomUrl(-3, '', 0, 1, 24, 1, 'login', '', 1) . ' ';
2963 if ($showstatus >= 0 && $obj->status == 0) {
2964 $outhtml .= '<strike class="opacitymediumxxx">';
2965 }
2966 $outhtml .= $labeltoshowhtml;
2967 if ($showstatus >= 0 && $obj->status == 0) {
2968 $outhtml .= '</strike>';
2969 }
2970 $labeltoshowhtml = $outhtml;
2971
2972 $out .= dol_escape_htmltag($outhtml);
2973 $out .= '">';
2974 $out .= $labeltoshow;
2975 $out .= '</option>';
2976
2977 $outarray[$userstatic->id] = $userstatic->getFullName($langs, $fullNameMode, -1, $maxlength) . $moreinfo;
2978 $outarray2[$userstatic->id] = array(
2979 'id' => $userstatic->id,
2980 'label' => $labeltoshow,
2981 'labelhtml' => $labeltoshowhtml,
2982 'color' => '',
2983 'picto' => ''
2984 );
2985
2986 $i++;
2987 }
2988 } else {
2989 $out .= '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '" disabled>';
2990 $out .= '<option value="">' . $langs->trans("None") . '</option>';
2991 }
2992 $out .= '</select>';
2993
2994 if ($num && !$forcecombo) {
2995 // Enhance with select2
2996 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
2997 $out .= ajax_combobox($htmlname);
2998 }
2999 } else {
3000 dol_print_error($this->db);
3001 }
3002
3003 $this->num = $num;
3004
3005 if ($outputmode == 2) {
3006 return $outarray2;
3007 } elseif ($outputmode) {
3008 return $outarray;
3009 }
3010
3011 return $out;
3012 }
3013
3014
3015 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3039 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)
3040 {
3041 // phpcs:enable
3042 global $langs, $user;
3043
3044 $userstatic = new User($this->db);
3045 $out = '';
3046
3047 // The list of selected users is provided by the caller through $listofuserid (owner first).
3048 // Fall back to the legacy global $_SESSION['assignedtouser'] only when no list is provided
3049 // (comm/action/card.php now scopes that session bucket per event id and no longer feeds this key).
3050 if (!empty($listofuserid)) {
3051 $assignedtouser = $listofuserid;
3052 } elseif (!empty($_SESSION['assignedtouser'])) {
3053 $assignedtouser = json_decode($_SESSION['assignedtouser'], true);
3054 if (!is_array($assignedtouser)) {
3055 $assignedtouser = array();
3056 }
3057 } else {
3058 $assignedtouser = array();
3059 }
3060 $nbassignetouser = count($assignedtouser);
3061
3062 //if ($nbassignetouser && $action != 'view') $out .= '<br>';
3063 if ($nbassignetouser) {
3064 $out .= '<ul class="attendees">';
3065 }
3066 $i = 0;
3067 $ownerid = 0;
3068 foreach ($assignedtouser as $key => $value) {
3069 if ($value['id'] == $ownerid) {
3070 continue;
3071 }
3072
3073 $out .= '<li>';
3074
3075 $userstatic->fetch($value['id']);
3076 $out .= $userstatic->getNomUrl(-4);
3077
3078 if ($i == 0) {
3079 $ownerid = $value['id'];
3080 $out .= ' (' . $langs->trans("Owner") . ')';
3081 }
3082 // Add picto to delete owner/assignee
3083 if ($nbassignetouser > 1 && $action != 'view') {
3084 $canremoveassignee = 1;
3085 if ($i == 0) {
3086 // We are on the owner of the event
3087 if (!$canremoveowner) {
3088 $canremoveassignee = 0;
3089 }
3090 if (!$user->hasRight('agenda', 'allactions', 'create')) {
3091 $canremoveassignee = 0; // Can't remove the owner
3092 }
3093 } else {
3094 // We are not on the owner of the event but on a secondary assignee
3095 }
3096 if ($canremoveassignee) {
3097 // If user has all permission, he should be ableto remove a assignee.
3098 // If user has not all permission, he can onlyremove assignee of other (he can't remove itself)
3099 $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 . '">';
3100 }
3101 }
3102 // Show my availability
3103 if ($showproperties) {
3104 if ($ownerid == $value['id'] && is_array($listofuserid) && count($listofuserid) && in_array($ownerid, array_keys($listofuserid))) {
3105 $out .= '<div class="myavailability inline-block">';
3106 $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;';
3107 //$out .= '<span class="opacitymedium">' . $langs->trans("Availability") . ':</span>';
3108 $out .= '</span>';
3109 $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>';
3110 $out .= '</div>';
3111 }
3112 }
3113 //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
3114 //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
3115
3116 $out .= '</li>';
3117 $i++;
3118 }
3119 if ($nbassignetouser) {
3120 $out .= '</ul>';
3121 }
3122
3123 // Method with no ajax
3124 if ($action != 'view') {
3125 // Section to add another user
3126 $out .= '<div class="divadduser'.$htmlname.'">';
3127 $out .= '<input type="hidden" class="removedassignedhidden" name="removedassigned" value="">';
3128 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
3129 $out .= 'jQuery(".removedassigned").click(function() { jQuery(".removedassignedhidden").val(jQuery(this).val()); });';
3130 $out .= 'jQuery(".assignedtouser").change(function() { console.log(jQuery(".assignedtouser option:selected").val());';
3131 $out .= ' if (jQuery(".assignedtouser option:selected").val() > 0) { jQuery("#' . $action . 'assignedtouser").attr("disabled", false); }';
3132 $out .= ' else { jQuery("#' . $action . 'assignedtouser").attr("disabled", true); }';
3133 $out .= '});';
3134 $out .= '})</script>';
3135 $out .= img_picto('', 'user', 'class="pictofixedwidth"');
3136 $out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter, 0, '', 'minwidth200');
3137 $out .= ' <button type="submit" disabled class="button valignmiddle smallpaddingimp reposition butActionAdd" id="' . $action . 'assignedtouser" name="' . $action . 'assignedtouser" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
3138 $out .= $langs->trans("Add").'</button>';
3139 $out .= '</div>';
3140 //$out .= '<br>';
3141 }
3142
3143 return $out;
3144 }
3145
3146 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3166 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())
3167 {
3168 // phpcs:enable
3169 global $langs;
3170
3171 require_once DOL_DOCUMENT_ROOT.'/resource/class/html.formresource.class.php';
3172 require_once DOL_DOCUMENT_ROOT.'/resource/class/dolresource.class.php';
3173 $formresources = new FormResource($this->db);
3174 $resourcestatic = new Dolresource($this->db);
3175
3176 $out = '';
3177 if (!empty($_SESSION['assignedtoresource'])) {
3178 $assignedtoresource = json_decode($_SESSION['assignedtoresource'], true);
3179 if (!is_array($assignedtoresource)) {
3180 $assignedtoresource = array();
3181 }
3182 } else {
3183 $assignedtoresource = array();
3184 }
3185 $nbassignetoresource = count($assignedtoresource);
3186
3187 //if ($nbassignetoresource && $action != 'view') $out .= '<br>';
3188 if ($nbassignetoresource) {
3189 $out .= '<ul class="attendees">';
3190 }
3191 $i = 0;
3192
3193 foreach ($assignedtoresource as $key => $value) {
3194 $out .= '<li>';
3195 $resourcestatic->fetch($value['id']);
3196 $out .= $resourcestatic->getNomUrl(-1);
3197 if ($nbassignetoresource >= 1 && $action != 'view') {
3198 $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 . '">';
3199 }
3200 // Show my availability
3201 if ($showproperties) {
3202 if (is_array($listofresourceid) && count($listofresourceid)) {
3203 $out .= '<div class="myavailability inline-block">';
3204 $out .= '<span class="hideonsmartphone">&nbsp;-&nbsp;';
3205 //$out .= '<span class="opacitymedium">' . $langs->trans("Availability") . ': </span>';
3206 $out .= '</span>';
3207 $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>';
3208 $out .= '</div>';
3209 }
3210 }
3211 //$out.=' '.($value['mandatory']?$langs->trans("Mandatory"):$langs->trans("Optional"));
3212 //$out.=' '.($value['transparency']?$langs->trans("Busy"):$langs->trans("NotBusy"));
3213
3214 $out .= '</li>';
3215 $i++;
3216 }
3217 if ($nbassignetoresource) {
3218 $out .= '</ul>';
3219 }
3220
3221 // Method with no ajax
3222 if ($action != 'view') {
3223 $out .= '<input type="hidden" class="removedassignedresourcehidden" name="removedassignedresource" value="">';
3224 $out .= '<script nonce="' . getNonce() . '" type="text/javascript">jQuery(document).ready(function () {';
3225 $out .= 'jQuery(".removedassignedresource").click(function() { jQuery(".removedassignedresourcehidden").val(jQuery(this).val()); });';
3226 $out .= 'jQuery(".assignedtoresource").change(function() { console.log(jQuery(".assignedtoresource option:selected").val());';
3227 $out .= ' if (jQuery(".assignedtoresource option:selected").val() > 0) { jQuery("#' . $action . 'assignedtoresource").attr("disabled", false); }';
3228 $out .= ' else { jQuery("#' . $action . 'assignedtoresource").attr("disabled", true); }';
3229 $out .= '});';
3230 $out .= '})</script>';
3231
3232 $events = array();
3233 if ($nbassignetoresource) {
3234 //$out .= img_picto('', 'add', 'class="pictofixedwidth"');
3235 } else {
3236 $out .= img_picto('', 'resource', 'class="pictofixedwidth"');
3237 }
3238 $out .= $formresources->select_resource_list(0, $htmlname, '', 1, 1, 0, $events, '', 2, 0, 'minwidth200');
3239 //$out .= $this->select_dolusers('', $htmlname, $show_empty, $exclude, $disabled, $include, $enableonly, $force_entity, $maxlength, $showstatus, $morefilter);
3240 $out .= ' <button type="submit" disabled class="button valignmiddle smallpaddingimp reposition butActionAdd" id="' . $action . 'assignedtoresource" name="' . $action . 'assignedtoresource" value="' . dol_escape_htmltag($langs->trans("Add")) . '">';
3241 $out .= $langs->trans("Add");
3242 $out .= '</button>';
3243 $out .= '<br>';
3244 }
3245
3246 return $out;
3247 }
3248
3249 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3250
3280 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)
3281 {
3282 // phpcs:enable
3283 global $langs, $conf;
3284
3285 $out = '';
3286
3287 // check parameters
3288 $price_level = (!empty($price_level) ? $price_level : 0);
3289 if (is_null($ajaxoptions)) {
3290 $ajaxoptions = array();
3291 }
3292
3293 if (strval($filtertype) === '' && (isModEnabled("product") || isModEnabled("service"))) {
3294 if (isModEnabled("product") && !isModEnabled('service')) {
3295 $filtertype = '0';
3296 } elseif (!isModEnabled('product') && isModEnabled("service")) {
3297 $filtertype = '1';
3298 }
3299 }
3300
3301 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3302 $placeholder = (is_numeric($showempty) ? '' : 'placeholder="'.dolPrintHTML($showempty).'"');
3303
3304 if ($selected && empty($selected_input_value)) {
3305 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3306 $producttmpselect = new Product($this->db);
3307 $producttmpselect->fetch($selected);
3308 $selected_input_value = $producttmpselect->ref;
3309 unset($producttmpselect);
3310 }
3311 // handle case where product or service module is disabled + no filter specified
3312 if ($filtertype == '') {
3313 if (!isModEnabled('product')) { // when product module is disabled, show services only
3314 $filtertype = 1;
3315 } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
3316 $filtertype = 0;
3317 }
3318 }
3319 // mode=1 means customers products
3320 $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;
3321 if ((int) $warehouseId > 0) {
3322 $urloption .= '&warehouseid=' . (int) $warehouseId;
3323 }
3324
3325 if (isModEnabled('variants') && is_array($selected_combinations)) {
3326 // Code to automatically insert with javascript the select of attributes under the select of product
3327 // when a parent of variant has been selected.
3328 // Note: Samecode than for product input using select
3329 $htmltag = 'input';
3330 $out .= '
3331 <!-- script to auto show attributes select tags if a variant was selected -->
3332 <script nonce="' . getNonce() . '">
3333 // auto show attributes fields
3334 selected = ' . json_encode($selected_combinations) . ';
3335 combvalues = {};
3336
3337 jQuery(document).ready(function () {
3338
3339 jQuery("input[name=\'prod_entry_mode\']").change(function () {
3340 if (jQuery(this).val() == \'free\') {
3341 jQuery(\'div#attributes_box\').empty();
3342 }
3343 });
3344
3345 jQuery("'.$htmltag.'#' . $htmlname . '").change(function () {
3346
3347 if (!jQuery(this).val()) {
3348 jQuery(\'div#attributes_box\').empty();
3349 return;
3350 }
3351
3352 console.log("A change has started. We get variants fields to inject html select");
3353
3354 jQuery.getJSON("' . DOL_URL_ROOT . '/variants/ajax/getCombinations.php", {
3355 id: jQuery(this).val()
3356 }, function (data) {
3357 jQuery(\'div#attributes_box\').empty();
3358
3359 jQuery.each(data, function (key, val) {
3360
3361 combvalues[val.id] = val.values;
3362
3363 var span = jQuery(document.createElement(\'div\')).css({
3364 \'display\': \'table-row\'
3365 });
3366
3367 span.append(
3368 jQuery(document.createElement(\'div\')).text(val.label).css({
3369 \'font-weight\': \'bold\',
3370 \'display\': \'table-cell\'
3371 })
3372 );
3373
3374 var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
3375 \'margin-left\': \'15px\',
3376 \'white-space\': \'pre\'
3377 }).append(
3378 jQuery(document.createElement(\'option\')).val(\'\')
3379 );
3380
3381 jQuery.each(combvalues[val.id], function (key, val) {
3382 var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
3383
3384 if (selected[val.fk_product_attribute] == val.id) {
3385 tag.attr(\'selected\', \'selected\');
3386 }
3387
3388 html.append(tag);
3389 });
3390
3391 span.append(html);
3392 jQuery(\'div#attributes_box\').append(span);
3393 });
3394 })
3395 });
3396
3397 ' . ($selected ? 'jQuery("'.$htmltag.'#' . $htmlname . '").change();' : '') . '
3398 });
3399 </script>
3400 ';
3401 }
3402
3403 if (empty($hidelabel)) {
3404 $placeholder = ' placeholder="' . dolPrintHTMLForAttribute($langs->trans("RefOrLabel")) . '"';
3405 } elseif ($hidelabel > 1) {
3406 $placeholder = ' placeholder="' . dolPrintHTMLForAttribute($langs->trans("RefOrLabel")) . '"';
3407 if ($hidelabel == 2) {
3408 $out .= img_picto($langs->trans("Search"), 'search');
3409 }
3410 }
3411
3412 $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" />';
3413 if ($hidelabel == 3) {
3414 $out .= img_picto($langs->trans("Search"), 'search');
3415 }
3416
3417 $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);
3418 } else {
3419 $out .= $this->select_produits_list($selected, $htmlname, $filtertype, $limit, $price_level, '', $status, $finished, 0, $socid, $showempty, $forcecombo, $morecss, $hidepriceinlabel, $warehouseStatus, $status_purchase, $warehouseId);
3420
3421 if (isModEnabled('variants') && is_array($selected_combinations)) {
3422 // Code to automatically insert with javascript the select of attributes under the select of product
3423 // when a parent of variant has been selected.
3424 // Note: Samecode than for product input using Ajax
3425 $htmltag = 'select';
3426 $out .= '
3427 <!-- script to auto show attributes select tags if a variant was selected -->
3428 <script nonce="' . getNonce() . '">
3429 // auto show attributes fields
3430 selected = ' . json_encode($selected_combinations) . ';
3431 combvalues = {};
3432
3433 jQuery(document).ready(function () {
3434
3435 jQuery("input[name=\'prod_entry_mode\']").change(function () {
3436 if (jQuery(this).val() == \'free\') {
3437 jQuery(\'div#attributes_box\').empty();
3438 }
3439 });
3440
3441 jQuery("'.$htmltag.'#' . $htmlname . '").change(function () {
3442
3443 if (!jQuery(this).val()) {
3444 jQuery(\'div#attributes_box\').empty();
3445 return;
3446 }
3447
3448 console.log("A change has started. We get variants fields to inject html select");
3449
3450 jQuery.getJSON("' . DOL_URL_ROOT . '/variants/ajax/getCombinations.php", {
3451 id: jQuery(this).val()
3452 }, function (data) {
3453 jQuery(\'div#attributes_box\').empty();
3454
3455 jQuery.each(data, function (key, val) {
3456
3457 combvalues[val.id] = val.values;
3458
3459 var span = jQuery(document.createElement(\'div\')).css({
3460 \'display\': \'table-row\'
3461 });
3462
3463 span.append(
3464 jQuery(document.createElement(\'div\')).text(val.label).css({
3465 \'font-weight\': \'bold\',
3466 \'display\': \'table-cell\'
3467 })
3468 );
3469
3470 var html = jQuery(document.createElement(\'select\')).attr(\'name\', \'combinations[\' + val.id + \']\').css({
3471 \'margin-left\': \'15px\',
3472 \'white-space\': \'pre\'
3473 }).append(
3474 jQuery(document.createElement(\'option\')).val(\'\')
3475 );
3476
3477 jQuery.each(combvalues[val.id], function (key, val) {
3478 var tag = jQuery(document.createElement(\'option\')).val(val.id).html(val.value);
3479
3480 if (selected[val.fk_product_attribute] == val.id) {
3481 tag.attr(\'selected\', \'selected\');
3482 }
3483
3484 html.append(tag);
3485 });
3486
3487 span.append(html);
3488 jQuery(\'div#attributes_box\').append(span);
3489 });
3490 })
3491 });
3492
3493 ' . ($selected ? 'jQuery("'.$htmltag.'#' . $htmlname . '").change();' : '') . '
3494 });
3495 </script>
3496 ';
3497 }
3498 }
3499
3500 if (empty($nooutput)) {
3501 print $out;
3502 } else {
3503 return $out;
3504 }
3505 }
3506
3507 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3508
3524 public function select_bom($selected = '', $htmlname = 'bom_id', $limit = 0, $status = 1, $type = 0, $showempty = '1', $morecss = '', $nooutput = '', $forcecombo = 0, $TProducts = [])
3525 {
3526 // phpcs:enable
3527
3528 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3529
3530 $error = 0;
3531 $out = '';
3532
3533 if (!$forcecombo) {
3534 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3535 $events = array();
3536 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("BOM_USE_SEARCH_TO_SELECT"));
3537 }
3538
3539 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
3540
3541 $sql = 'SELECT b.rowid, b.ref, b.label as bomLabel, p.label as productLabel';
3542 $sql .= ' FROM ' . $this->db->prefix() . 'bom_bom as b';
3543 $sql .= ' INNER JOIN ' . $this->db->prefix() . 'product as p ON b.fk_product = p.rowid';
3544 $sql .= ' WHERE b.entity IN (' . getEntity('bom') . ')';
3545 if (!empty($status)) {
3546 $sql .= ' AND status = ' . (int) $status;
3547 }
3548 if (!empty($type)) {
3549 $sql .= ' AND bomtype = ' . (int) $type;
3550 }
3551 if (!empty($TProducts)) {
3552 $sql .= ' AND fk_product IN (' . $this->db->sanitize(implode(',', $TProducts)) . ')';
3553 }
3554 if (!empty($limit)) {
3555 $sql .= ' LIMIT ' . (int) $limit;
3556 }
3557 $resql = $this->db->query($sql);
3558 if ($resql) {
3559 if ($showempty) {
3560 $out .= '<option value="-1"';
3561 if (empty($selected)) {
3562 $out .= ' selected';
3563 }
3564 $out .= '>&nbsp;</option>';
3565 }
3566 while ($obj = $this->db->fetch_object($resql)) {
3567 $out .= '<option value="' . $obj->rowid . '"';
3568 if ($obj->rowid == $selected) {
3569 $out .= 'selected';
3570 }
3571 $out .= '>' . $obj->ref . ' - ' . $obj->productLabel . ' - ' . $obj->bomLabel . '</option>';
3572 }
3573 } else {
3574 $error++;
3575 dol_print_error($this->db);
3576 }
3577 $out .= '</select>';
3578 if (empty($nooutput)) {
3579 print $out;
3580 } else {
3581 return $out;
3582 }
3583 }
3584
3585 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3586
3613 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)
3614 {
3615 // phpcs:enable
3616 global $langs;
3617 global $hookmanager;
3618
3619 $out = '';
3620 $outarray = array();
3621
3622 // Units
3623 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3624 $langs->load('other');
3625 }
3626
3627 $warehouseStatusArray = array();
3628 if (!empty($warehouseStatus)) {
3629 require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
3630 if (preg_match('/warehouseclosed/', $warehouseStatus)) {
3631 $warehouseStatusArray[] = Entrepot::STATUS_CLOSED;
3632 }
3633 if (preg_match('/warehouseopen/', $warehouseStatus)) {
3634 $warehouseStatusArray[] = Entrepot::STATUS_OPEN_ALL;
3635 }
3636 if (preg_match('/warehouseinternal/', $warehouseStatus)) {
3637 $warehouseStatusArray[] = Entrepot::STATUS_OPEN_INTERNAL;
3638 }
3639 }
3640
3641 $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";
3642 if (count($warehouseStatusArray)) {
3643 $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
3644 } else {
3645 $selectFieldsGrouped = ", " . $this->db->ifsql("p.stock IS NULL", '0', "p.stock") . " AS stock";
3646 }
3647
3648 $sql = "SELECT ";
3649
3650 // Add select from hooks
3651 $parameters = array();
3652 $reshook = $hookmanager->executeHooks('selectProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
3653 if (empty($reshook)) {
3654 $sql .= $selectFields.$selectFieldsGrouped.$hookmanager->resPrint;
3655 } else {
3656 $sql .= $hookmanager->resPrint;
3657 }
3658
3659 if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
3660 // Take randomly the first category of product to allow a sort on it. Bugged feature !
3661 $sql .= ", (SELECT " . $this->db->prefix() . "categorie_product.fk_categorie
3662 FROM " . $this->db->prefix() . "categorie_product
3663 WHERE " . $this->db->prefix() . "categorie_product.fk_product = p.rowid
3664 LIMIT 1
3665 ) AS categorie_product_id";
3666 }
3667
3668 // Price by customer
3669 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3670 $sql .= ', pcp.rowid as idprodcustprice, pcp.price as custprice, pcp.price_ttc as custprice_ttc,';
3671 $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';
3672 $selectFields .= ", idprodcustprice, custprice, custprice_ttc, custprice_base_type, custtva_tx, custdefault_vat_code, custref, custdiscount_percent";
3673 }
3674 // Units
3675 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3676 $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";
3677 $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';
3678 }
3679
3680 // Multilang : we add translation
3681 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3682 $sql .= ", pl.label as label_translated";
3683 $sql .= ", pl.description as description_translated";
3684 $selectFields .= ", label_translated";
3685 $selectFields .= ", description_translated";
3686 }
3687 // Price by quantity
3688 if (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3689 $sql .= ", (SELECT pp.rowid FROM " . $this->db->prefix() . "product_price as pp WHERE pp.fk_product = p.rowid";
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_rowid";
3695 $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
3696 if ($price_level >= 1 && getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES')) {
3697 $sql .= " AND price_level = " . ((int) $price_level);
3698 }
3699 $sql .= " ORDER BY date_price";
3700 $sql .= " DESC LIMIT 1) as price_by_qty";
3701 $selectFields .= ", price_rowid, price_by_qty";
3702 }
3703
3704 //$sqlfields = $sql; // $sql fields to remove for count total
3705
3706 $sql .= " FROM ".$this->db->prefix()."product as p";
3707
3708 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_FORCE_INDEX')) {
3709 $sql .= " USE INDEX (" . $this->db->sanitize(getDolGlobalString('MAIN_PRODUCT_FORCE_INDEX')) . ")";
3710 }
3711
3712 // Add from (left join) from hooks
3713 $parameters = array(
3714 'socid' => $socid,
3715 );
3716 $reshook = $hookmanager->executeHooks('selectProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
3717 $sql .= $hookmanager->resPrint;
3718
3719 if (count($warehouseStatusArray)) {
3720 // Return line if product is inside the selected stock. If not, e.* and p.* will be null so we will count 0.
3721 // Replace this with a AND EXISTS ? Not possible as we need the ps.reel field for the SUM or 0 if no link.
3722 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_stock as ps ON ps.fk_product = p.rowid";
3723 $sql .= " LEFT JOIN " . $this->db->prefix() . "entrepot as e ON ps.fk_entrepot = e.rowid AND e.entity IN (" . getEntity('stock') . ")";
3724 $sql .= ' AND e.statut IN (' . $this->db->sanitize($this->db->escape(implode(',', $warehouseStatusArray))) . ')';
3725 }
3726
3727 // Price by customer (Add field pcp for the older price for couple product/thirdparty.
3728 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3729 $now = dol_now();
3730 $sql .= " LEFT JOIN (";
3731 $sql .= " SELECT pcp1.*";
3732 $sql .= " FROM " . $this->db->prefix() . "product_customer_price AS pcp1";
3733 $sql .= " LEFT JOIN (";
3734 $sql .= " SELECT fk_soc, fk_product, MIN(date_begin) AS date_begin";
3735 $sql .= " FROM " . $this->db->prefix() . "product_customer_price";
3736 $sql .= " WHERE fk_soc = " . ((int) $socid);
3737 $sql .= " AND date_begin <= '" . $this->db->idate($now) . "'";
3738 $sql .= " AND (date_end IS NULL OR '" . $this->db->idate($now) . "' <= date_end)";
3739 $sql .= " GROUP BY fk_soc, fk_product";
3740 $sql .= " ) AS pcp2 ON pcp1.fk_soc = pcp2.fk_soc AND pcp1.fk_product = pcp2.fk_product AND pcp1.date_begin = pcp2.date_begin";
3741 $sql .= " WHERE pcp2.fk_soc IS NOT NULL";
3742 $sql .= " ) AS pcp ON pcp.fk_soc = " . ((int) $socid) . " AND pcp.fk_product = p.rowid";
3743 }
3744 // Units : we add unit properties with a link on the primary key of unit
3745 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
3746 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units as u ON u.rowid = p.fk_unit";
3747 }
3748 // Multilang : we add translation fields with a link on unique key fk_product/lang.
3749 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3750 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_lang as pl ON pl.fk_product = p.rowid";
3751 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && !empty($socid)) {
3752 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
3753 $soc = new Societe($this->db);
3754 $result = $soc->fetch($socid);
3755 if ($result > 0 && !empty($soc->default_lang)) {
3756 $sql .= " AND pl.lang = '" . $this->db->escape($soc->default_lang) . "'";
3757 } else {
3758 $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3759 }
3760 } else {
3761 $sql .= " AND pl.lang = '" . $this->db->escape($langs->getDefaultLang()) . "'";
3762 }
3763 }
3764
3765 // Add WHERE conditions
3766 $sql .= ' WHERE p.entity IN (' . getEntity('product') . ')';
3767 if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD')) {
3768 if (getDolGlobalString('PRODUIT_ATTRIBUTES_HIDECHILD_BUT_ALLOW_SEARCH_IN_EAN13')) {
3769 if (strlen($filterkey) != 13) {
3770 $sql .= " AND NOT EXISTS (SELECT pac.rowid FROM ".$this->db->prefix()."product_attribute_combination as pac WHERE pac.fk_product_child = p.rowid)";
3771 }
3772 } else {
3773 $sql .= " AND NOT EXISTS (SELECT pac.rowid FROM ".$this->db->prefix()."product_attribute_combination as pac WHERE pac.fk_product_child = p.rowid)";
3774 }
3775 }
3776 if ($finished == 0) {
3777 $sql .= " AND p.finished = " . ((int) $finished);
3778 } elseif ($finished == 1) {
3779 $sql .= " AND p.finished = ".((int) $finished);
3780 }
3781 if ($status >= 0) {
3782 $sql .= " AND p.tosell = ".((int) $status);
3783 }
3784 if ($status_purchase >= 0) {
3785 $sql .= " AND p.tobuy = " . ((int) $status_purchase);
3786 }
3787 // Filter by product type
3788 if (strval($filtertype) != '') {
3789 $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
3790 } elseif (!isModEnabled('product')) { // when product module is disabled, show services only
3791 $sql .= " AND p.fk_product_type = 1";
3792 } elseif (!isModEnabled('service')) { // when service module is disabled, show products only
3793 $sql .= " AND p.fk_product_type = 0";
3794 }
3795
3796 if ((int) $warehouseId > 0) {
3797 $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)";
3798 }
3799
3800 // Add where from hooks
3801 $parameters = array(
3802 'filterkey' => &$filterkey,
3803 'socid' => $socid,
3804 );
3805 $reshook = $hookmanager->executeHooks('selectProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
3806 $sql .= $hookmanager->resPrint;
3807 // Add criteria on ref/label
3808 if ($filterkey != '') {
3809 $sqlSupplierSearch = '';
3810
3811 $sql .= ' AND (';
3812 $prefix = getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
3813 // For natural search
3814 $search_crit = explode(' ', $filterkey);
3815 $i = 0;
3816 if (count($search_crit) > 1) {
3817 $sql .= "(";
3818 }
3819 foreach ($search_crit as $crit) {
3820 if ($i > 0) {
3821 $sql .= " AND ";
3822 }
3823 $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3824 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3825 $sql .= " OR pl.label LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3826 }
3827 if ((getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && !empty($socid)) {
3828 $sql .= " OR pcp.ref_customer LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3829 }
3830 if (getDolGlobalString('PRODUCT_AJAX_SEARCH_ON_DESCRIPTION')) {
3831 $sql .= " OR p.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3832 if (getDolGlobalInt('MAIN_MULTILANGS')) {
3833 $sql .= " OR pl.description LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3834 }
3835 }
3836
3837 // include search in supplier ref
3838 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3839 $sqlSupplierSearch .= !empty($sqlSupplierSearch) ? ' AND ' : '';
3840 $sqlSupplierSearch .= " pfp.ref_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
3841 }
3842 $sql .= ")";
3843 $i++;
3844 }
3845 if (count($search_crit) > 1) {
3846 $sql .= ")";
3847 }
3848 if (isModEnabled('barcode')) {
3849 $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
3850 }
3851
3852 // include search in supplier ref
3853 if (getDolGlobalString('MAIN_SEARCH_PRODUCT_BY_FOURN_REF')) {
3854 $sql .= " OR EXISTS (SELECT pfp.fk_product FROM " . $this->db->prefix() . "product_fournisseur_price as pfp WHERE p.rowid = pfp.fk_product";
3855 $sql .= " AND (";
3856 $sql .= $sqlSupplierSearch;
3857 $sql .= "))";
3858 }
3859
3860 $sql .= ')';
3861 }
3862 if (count($warehouseStatusArray)) {
3863 $sql .= " GROUP BY " . $this->db->sanitize($selectFields, 0, 0, 1); // To have the SUM on ps.reel working in the select.
3864 }
3865
3866 // Sort by category
3867 if (getDolGlobalString('PRODUCT_SORT_BY_CATEGORY')) {
3868 $sql .= " ORDER BY categorie_product_id ".(getDolGlobalInt('PRODUCT_SORT_BY_CATEGORY') == 1 ? "ASC" : "DESC");
3869 } else {
3870 $sql .= $this->db->order("p.ref");
3871 }
3872
3873 $limit = getDolGlobalInt('SEARCH_LIMIT_AJAX') ?: $limit; // SEARCH_LIMIT_AJAX is a hidden option that has priority on visible option PRODUIT_LIMIT_SIZE if set.
3874 $sql .= $this->db->plimit($limit, 0);
3875
3876 /* The fast and low memory method to get and count full list converts the sql into a sql count */
3877 /*
3878 $nbtotalofrecords = 0;
3879 $sqlforcount = preg_replace('/^'.preg_quote($sqlfields, '/').'/', 'SELECT COUNT(*) as nbtotalofrecords', $sql);
3880 $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount);
3881
3882 $resql = $this->db->query($sqlforcount);
3883 if ($resql) {
3884 $objforcount = $this->db->fetch_object($resql);
3885 $nbtotalofrecords = $objforcount->nbtotalofrecords;
3886 } else {
3887 dol_print_error($this->db);
3888 }
3889 */
3890
3891 // Build output string
3892 dol_syslog(get_class($this) . "::select_produits_list search products", LOG_DEBUG);
3893
3894 // If we have no $limit parameter, this request may hang dur to high number of lines returned.
3895 // This should not happen because this method should not be called directly, iIt is called by select_produit() that always add a $limit parameter.
3896 $result = $this->db->query($sql);
3897
3898 if ($result) {
3899 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
3900 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3901 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
3902
3903 $num = $this->db->num_rows($result);
3904
3905 $events = array();
3906
3907 if (!$forcecombo) {
3908 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
3909 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt("PRODUIT_USE_SEARCH_TO_SELECT"));
3910 }
3911
3912 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
3913
3914 $textifempty = '';
3915 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
3916 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
3917 if (getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
3918 if ($showempty && !is_numeric($showempty)) {
3919 $textifempty = $langs->trans($showempty);
3920 } else {
3921 $textifempty .= $langs->trans("All");
3922 }
3923 } else {
3924 if ($showempty && !is_numeric($showempty)) {
3925 $textifempty = $langs->trans($showempty);
3926 }
3927 }
3928 if ($showempty) {
3929 $out .= '<option value="-1" selected>' . ($textifempty ? $textifempty : '&nbsp;') . '</option>';
3930 }
3931
3932 $i = 0;
3933 while ($num && $i < $num) {
3934 $opt = '';
3935 $optJson = array();
3936 $objp = $this->db->fetch_object($result);
3937
3938 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
3939 $sql = "SELECT rowid, quantity, price, unitprice, remise_percent, remise, price_base_type";
3940 $sql .= " FROM " . $this->db->prefix() . "product_price_by_qty";
3941 $sql .= " WHERE fk_product_price = " . ((int) $objp->price_rowid);
3942 $sql .= " ORDER BY quantity ASC";
3943
3944 dol_syslog(get_class($this) . "::select_produits_list search prices by qty", LOG_DEBUG);
3945 $result2 = $this->db->query($sql);
3946 if ($result2) {
3947 $nb_prices = $this->db->num_rows($result2);
3948 $j = 0;
3949 while ($nb_prices && $j < $nb_prices) {
3950 $objp2 = $this->db->fetch_object($result2);
3951
3952 $objp->price_by_qty_rowid = $objp2->rowid;
3953 $objp->price_by_qty_price_base_type = $objp2->price_base_type;
3954 $objp->price_by_qty_quantity = $objp2->quantity;
3955 $objp->price_by_qty_unitprice = $objp2->unitprice;
3956 $objp->price_by_qty_remise_percent = $objp2->remise_percent;
3957 // For backward compatibility
3958 $objp->quantity = $objp2->quantity;
3959 $objp->price = $objp2->price;
3960 $objp->unitprice = $objp2->unitprice;
3961 $objp->remise_percent = $objp2->remise_percent;
3962
3963 //$objp->tva_tx is not overwritten by $objp2 value
3964 //$objp->default_vat_code is not overwritten by $objp2 value
3965
3966 $this->constructProductListOption($objp, $opt, $optJson, 0, $selected, $hidepriceinlabel, $filterkey);
3967 '@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';
3968 $j++;
3969
3970 // Add new entry
3971 // "key" value of json key array is used by jQuery automatically as selected value
3972 // "label" value of json key array is used by jQuery automatically as text for combo box
3973 $out .= $opt;
3974 array_push($outarray, $optJson);
3975 }
3976 }
3977 } else {
3978 if (isModEnabled('dynamicprices') && !empty($objp->fk_price_expression)) {
3979 $price_product = new Product($this->db);
3980 $price_product->fetch($objp->rowid, '', '', '1');
3981
3982 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
3983 $priceparser = new PriceParser($this->db);
3984 $price_result = $priceparser->parseProduct($price_product);
3985 if ($price_result >= 0) {
3986 $objp->price = $price_result;
3987 $objp->unitprice = $price_result;
3988 //Calculate the VAT
3989 $objp->price_ttc = (float) price2num($objp->price) * (1 + ($objp->tva_tx / 100));
3990 $objp->price_ttc = price2num($objp->price_ttc, 'MU');
3991 }
3992 }
3993 if (getDolGlobalInt('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES') && !empty($objp->custprice)) {
3994 $price_level = '';
3995 }
3996 $this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected, $hidepriceinlabel, $filterkey);
3997 // Add new entry
3998 // "key" value of json key array is used by jQuery automatically as selected value
3999 // "label" value of json key array is used by jQuery automatically as text for combo box
4000 $out .= $opt;
4001 array_push($outarray, $optJson);
4002 }
4003
4004 $i++;
4005 }
4006
4007 $out .= '</select>';
4008
4009 $this->db->free($result);
4010
4011 if (empty($outputmode)) {
4012 return $out;
4013 }
4014
4015 return $outarray;
4016 } else {
4017 dol_print_error($this->db);
4018 }
4019
4020 return '';
4021 }
4022
4038 protected function constructProductListOption(&$objp, &$opt, &$optJson, $price_level, $selected, $hidepriceinlabel = 0, $filterkey = '', $novirtualstock = 0)
4039 {
4040 global $langs, $conf, $user;
4041 global $hookmanager;
4042
4043 $outkey = '';
4044 $outval = '';
4045 $outref = '';
4046 $outlabel = '';
4047 $outlabel_translated = '';
4048 $outdesc = '';
4049 $outdesc_translated = '';
4050 $outbarcode = '';
4051 $outorigin = '';
4052 $outtype = '';
4053 $outprice_ht = '';
4054 $outprice_ttc = '';
4055 $outpricebasetype = '';
4056 $outtva_tx = '';
4057 $outdefault_vat_code = '';
4058 $outqty = 1;
4059 $outdiscount = '0';
4060
4061 $maxlengtharticle = getDolGlobalInt('PRODUCT_MAX_LENGTH_COMBO', 48);
4062
4063 $productlabel = $objp->label;
4064 if (!empty($objp->label_translated)) {
4065 $productlabel = $objp->label_translated;
4066 }
4067 $label = $productlabel;
4068 if (!empty($filterkey) && $filterkey != '') {
4069 $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
4070 }
4071
4072 $outkey = $objp->rowid;
4073 $outref = $objp->ref;
4074 $outrefcust = empty($objp->custref) ? '' : $objp->custref;
4075 $outlabel = $objp->label;
4076 $outdesc = $objp->description;
4077 if (getDolGlobalInt('MAIN_MULTILANGS')) {
4078 $outlabel_translated = $objp->label_translated;
4079 $outdesc_translated = $objp->description_translated;
4080 }
4081 $outbarcode = $objp->barcode;
4082 $outorigin = $objp->fk_country;
4083 $outpbq = empty($objp->price_by_qty_rowid) ? '' : $objp->price_by_qty_rowid;
4084
4085 $outtype = $objp->fk_product_type;
4086 $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
4087 $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
4088
4089 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4090 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
4091 }
4092
4093 // Units
4094 $outvalUnits = '';
4095 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4096 if (!empty($objp->unit_short)) {
4097 $outvalUnits .= ' - ' . $objp->unit_short;
4098 }
4099 }
4100 if (getDolGlobalString('PRODUCT_SHOW_DIMENSIONS_IN_COMBO')) {
4101 if (!empty($objp->weight) && $objp->weight_units !== null) {
4102 $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
4103 $outvalUnits .= ' - ' . $unitToShow;
4104 }
4105 if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
4106 $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
4107 $outvalUnits .= ' - ' . $unitToShow;
4108 }
4109 if (!empty($objp->surface) && $objp->surface_units !== null) {
4110 $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
4111 $outvalUnits .= ' - ' . $unitToShow;
4112 }
4113 if (!empty($objp->volume) && $objp->volume_units !== null) {
4114 $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
4115 $outvalUnits .= ' - ' . $unitToShow;
4116 }
4117 }
4118 if ($outdurationvalue && $outdurationunit) {
4119 $da = array(
4120 'h' => $langs->trans('Hour'),
4121 'd' => $langs->trans('Day'),
4122 'w' => $langs->trans('Week'),
4123 'm' => $langs->trans('Month'),
4124 'y' => $langs->trans('Year')
4125 );
4126 if (isset($da[$outdurationunit])) {
4127 $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
4128 }
4129 }
4130
4131 // Set stocktag (stock too low or not or unknown)
4132 $stocktag = 0;
4133 if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4134 if ($user->hasRight('stock', 'lire')) {
4135 if ($objp->stock > 0) {
4136 $stocktag = 1;
4137 } elseif ($objp->stock <= 0) {
4138 $stocktag = -1;
4139 }
4140 }
4141 }
4142
4143 // Set full plain label for the native <option> text. Select2 uses this text
4144 // as its search corpus, while data-html below keeps the visible label short.
4145 $labeltosearch = '';
4146 $labeltosearch .= $objp->ref;
4147 if (!empty($objp->custref)) {
4148 $labeltosearch .= ' (' . $objp->custref . ')';
4149 }
4150 if ($outbarcode) {
4151 $labeltosearch .= ' (' . $outbarcode . ')';
4152 }
4153 $labeltosearch .= ' - ' . $productlabel;
4154 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4155 $labeltosearch .= ' (' . getCountry($outorigin, '1') . ')';
4156 }
4157
4158 // Set $labltoshowhtml
4159 $labeltoshowhtml = '';
4160 $labeltoshowhtml .= $objp->ref;
4161 if (!empty($objp->custref)) {
4162 $labeltoshowhtml .= ' (' . $objp->custref . ')';
4163 }
4164 if (!empty($filterkey) && $filterkey != '') {
4165 $labeltoshowhtml = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $labeltoshowhtml, 1);
4166 }
4167 if ($outbarcode) {
4168 $labeltoshowhtml .= ' (' . $outbarcode . ')';
4169 }
4170 $labeltoshowhtml .= ' - ' . dol_trunc($label, $maxlengtharticle);
4171 if ($outorigin && getDolGlobalString('PRODUCT_SHOW_ORIGIN_IN_COMBO')) {
4172 $labeltoshowhtml .= ' (' . getCountry($outorigin, '1') . ')';
4173 }
4174
4175 // Stock
4176 $labeltoshowstock = '';
4177 $labeltoshowhtmlstock = '';
4178 if (isModEnabled('stock') && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4179 if ($user->hasRight('stock', 'lire')) {
4180 $labeltoshowstock .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4181
4182 if ($objp->stock > 0) {
4183 $labeltoshowhtmlstock .= ' - <span class="product_line_stock_ok">';
4184 } elseif ($objp->stock <= 0) {
4185 $labeltoshowhtmlstock .= ' - <span class="product_line_stock_too_low">';
4186 }
4187 $labeltoshowhtmlstock .= $langs->transnoentities("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4188 $labeltoshowhtmlstock .= '</span>';
4189
4190 if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) { // Warning, this option may slow down combo list generation
4191 $langs->load("stocks");
4192
4193 $tmpproduct = new Product($this->db);
4194 $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
4195 $tmpproduct->load_virtual_stock();
4196 $virtualstock = $tmpproduct->stock_theorique;
4197
4198 $labeltoshowstock .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
4199
4200 $labeltoshowhtmlstock .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
4201 if ($virtualstock > 0) {
4202 $labeltoshowhtmlstock .= '<span class="product_line_stock_ok">';
4203 } elseif ($virtualstock <= 0) {
4204 $labeltoshowhtmlstock .= '<span class="product_line_stock_too_low">';
4205 }
4206 $labeltoshowhtmlstock .= $virtualstock;
4207 $labeltoshowhtmlstock .= '</span>';
4208
4209 unset($tmpproduct);
4210 }
4211 }
4212 }
4213
4214 // Price
4215 $found = 0;
4216 $labeltoshowprice = '';
4217 $labeltoshowhtmlprice = '';
4218 // If we need a particular price level (from 1 to n)
4219 if (empty($hidepriceinlabel) && $price_level >= 1 && (getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES'))) {
4220 $sql = "SELECT price, price_ttc, price_base_type, tva_tx, default_vat_code";
4221 $sql .= " FROM " . $this->db->prefix() . "product_price";
4222 $sql .= " WHERE fk_product = " . ((int) $objp->rowid);
4223 $sql .= " AND entity IN (" . getEntity('productprice') . ")";
4224 $sql .= " AND price_level = " . ((int) $price_level);
4225 $sql .= " ORDER BY date_price DESC, rowid DESC"; // Warning DESC must be both on date_price and rowid.
4226 $sql .= " LIMIT 1";
4227
4228 dol_syslog(get_class($this) . '::constructProductListOption search price for product ' . $objp->rowid . ' AND level ' . $price_level, LOG_DEBUG);
4229 $result2 = $this->db->query($sql);
4230 if ($result2) {
4231 $objp2 = $this->db->fetch_object($result2);
4232 if ($objp2) {
4233 $found = 1;
4234 if ($objp2->price_base_type == 'HT') {
4235 $labeltoshowprice .= ' - ' . price($objp2->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4236 $labeltoshowhtmlprice .= ' - ' . price($objp2->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4237 } else {
4238 $labeltoshowprice .= ' - ' . price($objp2->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4239 $labeltoshowhtmlprice .= ' - ' . price($objp2->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4240 }
4241 $outprice_ht = price($objp2->price);
4242 $outprice_ttc = price($objp2->price_ttc);
4243 $outpricebasetype = $objp2->price_base_type;
4244 if (getDolGlobalString('PRODUIT_MULTIPRICES_USE_VAT_PER_LEVEL')) { // using this option is a bug. kept for backward compatibility
4245 $outtva_tx = $objp2->tva_tx; // We use the vat rate on line of multiprice
4246 $outdefault_vat_code = $objp2->default_vat_code; // We use the vat code on line of multiprice
4247 } else {
4248 $outtva_tx = $objp->tva_tx; // We use the vat rate of product, not the one on line of multiprice
4249 $outdefault_vat_code = $objp->default_vat_code; // We use the vat code or product, not the one on line of multiprice
4250 }
4251 }
4252 } else {
4253 dol_print_error($this->db);
4254 }
4255 }
4256
4257 // Price by quantity
4258 if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1 && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_BY_QTY_MULTIPRICES'))) {
4259 $found = 1;
4260 $outqty = $objp->quantity;
4261 $outdiscount = $objp->remise_percent;
4262 if ($objp->quantity == 1) {
4263 $labeltoshowprice .= ' - ' . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/";
4264 $labeltoshowhtmlprice .= ' - ' . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/";
4265 $labeltoshowprice .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
4266 $labeltoshowhtmlprice .= $langs->transnoentities("Unit");
4267 } else {
4268 $labeltoshowprice .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4269 $labeltoshowhtmlprice .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4270 $labeltoshowprice .= $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
4271 $labeltoshowhtmlprice .= $langs->transnoentities("Units");
4272 }
4273
4274 $outprice_ht = price($objp->unitprice);
4275 $outprice_ttc = price($objp->unitprice * (1 + ($objp->tva_tx / 100)));
4276 $outpricebasetype = $objp->price_base_type;
4277 $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
4278 $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
4279 }
4280 if (empty($hidepriceinlabel) && !empty($objp->quantity) && $objp->quantity >= 1) {
4281 $labeltoshowprice .= " (" . price($objp->unitprice, 1, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->trans("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
4282 $labeltoshowhtmlprice .= " (" . price($objp->unitprice, 0, $langs, 0, 0, -1, $conf->currency) . "/" . $langs->transnoentities("Unit") . ")"; // Do not use strtolower because it breaks utf8 encoding
4283 }
4284 if (empty($hidepriceinlabel) && !empty($objp->remise_percent) && $objp->remise_percent >= 1) {
4285 $labeltoshowprice .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4286 $labeltoshowhtmlprice .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4287 }
4288
4289 // Price by customer
4290 if (empty($hidepriceinlabel) && (getDolGlobalString('PRODUIT_CUSTOMER_PRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES'))) {
4291 if (!empty($objp->idprodcustprice)) {
4292 $found = 1;
4293
4294 if ($objp->custprice_base_type == 'HT') {
4295 $labeltoshowprice .= ' - ' . price($objp->custprice, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4296 $labeltoshowhtmlprice .= ' - ' . price($objp->custprice, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4297 } else {
4298 $labeltoshowprice .= ' - ' . price($objp->custprice_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4299 $labeltoshowhtmlprice .= ' - ' . price($objp->custprice_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4300 }
4301
4302 $outprice_ht = price($objp->custprice);
4303 $outprice_ttc = price($objp->custprice_ttc);
4304 $outpricebasetype = $objp->custprice_base_type;
4305 $outtva_tx = $objp->custtva_tx;
4306 $outdefault_vat_code = $objp->custdefault_vat_code;
4307 $outdiscount = $objp->custdiscount_percent;
4308 }
4309 }
4310
4311 // If level no defined or multiprice not found, we used the default price
4312 if (empty($hidepriceinlabel) && !$found) {
4313 if ($objp->price_base_type == 'HT') {
4314 $labeltoshowprice .= ' - ' . price($objp->price, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("HT");
4315 $labeltoshowhtmlprice .= ' - ' . price($objp->price, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("HT");
4316 } else {
4317 $labeltoshowprice .= ' - ' . price($objp->price_ttc, 1, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->trans("TTC");
4318 $labeltoshowhtmlprice .= ' - ' . price($objp->price_ttc, 0, $langs, 0, 0, -1, $conf->currency) . ' ' . $langs->transnoentities("TTC");
4319 }
4320 $outprice_ht = price($objp->price);
4321 $outprice_ttc = price($objp->price_ttc);
4322 $outpricebasetype = $objp->price_base_type;
4323 $outtva_tx = $objp->tva_tx;
4324 $outdefault_vat_code = $objp->default_vat_code;
4325 }
4326
4327 $optiontext = $labeltosearch.$outvalUnits.$labeltoshowprice.$labeltoshowstock;
4328 $optionhtml = $labeltoshowhtml.$outvalUnits.$labeltoshowhtmlprice.$labeltoshowhtmlstock;
4329 $optionhtmlforattribute = dol_escape_htmltag($optionhtml, 0, 0, '', 0, 1);
4330
4331 // Build options
4332 $opt = '<option value="' . $objp->rowid . '"';
4333 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
4334 if (!empty($objp->price_by_qty_rowid) && $objp->price_by_qty_rowid > 0) {
4335 $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 . '"';
4336 }
4337 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
4338 $opt .= ' data-labeltrans="' . dol_escape_htmltag($outlabel_translated, 0, 0, '', 0, 1) . '"';
4339 $opt .= ' data-desctrans="' . dol_escape_htmltag($outdesc_translated) . '"';
4340 }
4341
4342 if ($stocktag == 1) {
4343 $opt .= ' class="product_line_stock_ok" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml, 0, array('strong')).dolPrintHTMLForAttribute($outvalUnits).$labeltoshowhtmlprice.dolPrintHTMLForAttribute($labeltoshowhtmlstock).'"';
4344 //$opt .= ' class="product_line_stock_ok"';
4345 }
4346 if ($stocktag == -1) {
4347 $opt .= ' class="product_line_stock_too_low" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml, 0, array('strong')).dolPrintHTMLForAttribute($outvalUnits).$labeltoshowhtmlprice.dolPrintHTMLForAttribute($labeltoshowhtmlstock).'"';
4348 //$opt .= ' class="product_line_stock_too_low"';
4349 }
4350 $opt .= ' data-html="'.$optionhtmlforattribute.'" data-select-html="'.$optionhtmlforattribute.'"';
4351
4352 $opt .= '>';
4353
4354 // Ref, barcode, country
4355 $opt .= dol_escape_htmltag($optiontext, 0, 0, '', 0, 1);
4356 $outval .= $labeltoshowhtml;
4357
4358 // Units
4359 $outval .= $outvalUnits;
4360
4361 // Price
4362 $outval .= $labeltoshowhtmlprice;
4363
4364 // Stock
4365 $outval .= $labeltoshowhtmlstock;
4366
4367
4368 $parameters = array('objp' => $objp);
4369 $reshook = $hookmanager->executeHooks('constructProductListOption', $parameters); // Note that $action and $object may have been modified by hook
4370 if (empty($reshook)) {
4371 $opt .= $hookmanager->resPrint;
4372 } else {
4373 $opt = $hookmanager->resPrint;
4374 }
4375
4376 $opt .= "</option>\n";
4377 $optJson = array(
4378 'key' => $outkey,
4379 'value' => $outref,
4380 'label' => $outval,
4381 'label2' => $outlabel,
4382 'desc' => $outdesc,
4383 'type' => $outtype,
4384 'price_ht' => price2num($outprice_ht),
4385 'price_ttc' => price2num($outprice_ttc),
4386 'price_ht_locale' => price(price2num($outprice_ht)),
4387 'price_ttc_locale' => price(price2num($outprice_ttc)),
4388 'pricebasetype' => $outpricebasetype,
4389 'tva_tx' => $outtva_tx,
4390 'default_vat_code' => $outdefault_vat_code,
4391 'qty' => $outqty,
4392 'discount' => $outdiscount,
4393 'duration_value' => $outdurationvalue,
4394 'duration_unit' => $outdurationunit,
4395 'pbq' => $outpbq,
4396 'labeltrans' => $outlabel_translated,
4397 'desctrans' => $outdesc_translated,
4398 'ref_customer' => $outrefcust
4399 );
4400 }
4401
4402 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4403
4420 public function select_produits_fournisseurs($socid, $selected = '', $htmlname = 'productid', $filtertype = '', $notused = '', $ajaxoptions = array(), $hidelabel = 0, $alsoproductwithnosupplierprice = 0, $morecss = '', $placeholder = '', $nooutput = 0)
4421 {
4422 // phpcs:enable
4423 global $langs, $conf;
4424 global $price_level, $status, $finished;
4425
4426 if (!isset($status)) {
4427 $status = 1;
4428 }
4429
4430 $selected_input_value = '';
4431 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('PRODUIT_USE_SEARCH_TO_SELECT')) {
4432 if ((int) $selected > 0) {
4433 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4434 $producttmpselect = new Product($this->db);
4435 $producttmpselect->fetch((int) $selected);
4436 $selected_input_value = $producttmpselect->ref;
4437 unset($producttmpselect);
4438 } elseif (preg_match('/^idprod_([0-9]+)$/', (string) $selected, $regtmpsel)) {
4439 // Preselect when a product without supplier price was just created ('idprod_ID' value, used by backtopage of creation popup)
4440 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
4441 $producttmpselect = new Product($this->db);
4442 $producttmpselect->fetch((int) $regtmpsel[1]);
4443 $selected_input_value = $producttmpselect->ref;
4444 unset($producttmpselect);
4445 }
4446
4447 // mode=2 means suppliers products
4448 $urloption = ($socid > 0 ? 'socid=' . $socid . '&' : '') . 'htmlname=' . $htmlname . '&outjson=1&price_level=' . $price_level . '&type=' . $filtertype . '&mode=2&status=' . $status . '&finished=' . $finished . '&alsoproductwithnosupplierprice=' . $alsoproductwithnosupplierprice;
4449
4450 $s = ($hidelabel ? '' : $langs->trans("RefOrLabel") . ' : ') . '<input type="text" class="'.$morecss.'" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . ($placeholder ? ' placeholder="' . $placeholder . '"' : '') . '>';
4451
4452 $s .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/product/ajax/products.php', $urloption, getDolGlobalInt('PRODUIT_USE_SEARCH_TO_SELECT'), 0, $ajaxoptions);
4453 } else {
4454 $s = $this->select_produits_fournisseurs_list($socid, $selected, $htmlname, $filtertype, $notused, '', $status, 0, 0, $alsoproductwithnosupplierprice, $morecss, getDolGlobalInt('SUPPLIER_SHOW_STOCK_IN_PRODUCTS_COMBO'), $placeholder);
4455 }
4456
4457 if ($nooutput) {
4458 return $s;
4459 } else {
4460 print $s;
4461 }
4462 }
4463
4464 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4465
4484 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 = '')
4485 {
4486 // phpcs:enable
4487 global $langs, $conf, $user;
4488 global $hookmanager;
4489
4490 $out = '';
4491 $outarray = array();
4492
4493 $maxlengtharticle = getDolGlobalInt('PRODUCT_MAX_LENGTH_COMBO', 48);
4494
4495 $langs->load('stocks');
4496 // Units
4497 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4498 $langs->load('other');
4499 }
4500
4501 $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,";
4502 $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice, pfp.barcode";
4503 $sql .= ", pfp.multicurrency_code, pfp.multicurrency_unitprice";
4504 $sql .= ", pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.default_vat_code, pfp.fk_soc, s.nom as name";
4505 $sql .= ", pfp.supplier_reputation";
4506 // if we use supplier description of the products
4507 if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
4508 $sql .= ", pfp.desc_fourn as description";
4509 } else {
4510 $sql .= ", p.description";
4511 }
4512 // Units
4513 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4514 $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";
4515 }
4516
4517 // Add select from hooks
4518 $parameters = [];
4519 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListSelect', $parameters); // Note that $action and $object may have been modified by hook
4520 $sql .= $hookmanager->resPrint;
4521
4522 $sql .= " FROM " . $this->db->prefix() . "product as p";
4523
4524 // Add join from hooks
4525 $parameters = [];
4526 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListFrom', $parameters); // Note that $action and $object may have been modified by hook
4527 $sql .= $hookmanager->resPrint;
4528
4529 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON ( p.rowid = pfp.fk_product AND pfp.entity IN (" . getEntity('product') . ") )";
4530 if ($socid > 0) {
4531 $sql .= " AND pfp.fk_soc = " . ((int) $socid);
4532 }
4533 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
4534 // Units
4535 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4536 $sql .= " LEFT JOIN " . $this->db->prefix() . "c_units u ON u.rowid = p.fk_unit";
4537 }
4538 $sql .= " WHERE p.entity IN (" . getEntity('product') . ")";
4539 if ($statut != -1) {
4540 $sql .= " AND p.tobuy = " . ((int) $statut);
4541 }
4542 if (strval($filtertype) != '') {
4543 $sql .= " AND p.fk_product_type = " . ((int) $filtertype);
4544 }
4545
4546 // Add where from hooks
4547 $parameters = array();
4548 $reshook = $hookmanager->executeHooks('selectSuppliersProductsListWhere', $parameters); // Note that $action and $object may have been modified by hook
4549 $sql .= $hookmanager->resPrint;
4550 // Add criteria on ref/label
4551 if ($filterkey != '') {
4552 $sql .= ' AND (';
4553 $prefix = getDolGlobalString('PRODUCT_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
4554 // For natural search
4555 $search_crit = explode(' ', $filterkey);
4556 $i = 0;
4557 if (count($search_crit) > 1) {
4558 $sql .= "(";
4559 }
4560 foreach ($search_crit as $crit) {
4561 if ($i > 0) {
4562 $sql .= " AND ";
4563 }
4564 $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) . "%'";
4565 if (getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
4566 $sql .= " OR pfp.desc_fourn LIKE '" . $this->db->escape($prefix . $crit) . "%'";
4567 }
4568 $sql .= ")";
4569 $i++;
4570 }
4571 if (count($search_crit) > 1) {
4572 $sql .= ")";
4573 }
4574 if (isModEnabled('barcode')) {
4575 $sql .= " OR p.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
4576 $sql .= " OR pfp.barcode LIKE '" . $this->db->escape($prefix . $filterkey) . "%'";
4577 }
4578 $sql .= ')';
4579 }
4580 $sql .= " ORDER BY pfp.ref_fourn DESC, pfp.quantity ASC";
4581 $sql .= $this->db->plimit($limit, 0);
4582
4583 // Build output string
4584
4585 dol_syslog(get_class($this) . "::select_produits_fournisseurs_list", LOG_DEBUG);
4586 $result = $this->db->query($sql);
4587 if ($result) {
4588 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4589 require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
4590
4591 $num = $this->db->num_rows($result);
4592
4593 //$out.='<select class="flat" id="select'.$htmlname.'" name="'.$htmlname.'">'; // remove select to have id same with combo and ajax
4594 $out .= '<select class="flat ' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '">';
4595 if (!$selected) {
4596 $out .= '<option value="-1" selected>' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
4597 } else {
4598 $out .= '<option value="-1">' . ($placeholder ? $placeholder : '&nbsp;') . '</option>';
4599 }
4600
4601 $i = 0;
4602 while ($i < $num) {
4603 $objp = $this->db->fetch_object($result);
4604
4605 if (is_null($objp->idprodfournprice)) {
4606 // There is no supplier price found, we will use the vat rate for sale
4607 $objp->tva_tx = $objp->tva_tx_sale;
4608 $objp->default_vat_code = $objp->default_vat_code_sale;
4609 }
4610
4611 $outkey = $objp->idprodfournprice; // id in table of price
4612 if (!$outkey && $alsoproductwithnosupplierprice) {
4613 $outkey = 'idprod_' . $objp->rowid; // id of product
4614 }
4615
4616 $outref = $objp->ref;
4617 $outbarcode = $objp->barcode;
4618 $outqty = 1;
4619 $outdiscount = 0;
4620 $outtype = $objp->fk_product_type;
4621 $outdurationvalue = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, 0, dol_strlen($objp->duration) - 1) : '';
4622 $outdurationunit = $outtype == Product::TYPE_SERVICE ? substr($objp->duration, -1) : '';
4623
4624 // Units
4625 $outvalUnits = '';
4626 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
4627 if (!empty($objp->unit_short)) {
4628 $outvalUnits .= ' - ' . $objp->unit_short;
4629 }
4630 if (!empty($objp->weight) && $objp->weight_units !== null) {
4631 $unitToShow = showDimensionInBestUnit($objp->weight, $objp->weight_units, 'weight', $langs);
4632 $outvalUnits .= ' - ' . $unitToShow;
4633 }
4634 if ((!empty($objp->length) || !empty($objp->width) || !empty($objp->height)) && $objp->length_units !== null) {
4635 $unitToShow = $objp->length . ' x ' . $objp->width . ' x ' . $objp->height . ' ' . measuringUnitString(0, 'size', $objp->length_units);
4636 $outvalUnits .= ' - ' . $unitToShow;
4637 }
4638 if (!empty($objp->surface) && $objp->surface_units !== null) {
4639 $unitToShow = showDimensionInBestUnit($objp->surface, $objp->surface_units, 'surface', $langs);
4640 $outvalUnits .= ' - ' . $unitToShow;
4641 }
4642 if (!empty($objp->volume) && $objp->volume_units !== null) {
4643 $unitToShow = showDimensionInBestUnit($objp->volume, $objp->volume_units, 'volume', $langs);
4644 $outvalUnits .= ' - ' . $unitToShow;
4645 }
4646 if ($outdurationvalue && $outdurationunit) {
4647 $da = array(
4648 'h' => $langs->trans('Hour'),
4649 'd' => $langs->trans('Day'),
4650 'w' => $langs->trans('Week'),
4651 'm' => $langs->trans('Month'),
4652 'y' => $langs->trans('Year')
4653 );
4654 if (isset($da[$outdurationunit])) {
4655 $outvalUnits .= ' - ' . $outdurationvalue . ' ' . $langs->transnoentities($da[$outdurationunit] . ($outdurationvalue > 1 ? 's' : ''));
4656 }
4657 }
4658 }
4659
4660 $objRef = $objp->ref;
4661 if ($filterkey && $filterkey != '') {
4662 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
4663 }
4664 $objRefFourn = $objp->ref_fourn;
4665 if ($filterkey && $filterkey != '') {
4666 $objRefFourn = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRefFourn, 1);
4667 }
4668 $label = $objp->label;
4669 if ($filterkey && $filterkey != '') {
4670 $label = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $label, 1);
4671 }
4672
4673 switch ($objp->fk_product_type) {
4675 $picto = 'product';
4676 break;
4678 $picto = 'service';
4679 break;
4680 default:
4681 $picto = '';
4682 break;
4683 }
4684
4685 if (empty($picto)) {
4686 $optlabel = '';
4687 } else {
4688 $optlabel = img_object('', $picto, 'class="paddingright classfortooltip"', 0, 0, 1);
4689 }
4690
4691 $optlabel .= $objp->ref;
4692 if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
4693 $optlabel .= ' <span class="opacitymedium">(' . $objp->ref_fourn . ')</span>';
4694 }
4695 if (isModEnabled('barcode') && !empty($objp->barcode)) {
4696 $optlabel .= ' (' . $outbarcode . ')';
4697 }
4698 $optlabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
4699
4700 $outvallabel = $objRef;
4701 if (!empty($objp->idprodfournprice) && ($objp->ref != $objp->ref_fourn)) {
4702 $outvallabel .= ' (' . $objRefFourn . ')';
4703 }
4704 if (isModEnabled('barcode') && !empty($objp->barcode)) {
4705 $outvallabel .= ' (' . $outbarcode . ')';
4706 }
4707 $outvallabel .= ' - ' . dol_trunc($label, $maxlengtharticle);
4708
4709 $outsearchlabel = implode(' ', array_filter(array(
4710 (string) $objp->ref,
4711 (string) $objp->ref_fourn,
4712 (string) $objp->barcode,
4713 (string) $objp->label,
4714 dol_string_nohtmltag((string) $objp->description)
4715 ), function (string $value): bool {
4716 return $value !== '';
4717 }));
4718
4719 // Units
4720 $optlabel .= $outvalUnits;
4721 $outvallabel .= $outvalUnits;
4722
4723 if (!empty($objp->idprodfournprice)) {
4724 $outqty = $objp->quantity;
4725 $outdiscount = $objp->remise_percent;
4726 if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
4727 $prod_supplier = new ProductFournisseur($this->db);
4728 $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
4729 $prod_supplier->id = $objp->fk_product;
4730 $prod_supplier->fourn_qty = $objp->quantity;
4731 $prod_supplier->fourn_tva_tx = $objp->tva_tx;
4732 $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
4733
4734 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4735 $priceparser = new PriceParser($this->db);
4736 $price_result = $priceparser->parseProductSupplier($prod_supplier);
4737 if ($price_result >= 0) {
4738 $objp->fprice = $price_result;
4739 if ($objp->quantity >= 1) {
4740 $objp->unitprice = $objp->fprice / $objp->quantity; // Replace dynamically unitprice
4741 }
4742 }
4743 }
4744 if ($objp->quantity == 1) {
4745 $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
4746 $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/";
4747 $optlabel .= $langs->trans("Unit"); // Do not use strtolower because it breaks utf8 encoding
4748 $outvallabel .= $langs->transnoentities("Unit");
4749 } else {
4750 $optlabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4751 $outvallabel .= ' - ' . price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 0, $langs, 0, 0, -1, $conf->currency) . "/" . $objp->quantity;
4752 $optlabel .= ' ' . $langs->trans("Units"); // Do not use strtolower because it breaks utf8 encoding
4753 $outvallabel .= ' ' . $langs->transnoentities("Units");
4754 }
4755
4756 if ($objp->quantity != 1) {
4757 $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
4758 $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
4759 }
4760 if ($objp->remise_percent >= 1) {
4761 $optlabel .= " - " . $langs->trans("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4762 $outvallabel .= " - " . $langs->transnoentities("Discount") . " : " . vatrate($objp->remise_percent) . ' %';
4763 }
4764 if ($objp->duration) {
4765 $optlabel .= " - " . $objp->duration;
4766 $outvallabel .= " - " . $objp->duration;
4767 }
4768 if (!$socid) {
4769 $optlabel .= " - " . dol_trunc($objp->name, 8);
4770 $outvallabel .= " - " . dol_trunc($objp->name, 8);
4771 }
4772 if ($objp->supplier_reputation) {
4773 //TODO dictionary
4774 $reputations = array('' => $langs->trans('Standard'), 'FAVORITE' => $langs->trans('Favorite'), 'NOTTHGOOD' => $langs->trans('NotTheGoodQualitySupplier'), 'DONOTORDER' => $langs->trans('DoNotOrderThisProductToThisSupplier'));
4775
4776 $optlabel .= " - " . $reputations[$objp->supplier_reputation];
4777 $outvallabel .= " - " . $reputations[$objp->supplier_reputation];
4778 }
4779 } else {
4780 $optlabel .= " - <span class='opacitymedium'>" . $langs->trans("NoPriceDefinedForThisSupplier") . '</span>';
4781 $outvallabel .= ' - ' . $langs->transnoentities("NoPriceDefinedForThisSupplier");
4782 }
4783
4784 if (isModEnabled('stock') && $showstockinlist && isset($objp->stock) && ($objp->fk_product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
4785 $novirtualstock = ($showstockinlist == 2);
4786
4787 if ($user->hasRight('stock', 'lire')) {
4788 $outvallabel .= ' - ' . $langs->trans("Stock") . ': ' . price(price2num($objp->stock, 'MS'), 0, $langs, 0, 0);
4789
4790 if ($objp->stock > 0) {
4791 $optlabel .= ' - <span class="product_line_stock_ok">';
4792 } elseif ($objp->stock <= 0) {
4793 $optlabel .= ' - <span class="product_line_stock_too_low">';
4794 }
4795 $optlabel .= $langs->transnoentities("Stock") . ':' . price(price2num($objp->stock, 'MS'));
4796 $optlabel .= '</span>';
4797 if (empty($novirtualstock) && getDolGlobalString('STOCK_SHOW_VIRTUAL_STOCK_IN_PRODUCTS_COMBO')) { // Warning, this option may slow down combo list generation
4798 $langs->load("stocks");
4799
4800 $tmpproduct = new Product($this->db);
4801 $tmpproduct->fetch($objp->rowid, '', '', '', 1, 1, 1); // Load product without lang and prices arrays (we just need to make ->virtual_stock() after)
4802 $tmpproduct->load_virtual_stock();
4803 $virtualstock = $tmpproduct->stock_theorique;
4804
4805 $outvallabel .= ' - ' . $langs->trans("VirtualStock") . ':' . $virtualstock;
4806
4807 $optlabel .= ' - ' . $langs->transnoentities("VirtualStock") . ':';
4808 if ($virtualstock > 0) {
4809 $optlabel .= '<span class="product_line_stock_ok">';
4810 } elseif ($virtualstock <= 0) {
4811 $optlabel .= '<span class="product_line_stock_too_low">';
4812 }
4813 $optlabel .= $virtualstock;
4814 $optlabel .= '</span>';
4815
4816 unset($tmpproduct);
4817 }
4818 }
4819 }
4820
4821 $optstart = '<option value="' . $outkey . '"';
4822 if ($selected && preg_match('/^idprod_/', (string) $selected) && (string) $selected == 'idprod_'.$objp->rowid) {
4823 $optstart .= ' selected';
4824 } elseif ($selected && (string) $selected == (string) $objp->idprodfournprice) {
4825 $optstart .= ' selected';
4826 }
4827
4828 if (empty($objp->idprodfournprice) && empty($alsoproductwithnosupplierprice)) {
4829 $optstart .= ' disabled';
4830 }
4831
4832 if (!empty($objp->idprodfournprice) && $objp->idprodfournprice > 0) {
4833 $optstart .= ' data-product-id="' . dol_escape_htmltag($objp->rowid) . '"';
4834 $optstart .= ' data-price-id="' . dol_escape_htmltag($objp->idprodfournprice) . '"';
4835 $optstart .= ' data-qty="' . dol_escape_htmltag($objp->quantity) . '"';
4836 $optstart .= ' data-up="' . dol_escape_htmltag(price2num($objp->unitprice)) . '"'; // the price with numeric international format
4837 $optstart .= ' data-up-locale="' . dol_escape_htmltag(price($objp->unitprice)) . '"'; // the price formatted in user language
4838 $optstart .= ' data-discount="' . dol_escape_htmltag((string) $outdiscount) . '"';
4839 $optstart .= ' data-tvatx="' . dol_escape_htmltag(price2num($objp->tva_tx)) . '"'; // the rate with numeric international format
4840 $optstart .= ' data-tvatx-formated="' . dol_escape_htmltag(price($objp->tva_tx, 0, $langs, 1, -1, 2)) . '"'; // the rate formatted in user language
4841 $optstart .= ' data-default-vat-code="' . dol_escape_htmltag($objp->default_vat_code) . '"';
4842 $optstart .= ' data-supplier-ref="' . dol_escape_htmltag($objp->ref_fourn) . '"';
4843 if (isModEnabled('multicurrency')) {
4844 $optstart .= ' data-multicurrency-code="' . dol_escape_htmltag($objp->multicurrency_code) . '"';
4845 $optstart .= ' data-multicurrency-unitprice="' . dol_escape_htmltag(price2num($objp->multicurrency_unitprice)) . '"'; // the price with numeric international format
4846 }
4847 }
4848 $optstart .= ' data-description="' . dol_escape_htmltag($objp->description, 0, 1) . '"';
4849 $optstart .= ' data-search="' . dol_escape_htmltag($outsearchlabel) . '"';
4850
4851 // set $parameters to call hook
4852 $outarrayentry = array(
4853 'key' => $outkey,
4854 'value' => $outref,
4855 'label' => $outvallabel,
4856 'labelhtml' => $optlabel,
4857 'qty' => $outqty,
4858 'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
4859 'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
4860 'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
4861 'tva_tx_formated' => price($objp->tva_tx, 0, $langs, 1, -1, 2),
4862 'tva_tx' => price2num($objp->tva_tx),
4863 'default_vat_code' => $objp->default_vat_code,
4864 'supplier_ref' => $objp->ref_fourn,
4865 'discount' => $outdiscount,
4866 'type' => $outtype,
4867 'duration_value' => $outdurationvalue,
4868 'duration_unit' => $outdurationunit,
4869 'disabled' => empty($objp->idprodfournprice),
4870 'description' => $objp->description
4871 );
4872 if (isModEnabled('multicurrency')) {
4873 $outarrayentry['multicurrency_code'] = $objp->multicurrency_code;
4874 $outarrayentry['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4875 }
4876 $parameters = array(
4877 'objp' => &$objp,
4878 'optstart' => &$optstart,
4879 'optlabel' => &$optlabel,
4880 'outvallabel' => &$outvallabel,
4881 'outarrayentry' => &$outarrayentry,
4882 'fk_soc' => $socid
4883 );
4884 $reshook = $hookmanager->executeHooks('selectProduitsFournisseurListOption', $parameters, $this);
4885
4886
4887 // Add new entry
4888 // "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
4889 // "label" value of json key array is used by jQuery automatically as text for combo box
4890 $out .= $optstart . ' data-html="' . dol_escape_htmltag($optlabel) . '">' . $optlabel . "</option>\n";
4891 $outarraypush = array(
4892 'key' => $outkey,
4893 'value' => $outref,
4894 'label' => $outvallabel,
4895 'labelhtml' => $optlabel,
4896 'qty' => $outqty,
4897 'price_qty_ht' => price2num($objp->fprice, 'MU'), // Keep higher resolution for price for the min qty
4898 'price_qty_ht_locale' => price($objp->fprice),
4899 'price_unit_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price
4900 'price_unit_ht_locale' => price($objp->unitprice),
4901 'price_ht' => price2num($objp->unitprice, 'MU'), // This is used to fill the Unit Price (for compatibility)
4902 'tva_tx_formated' => price($objp->tva_tx),
4903 'tva_tx' => price2num($objp->tva_tx),
4904 'default_vat_code' => $objp->default_vat_code,
4905 'supplier_ref' => $objp->ref_fourn,
4906 'discount' => $outdiscount,
4907 'type' => $outtype,
4908 'duration_value' => $outdurationvalue,
4909 'duration_unit' => $outdurationunit,
4910 'disabled' => empty($objp->idprodfournprice),
4911 'description' => $objp->description
4912 );
4913 if (isModEnabled('multicurrency')) {
4914 $outarraypush['multicurrency_code'] = $objp->multicurrency_code;
4915 $outarraypush['multicurrency_unitprice'] = price2num($objp->multicurrency_unitprice, 'MU');
4916 }
4917 array_push($outarray, $outarraypush);
4918
4919 // Example of var_dump $outarray
4920 // array(1) {[0]=>array(6) {[key"]=>string(1) "2" ["value"]=>string(3) "ppp"
4921 // ["label"]=>string(76) "ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/unit (20,00 Euros/unit)"
4922 // ["qty"]=>string(1) "1" ["discount"]=>string(1) "0" ["disabled"]=>bool(false)
4923 //}
4924 //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4925 //$outval=array('label'=>'ppp (<strong>f</strong>ff2) - ppp - 20,00 Euros/ Unit (20,00 Euros/unit)');
4926 //var_dump($outval); var_dump(utf8_check($outval)); var_dump(json_encode($outval));
4927
4928 $i++;
4929 }
4930 $out .= '</select>';
4931
4932 $this->db->free($result);
4933
4934 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
4935 $out .= ajax_combobox($htmlname);
4936 } else {
4937 dol_print_error($this->db);
4938 }
4939
4940 if (empty($outputmode)) {
4941 return $out;
4942 }
4943 return $outarray;
4944 }
4945
4946 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4947
4956 public function select_product_fourn_price($productid, $htmlname = 'productfournpriceid', $selected_supplier = 0)
4957 {
4958 // phpcs:enable
4959 global $langs, $conf;
4960
4961 $langs->load('stocks');
4962
4963 $sql = "SELECT p.rowid, p.ref, p.label, p.price, p.duration, pfp.fk_soc,";
4964 $sql .= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice,";
4965 $sql .= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
4966 $sql .= " FROM " . $this->db->prefix() . "product as p";
4967 $sql .= " LEFT JOIN " . $this->db->prefix() . "product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
4968 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON pfp.fk_soc = s.rowid";
4969 $sql .= " WHERE pfp.entity IN (" . getEntity('productsupplierprice') . ")";
4970 $sql .= " AND p.tobuy = 1";
4971 $sql .= " AND s.fournisseur = 1";
4972 $sql .= " AND p.rowid = " . ((int) $productid);
4973 if (!getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED')) {
4974 $sql .= " ORDER BY s.nom, pfp.ref_fourn DESC";
4975 } else {
4976 $sql .= " ORDER BY pfp.unitprice - pfp.unitprice * pfp.remise_percent / 100 ASC";
4977 }
4978
4979 dol_syslog(get_class($this) . "::select_product_fourn_price", LOG_DEBUG);
4980 $result = $this->db->query($sql);
4981
4982 if ($result) {
4983 $num = $this->db->num_rows($result);
4984
4985 $form = '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
4986
4987 if (!$num) {
4988 $form .= '<option value="0">-- ' . $langs->trans("NoSupplierPriceDefinedForThisProduct") . ' --</option>';
4989 } else {
4990 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
4991 $form .= '<option value="0">&nbsp;</option>';
4992
4993 $i = 0;
4994 while ($i < $num) {
4995 $objp = $this->db->fetch_object($result);
4996
4997 $opt = '<option value="' . $objp->idprodfournprice . '"';
4998 //if there is only one supplier, preselect it
4999 if ($num == 1 || ($selected_supplier > 0 && $objp->fk_soc == $selected_supplier) || ($i == 0 && getDolGlobalString('PRODUCT_BEST_SUPPLIER_PRICE_PRESELECTED'))) {
5000 $opt .= ' selected';
5001 }
5002 $opt .= '>' . $objp->name . ' - ' . $objp->ref_fourn . ' - ';
5003
5004 if (isModEnabled('dynamicprices') && !empty($objp->fk_supplier_price_expression)) {
5005 $prod_supplier = new ProductFournisseur($this->db);
5006 $prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
5007 $prod_supplier->id = $productid;
5008 $prod_supplier->fourn_qty = $objp->quantity;
5009 $prod_supplier->fourn_tva_tx = $objp->tva_tx;
5010 $prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
5011
5012 require_once DOL_DOCUMENT_ROOT . '/product/dynamic_price/class/price_parser.class.php';
5013 $priceparser = new PriceParser($this->db);
5014 $price_result = $priceparser->parseProductSupplier($prod_supplier);
5015 if ($price_result >= 0) {
5016 $objp->fprice = $price_result;
5017 if ($objp->quantity >= 1) {
5018 $objp->unitprice = $objp->fprice / $objp->quantity;
5019 }
5020 }
5021 }
5022 if ($objp->quantity == 1) {
5023 $opt .= price($objp->fprice * (getDolGlobalString('DISPLAY_DISCOUNTED_SUPPLIER_PRICE') ? (1 - $objp->remise_percent / 100) : 1), 1, $langs, 0, 0, -1, $conf->currency) . "/";
5024 }
5025
5026 $opt .= $objp->quantity . ' ';
5027
5028 if ($objp->quantity == 1) {
5029 $opt .= $langs->trans("Unit");
5030 } else {
5031 $opt .= $langs->trans("Units");
5032 }
5033 if ($objp->quantity > 1) {
5034 $opt .= " - ";
5035 $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");
5036 }
5037 if ($objp->duration) {
5038 $opt .= " - " . $objp->duration;
5039 }
5040 $opt .= "</option>\n";
5041
5042 $form .= $opt;
5043 $i++;
5044 }
5045 }
5046
5047 $form .= '</select>';
5048 $this->db->free($result);
5049 return $form;
5050 } else {
5051 dol_print_error($this->db);
5052 return '';
5053 }
5054 }
5055
5056
5057 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5064 {
5065 // phpcs:enable
5066 global $langs, $hookmanager;
5067
5068 $num = count($this->cache_conditions_paiements);
5069 if ($num > 0) {
5070 return 0; // Cache already loaded
5071 }
5072
5073 dol_syslog(__METHOD__, LOG_DEBUG);
5074
5075 $this->cache_conditions_paiements = array();
5076
5077 $sql = "SELECT rowid, code, libelle as label, deposit_percent, entity";
5078 $sql .= " FROM " . $this->db->prefix() . 'c_payment_term';
5079 $sql .= " WHERE entity IN (" . getEntity('c_payment_term') . ")";
5080 $sql .= " AND active > 0";
5081 $sql .= " ORDER BY sortorder";
5082
5083 $resql = $this->db->query($sql);
5084 if ($resql) {
5085 $num = $this->db->num_rows($resql);
5086 $i = 0;
5087 while ($i < $num) {
5088 $obj = $this->db->fetch_object($resql);
5089
5090 // If a translation exists, we use it, otherwise, we take the label by default
5091 $label = ($langs->trans("PaymentConditionShort" . $obj->code) != "PaymentConditionShort" . $obj->code ? $langs->trans("PaymentConditionShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5092
5093 $this->cache_conditions_paiements[$obj->rowid]['code'] = (string) $obj->code;
5094 $this->cache_conditions_paiements[$obj->rowid]['label'] = (string) $label;
5095 $this->cache_conditions_paiements[$obj->rowid]['deposit_percent'] = (string) $obj->deposit_percent;
5096 $this->cache_conditions_paiements[$obj->rowid]['entity'] = (int) $obj->entity;
5097
5098 $i++;
5099 }
5100
5101 $parameters = array('context' => 'paymentterm');
5102 $reshook = $hookmanager->executeHooks('loadDictionaryCache', $parameters, $this); // Note that $action and $object may have been modified by hook
5103 if (empty($reshook)) {
5104 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
5105 $this->cache_conditions_paiements = array_merge($this->cache_conditions_paiements, $hookmanager->resArray);
5106 }
5107 } else {
5108 $this->cache_conditions_paiements = $hookmanager->resArray;
5109 }
5110
5111 //$this->cache_conditions_paiements=dol_sort_array($this->cache_conditions_paiements, 'label', 'asc', 0, 0, 1); // We use the field sortorder of table
5112
5113 return $num;
5114 } else {
5115 dol_print_error($this->db);
5116 return -1;
5117 }
5118 }
5119
5120 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5121
5128 {
5129 // phpcs:enable
5130 $factureRec = new FactureRec($this->db);
5131
5132 $this->cache_rule_for_lines_dates = $factureRec->fields['rule_for_lines_dates']['arrayofkeyval'];
5133
5134 if (empty($this->cache_rule_for_lines_dates)) {
5135 return -1;
5136 }
5137
5138 return 1;
5139 }
5140
5141 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5142
5148 public function load_cache_availability()
5149 {
5150 // phpcs:enable
5151 global $langs;
5152
5153 $num = count($this->cache_availability); // TODO Use $conf->cache['availability'] instead of $this->cache_availability
5154 if ($num > 0) {
5155 return 0; // Cache already loaded
5156 }
5157
5158 dol_syslog(__METHOD__, LOG_DEBUG);
5159
5160 $this->cache_availability = array();
5161
5162 $langs->load('propal');
5163
5164 $sql = "SELECT rowid, code, label, position";
5165 $sql .= " FROM " . $this->db->prefix() . 'c_availability';
5166 $sql .= " WHERE active > 0";
5167
5168 $resql = $this->db->query($sql);
5169 if ($resql) {
5170 $num = $this->db->num_rows($resql);
5171 $i = 0;
5172 while ($i < $num) {
5173 $obj = $this->db->fetch_object($resql);
5174
5175 // If a translation exists, we use is, otherwise, we take the label by default
5176 $label = ($langs->trans("AvailabilityType" . $obj->code) != "AvailabilityType" . $obj->code ? $langs->trans("AvailabilityType" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5177 $this->cache_availability[$obj->rowid]['code'] = (string) $obj->code;
5178 $this->cache_availability[$obj->rowid]['label'] = (string) $label;
5179 $this->cache_availability[$obj->rowid]['position'] = (int) $obj->position;
5180 $i++;
5181 }
5182
5183 // @phan-suppress-next-line PhanTypeMismatchProperty PhanTypeMismatchDimFetch
5184 $this->cache_availability = dol_sort_array($this->cache_availability, 'position', 'asc', 0, 0, 1);
5185
5186 return $num;
5187 } else {
5188 dol_print_error($this->db);
5189 return -1;
5190 }
5191 }
5192
5204 public function selectAvailabilityDelay($selected = '', $htmlname = 'availid', $filtertype = '', $addempty = 0, $morecss = '', $noouput = 0)
5205 {
5206 global $langs, $user;
5207
5208 $this->load_cache_availability();
5209
5210 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
5211
5212 $out = '<select id="' . $htmlname . '" class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5213 if ($addempty) {
5214 $out .= '<option value="-1">'.(is_numeric($addempty) ? '&nbsp;' : $langs->trans($addempty)).'</option>';
5215 }
5216 foreach ($this->cache_availability as $id => $arrayavailability) {
5217 if ($selected == $id) {
5218 $out .= '<option value="' . $id . '" selected>';
5219 } else {
5220 $out .= '<option value="' . $id . '">';
5221 }
5222 $out .= dol_escape_htmltag($arrayavailability['label']);
5223 $out .= '</option>';
5224 }
5225 $out .= '</select>';
5226 if ($user->admin) {
5227 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5228 }
5229 $out .= ajax_combobox($htmlname);
5230
5231 if ($noouput) {
5232 return $out;
5233 } else {
5234 print $out;
5235 return '';
5236 }
5237 }
5238
5244 public function loadCacheInputReason()
5245 {
5246 global $langs;
5247
5248 $num = count($this->cache_demand_reason); // TODO Use $conf->cache['input_reason'] instead of $this->cache_demand_reason
5249 if ($num > 0) {
5250 return 0; // Cache already loaded
5251 }
5252
5253 $sql = "SELECT rowid, code, label";
5254 $sql .= " FROM " . $this->db->prefix() . 'c_input_reason';
5255 $sql .= " WHERE active > 0";
5256
5257 $resql = $this->db->query($sql);
5258 if ($resql) {
5259 $num = $this->db->num_rows($resql);
5260 $i = 0;
5262 $tmparray = array();
5263 while ($i < $num) {
5264 $obj = $this->db->fetch_object($resql);
5265
5266 // If a translation exists, we use is, otherwise, we take the label by default
5267 $label = ($obj->label != '-' ? (string) $obj->label : '');
5268 if ($langs->trans("DemandReasonType" . $obj->code) != "DemandReasonType" . $obj->code) {
5269 $label = $langs->trans("DemandReasonType" . $obj->code); // So translation key DemandReasonTypeSRC_XXX will work
5270 }
5271 if ($langs->trans($obj->code) != $obj->code) {
5272 $label = $langs->trans($obj->code); // So translation key SRC_XXX will work
5273 }
5274
5275 $tmparray[(int) $obj->rowid]
5276 = array(
5277 'id' => (int) $obj->rowid,
5278 'code' => (string) $obj->code,
5279 'label' => $label,
5280 );
5281 $i++;
5282 }
5283
5284 $this->cache_demand_reason = dol_sort_array($tmparray, 'label', 'asc', 0, 0, 1);
5285
5286 unset($tmparray);
5287 return $num;
5288 } else {
5289 dol_print_error($this->db);
5290 return -1;
5291 }
5292 }
5293
5306 public function selectInputReason($selected = '', $htmlname = 'demandreasonid', $exclude = '', $addempty = 0, $morecss = '', $notooltip = 0)
5307 {
5308 global $langs, $user;
5309
5310 $this->loadCacheInputReason();
5311
5312 print '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="select_' . $htmlname . '" name="' . $htmlname . '">';
5313 if ($addempty) {
5314 print '<option value="0"' . (empty($selected) ? ' selected' : '') . '>&nbsp;</option>';
5315 }
5316 foreach ($this->cache_demand_reason as $id => $arraydemandreason) {
5317 if ($arraydemandreason['code'] == $exclude) {
5318 continue;
5319 }
5320
5321 if ($selected && ($selected == $arraydemandreason['id'] || $selected == $arraydemandreason['code'])) {
5322 print '<option value="' . $arraydemandreason['id'] . '" selected>';
5323 } else {
5324 print '<option value="' . $arraydemandreason['id'] . '">';
5325 }
5326 $label = $arraydemandreason['label']; // Translation of label was already done into the ->loadCacheInputReason
5327 print $langs->trans($label);
5328 print '</option>';
5329 }
5330 print '</select>';
5331 if ($user->admin && empty($notooltip)) {
5332 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5333 }
5334 print ajax_combobox('select_' . $htmlname);
5335 }
5336
5337 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5338
5345 {
5346 // phpcs:enable
5347 global $langs, $hookmanager;
5348
5349 $num = count($this->cache_types_paiements); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_types_paiements
5350 if ($num > 0) {
5351 return $num; // Cache already loaded
5352 }
5353
5354 dol_syslog(__METHOD__, LOG_DEBUG);
5355
5356 $this->cache_types_paiements = array();
5357
5358 $sql = "SELECT id, code, libelle as label, type, entity, active";
5359 $sql .= " FROM " . $this->db->prefix() . "c_paiement";
5360 $sql .= " WHERE entity IN (" . getEntity('c_paiement') . ")";
5361
5362 $resql = $this->db->query($sql);
5363 if ($resql) {
5364 $num = $this->db->num_rows($resql);
5365 $i = 0;
5366 while ($i < $num) {
5367 $obj = $this->db->fetch_object($resql);
5368
5369 // If a translation exists, we use is, otherwise, we take the label by default
5370 $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5371 $this->cache_types_paiements[(int) $obj->id]['id'] = (int) $obj->id;
5372 $this->cache_types_paiements[(int) $obj->id]['code'] = (string) $obj->code;
5373 $this->cache_types_paiements[(int) $obj->id]['label'] = (string) $label;
5374 $this->cache_types_paiements[(int) $obj->id]['type'] = (int) $obj->type;
5375 $this->cache_types_paiements[(int) $obj->id]['entity'] = (int) $obj->entity;
5376 $this->cache_types_paiements[(int) $obj->id]['active'] = (int) $obj->active;
5377 $i++;
5378 }
5379
5380 $parameters = array('context' => 'paymenttype');
5381 $reshook = $hookmanager->executeHooks('loadDictionaryCache', $parameters, $this); // Note that $action and $object may have been modified by hook
5382 if (empty($reshook)) {
5383 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
5384 $this->cache_types_paiements = array_merge($this->cache_types_paiements, $hookmanager->resArray);
5385 }
5386 } else {
5387 $this->cache_types_paiements = $hookmanager->resArray;
5388 }
5389
5390 $this->cache_types_paiements = dol_sort_array($this->cache_types_paiements, 'label', 'asc', 0, 0, 1);
5391
5392 return $num;
5393 } else {
5394 dol_print_error($this->db);
5395 return -1;
5396 }
5397 }
5398
5399
5400 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5401
5420 public function select_conditions_paiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1, $noprint = 0)
5421 {
5422 // phpcs:enable
5423 $out = $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, $noinfoadmin, $morecss, $deposit_percent);
5424 if (empty($noprint)) {
5425 print $out;
5426 } else {
5427 return $out;
5428 }
5429 }
5430
5431
5448 public function getSelectConditionsPaiements($selected = 0, $htmlname = 'condid', $filtertype = -1, $addempty = 0, $noinfoadmin = 0, $morecss = '', $deposit_percent = -1)
5449 {
5450 global $langs, $user;
5451
5452 $out = '';
5453 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
5454
5456
5457 // Set default value if not already set by caller
5458 if (empty($selected) && strpos($htmlname, 'search_') !== 0 && getDolGlobalInt('MAIN_DEFAULT_PAYMENT_TERM_ID')) {
5459 dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TERM_ID", LOG_NOTICE);
5460 $selected = getDolGlobalInt('MAIN_DEFAULT_PAYMENT_TERM_ID');
5461 }
5462
5463 $out .= '<select id="' . $htmlname . '" class="flat selectpaymentterms' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5464 if ($addempty) {
5465 $out .= '<option value="0">&nbsp;</option>';
5466 }
5467
5468 $selectedDepositPercent = null;
5469
5470 foreach ($this->cache_conditions_paiements as $id => $arrayconditions) {
5471 if ($filtertype <= 0 && !empty($arrayconditions['deposit_percent'])) {
5472 continue;
5473 }
5474
5475 if ($selected == $id) {
5476 $selectedDepositPercent = $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'];
5477 $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '" selected>';
5478 } else {
5479 $out .= '<option value="' . $id . '" data-deposit_percent="' . $arrayconditions['deposit_percent'] . '">';
5480 }
5481 $label = $arrayconditions['label'];
5482
5483 if (!empty($arrayconditions['deposit_percent'])) {
5484 $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $arrayconditions['deposit_percent'], $label);
5485 }
5486
5487 $out .= $label;
5488 $out .= '</option>';
5489 }
5490 $out .= '</select>';
5491 if ($user->admin && empty($noinfoadmin)) {
5492 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5493 }
5494 $out .= ajax_combobox($htmlname);
5495
5496 if ($deposit_percent >= 0) {
5497 $out .= ' <span id="' . $htmlname . '_deposit_percent_container"' . (empty($selectedDepositPercent) ? ' style="display: none"' : '') . '>';
5498 $out .= $langs->trans('DepositPercent') . ' : ';
5499 $out .= '<input id="' . $htmlname . '_deposit_percent" name="' . $htmlname . '_deposit_percent" class="maxwidth50" value="' . $deposit_percent . '" />';
5500 $out .= '</span>';
5501 $out .= '
5502 <script nonce="' . getNonce() . '">
5503 $(document).ready(function () {
5504 $("#' . $htmlname . '").change(function () {
5505 let $selected = $(this).find("option:selected");
5506 let depositPercent = $selected.attr("data-deposit_percent");
5507
5508 if (depositPercent.length > 0) {
5509 $("#' . $htmlname . '_deposit_percent_container").show().find("#' . $htmlname . '_deposit_percent").val(depositPercent);
5510 } else {
5511 $("#' . $htmlname . '_deposit_percent_container").hide();
5512 }
5513
5514 return true;
5515 });
5516 });
5517 </script>';
5518 }
5519
5520 return $out;
5521 }
5522
5523
5532 public function getSelectRuleForLinesDates($selected = '', $htmlname = 'rule_for_lines_dates', $addempty = 0)
5533 {
5534 global $langs;
5535
5536 $out = '';
5537
5539
5540 $out .= '<select id="' . $htmlname . '" class="flat selectbillingterm" name="' . $htmlname . '">';
5541 if ($addempty) {
5542 $out .= '<option value="-1">&nbsp;</option>';
5543 }
5544
5545
5546 foreach ($this->cache_rule_for_lines_dates as $rule_for_lines_dates_key => $rule_for_lines_dates_name) {
5547 if ($selected == $rule_for_lines_dates_key) {
5548 $out .= '<option value="' . $rule_for_lines_dates_key . '" selected>';
5549 } else {
5550 $out .= '<option value="' . $rule_for_lines_dates_key . '">';
5551 }
5552
5553 $out .= $langs->trans($rule_for_lines_dates_name);
5554 $out .= '</option>';
5555 }
5556 $out .= '</select>';
5557
5558 $out .= ajax_combobox($htmlname);
5559
5560 return $out;
5561 }
5562
5563
5564 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5565
5582 public function select_types_paiements($selected = '', $htmlname = 'paiementtype', $filtertype = '', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '', $nooutput = 0)
5583 {
5584 // phpcs:enable
5585 global $langs, $user;
5586
5587 $out = '';
5588
5589 dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $filtertype . ", " . $format, LOG_DEBUG);
5590
5591 $filterarray = array();
5592 if ($filtertype == 'CRDT') {
5593 $filterarray = array(0, 2, 3);
5594 } elseif ($filtertype == 'DBIT') {
5595 $filterarray = array(1, 2, 3);
5596 } elseif ($filtertype != '' && $filtertype != '-1') {
5597 $filterarray = explode(',', $filtertype);
5598 }
5599
5601
5602 // Set default value if not already set by caller
5603 if (empty($selected) && strpos($htmlname, 'search_') !== 0 && getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID')) {
5604 dol_syslog(__METHOD__ . "Using deprecated option MAIN_DEFAULT_PAYMENT_TYPE_ID", LOG_NOTICE);
5605 $selected = getDolGlobalString('MAIN_DEFAULT_PAYMENT_TYPE_ID');
5606 }
5607
5608 $out .= '<select id="select' . $htmlname . '" class="flat selectpaymenttypes' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5609 if ($empty) {
5610 $out .= '<option value="">&nbsp;</option>';
5611 }
5612 foreach ($this->cache_types_paiements as $id => $arraytypes) {
5613 // If not good status
5614 if ($active >= 0 && $arraytypes['active'] != $active) {
5615 continue;
5616 }
5617
5618 // We skip of the user requested to filter on specific payment methods
5619 if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
5620 continue;
5621 }
5622
5623 // We discard empty lines if showempty is on because an empty line has already been output.
5624 if ($empty && empty($arraytypes['code'])) {
5625 continue;
5626 }
5627
5628 if ($format == 0) {
5629 $out .= '<option value="' . $id . '" data-code="'.$arraytypes['code'].'"';
5630 } elseif ($format == 1) {
5631 $out .= '<option value="' . $arraytypes['code'] . '"';
5632 } elseif ($format == 2) {
5633 $out .= '<option value="' . $arraytypes['code'] . '"';
5634 } elseif ($format == 3) {
5635 $out .= '<option value="' . $id . '"';
5636 }
5637 // Print attribute selected or not
5638 if ($format == 1 || $format == 2) {
5639 if ($selected == $arraytypes['code']) {
5640 $out .= ' selected';
5641 }
5642 } else {
5643 if ($selected == $id) {
5644 $out .= ' selected';
5645 }
5646 }
5647 $out .= '>';
5648 $value = '';
5649 if ($format == 0) {
5650 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5651 } elseif ($format == 1) {
5652 $value = $arraytypes['code'];
5653 } elseif ($format == 2) {
5654 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5655 } elseif ($format == 3) {
5656 $value = $arraytypes['code'];
5657 }
5658 $out .= $value ? $value : '&nbsp;';
5659 $out .= '</option>';
5660 }
5661 $out .= '</select>';
5662 if ($user->admin && !$noadmininfo) {
5663 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5664 }
5665 $out .= ajax_combobox('select' . $htmlname);
5666
5667 if (empty($nooutput)) {
5668 print $out;
5669 } else {
5670 return $out;
5671 }
5672 }
5673
5674
5683 public function selectPriceBaseType($selected = '', $htmlname = 'price_base_type', $addjscombo = 0)
5684 {
5685 global $langs;
5686
5687 $return = '<select class="flat maxwidth100" id="select_' . $htmlname . '" name="' . $htmlname . '">';
5688 $options = array(
5689 'HT' => $langs->trans("HT"),
5690 'TTC' => $langs->trans("TTC")
5691 );
5692 foreach ($options as $id => $value) {
5693 if ($selected == $id) {
5694 $return .= '<option value="' . $id . '" selected>' . $value;
5695 } else {
5696 $return .= '<option value="' . $id . '">' . $value;
5697 }
5698 $return .= '</option>';
5699 }
5700 $return .= '</select>';
5701 if ($addjscombo) {
5702 $return .= ajax_combobox('select_' . $htmlname);
5703 }
5704
5705 return $return;
5706 }
5707
5708 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5709
5716 {
5717 // phpcs:enable
5718 global $langs;
5719
5720 $num = count($this->cache_transport_mode); // TODO Use $conf->cache['payment_mode'] instead of $this->cache_transport_mode
5721 if ($num > 0) {
5722 return $num; // Cache already loaded
5723 }
5724
5725 dol_syslog(__METHOD__, LOG_DEBUG);
5726
5727 $this->cache_transport_mode = array();
5728
5729 $sql = "SELECT rowid, code, label, active";
5730 $sql .= " FROM " . $this->db->prefix() . "c_transport_mode";
5731 $sql .= " WHERE entity IN (" . getEntity('c_transport_mode') . ")";
5732
5733 $resql = $this->db->query($sql);
5734 if ($resql) {
5735 $num = $this->db->num_rows($resql);
5736 $i = 0;
5737 while ($i < $num) {
5738 $obj = $this->db->fetch_object($resql);
5739
5740 // If traduction exist, we use it else we take the default label
5741 $label = ($langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) != "PaymentTypeShort" . $obj->code ? $langs->transnoentitiesnoconv("PaymentTypeShort" . $obj->code) : ($obj->label != '-' ? $obj->label : ''));
5742 $this->cache_transport_mode[(int) $obj->rowid]
5743 = array(
5744 'rowid' => (int) $obj->rowid,
5745 'code' => (string) $obj->code,
5746 'label' => (string) $label,
5747 'active' => (int) $obj->active,
5748 );
5749 $i++;
5750 }
5751
5752 $this->cache_transport_mode = dol_sort_array($this->cache_transport_mode, 'label', 'asc', 0, 0, 1);
5753
5754 return $num;
5755 } else {
5756 dol_print_error($this->db);
5757 return -1;
5758 }
5759 }
5760
5774 public function selectTransportMode($selected = '', $htmlname = 'transportmode', $format = 0, $empty = 1, $noadmininfo = 0, $maxlength = 0, $active = 1, $morecss = '')
5775 {
5776 global $langs, $user;
5777
5778 dol_syslog(__METHOD__ . " " . $selected . ", " . $htmlname . ", " . $format, LOG_DEBUG);
5779
5781
5782 print '<select id="select' . $htmlname . '" class="flat selectmodetransport' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
5783 if ($empty) {
5784 print '<option value="">&nbsp;</option>';
5785 }
5786 foreach ($this->cache_transport_mode as $id => $arraytypes) {
5787 // If not good status
5788 if ($active >= 0 && $arraytypes['active'] != $active) {
5789 continue;
5790 }
5791
5792 // We discard empty line if showempty is on because an empty line has already been output.
5793 if ($empty && empty($arraytypes['code'])) {
5794 continue;
5795 }
5796
5797 if ($format == 0) {
5798 print '<option value="' . $id . '"';
5799 } elseif ($format == 1) {
5800 print '<option value="' . $arraytypes['code'] . '"';
5801 } elseif ($format == 2) {
5802 print '<option value="' . $arraytypes['code'] . '"';
5803 } elseif ($format == 3) {
5804 print '<option value="' . $id . '"';
5805 }
5806 // If text is selected, we compare with code, else with id
5807 if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
5808 print ' selected';
5809 } elseif ($selected == $id) {
5810 print ' selected';
5811 }
5812 print '>';
5813 $value = '';
5814 if ($format == 0) {
5815 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5816 } elseif ($format == 1) {
5817 $value = $arraytypes['code'];
5818 } elseif ($format == 2) {
5819 $value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
5820 } elseif ($format == 3) {
5821 $value = $arraytypes['code'];
5822 }
5823 print $value ? $value : '&nbsp;';
5824 print '</option>';
5825 }
5826 print '</select>';
5827
5828 print ajax_combobox("select".$htmlname);
5829
5830 if ($user->admin && !$noadmininfo) {
5831 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5832 }
5833 }
5834
5847 public function selectShippingMethod($selected = '', $htmlname = 'shipping_method_id', $filtre = '', $useempty = 0, $moreattrib = '', $noinfoadmin = 0, $morecss = '')
5848 {
5849 global $langs, $user;
5850
5851 $langs->loadLangs(array("admin", "sendings"));
5852
5853 $sql = "SELECT rowid, code, libelle as label";
5854 $sql .= " FROM " . $this->db->prefix() . "c_shipment_mode";
5855 $sql .= " WHERE active > 0";
5856 if ($filtre) {
5857 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
5858 }
5859 $sql .= " ORDER BY libelle ASC";
5860
5861 dol_syslog(get_class($this) . "::selectShippingMode", LOG_DEBUG);
5862
5863 $result = $this->db->query($sql);
5864 if ($result) {
5865 $num = $this->db->num_rows($result);
5866 $i = 0;
5867 if ($num) {
5868 print '<select id="select' . $htmlname . '" class="flat selectshippingmethod' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
5869 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
5870 print '<option value="-1">&nbsp;</option>';
5871 }
5872 while ($i < $num) {
5873 $obj = $this->db->fetch_object($result);
5874 if ($selected == $obj->rowid) {
5875 print '<option value="' . $obj->rowid . '" selected>';
5876 } else {
5877 print '<option value="' . $obj->rowid . '">';
5878 }
5879 print ($langs->trans("SendingMethod" . strtoupper($obj->code)) != "SendingMethod" . strtoupper($obj->code)) ? $langs->trans("SendingMethod" . strtoupper($obj->code)) : $obj->label;
5880 print '</option>';
5881 $i++;
5882 }
5883 print "</select>";
5884 if ($user->admin && empty($noinfoadmin)) {
5885 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
5886 }
5887
5888 print ajax_combobox('select' . $htmlname);
5889 } else {
5890 print $langs->trans("NoShippingMethodDefined");
5891 }
5892 } else {
5893 dol_print_error($this->db);
5894 }
5895 }
5896
5906 public function formSelectShippingMethod($page, $selected = '', $htmlname = 'shipping_method_id', $addempty = 0)
5907 {
5908 global $langs;
5909
5910 $langs->load("sendings");
5911
5912 if ($htmlname != "none") {
5913 print '<form method="POST" action="' . $page . '">';
5914 print '<input type="hidden" name="action" value="setshippingmethod">';
5915 print '<input type="hidden" name="token" value="' . newToken() . '">';
5916 $this->selectShippingMethod($selected, $htmlname, '', $addempty);
5917 print '<input type="submit" class="button valignmiddle" value="' . $langs->trans("Modify") . '">';
5918 print '</form>';
5919 } else {
5920 if ($selected) {
5921 $code = $langs->getLabelFromKey($this->db, $selected, 'c_shipment_mode', 'rowid', 'code');
5922 print $langs->trans("SendingMethod" . strtoupper($code));
5923 } else {
5924 print "&nbsp;";
5925 }
5926 }
5927 }
5928
5937 public function selectSituationInvoices($selected = '', $socid = 0)
5938 {
5939 global $langs;
5940
5941 $langs->load('bills');
5942
5943 $opt = '';
5944
5945 $sql = "SELECT rowid, ref, situation_cycle_ref, situation_counter, situation_final, fk_soc";
5946 $sql .= ' FROM ' . $this->db->prefix() . 'facture';
5947 $sql .= ' WHERE entity IN (' . getEntity('invoice') . ')';
5948 $sql .= ' AND situation_counter >= 1';
5949 $sql .= ' AND fk_soc = ' . (int) $socid;
5950 $sql .= ' AND type <> 2';
5951 $sql .= ' ORDER by situation_cycle_ref, situation_counter desc';
5952 $resql = $this->db->query($sql);
5953
5954 $nbSituationInvoiceForThirdparty = 0;
5955
5956 if ($resql && $this->db->num_rows($resql) > 0) {
5957 // Last seen cycle
5958 $ref = 0;
5959 while ($obj = $this->db->fetch_object($resql)) {
5960 //Same cycle ?
5961 if ($obj->situation_cycle_ref != $ref) {
5962 // Just seen this cycle
5963 $ref = $obj->situation_cycle_ref;
5964 //not final ?
5965 if ($obj->situation_final != 1) {
5966 //Not prov?
5967 if (substr($obj->ref, 1, 4) != 'PROV') {
5968 $nbSituationInvoiceForThirdparty++;
5969
5970 if ($selected == $obj->rowid) {
5971 $opt .= '<option value="' . $obj->rowid . '" selected>' . $obj->ref . '</option>';
5972 } else {
5973 $opt .= '<option value="' . $obj->rowid . '">' . $obj->ref . '</option>';
5974 }
5975 }
5976 }
5977 }
5978 }
5979 } else {
5980 dol_syslog("Error sql=" . $sql . ", error=" . $this->error, LOG_ERR);
5981 }
5982
5983 if ($nbSituationInvoiceForThirdparty > 0) {
5984 $opt = '<option class="minwidth100" value="" selected>&nbsp;</option>'.$opt;
5985 } else {
5986 $opt = '<option class="minwidth100" value="-1" selected>'.$langs->trans('NoSituations').'</option>';
5987 }
5988
5989 return $opt;
5990 }
5991
6001 public function selectUnits($selected = '', $htmlname = 'units', $showempty = 0, $unit_type = '')
6002 {
6003 global $langs;
6004
6005 $langs->load('products');
6006
6007 $return = '<select class="flat" id="' . $htmlname . '" name="' . $htmlname . '">';
6008
6009 $sql = "SELECT rowid, label, code FROM " . $this->db->prefix() . "c_units";
6010 $sql .= ' WHERE active > 0';
6011 if (!empty($unit_type)) {
6012 $sql .= " AND unit_type = '" . $this->db->escape($unit_type) . "'";
6013 }
6014 $sql .= " ORDER BY sortorder";
6015
6016 $resql = $this->db->query($sql);
6017 if ($resql && $this->db->num_rows($resql) > 0) {
6018 if ($showempty) {
6019 $return .= '<option value="-1"></option>';
6020 }
6021
6022 while ($res = $this->db->fetch_object($resql)) {
6023 $unitLabel = $res->label;
6024 if (!empty($langs->tab_translate['unit' . $res->code])) { // check if Translation is available before
6025 $unitLabel = $langs->trans('unit' . $res->code) != $res->label ? $langs->trans('unit' . $res->code) : $res->label;
6026 }
6027
6028 if ($selected == $res->rowid) {
6029 $return .= '<option value="' . $res->rowid . '" selected>' . $unitLabel . '</option>';
6030 } else {
6031 $return .= '<option value="' . $res->rowid . '">' . $unitLabel . '</option>';
6032 }
6033 }
6034 $return .= '</select>';
6035
6036 $return .= ajax_combobox($htmlname);
6037 }
6038 return $return;
6039 }
6040
6041 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6042
6058 public function select_comptes($selected = '', $htmlname = 'accountid', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '', $showcurrency = 0, $morecss = '', $nooutput = 0, $addentrynone = 0)
6059 {
6060 // phpcs:enable
6061 global $langs;
6062
6063 $out = '';
6064
6065 $langs->loadLangs(array("admin", "banks"));
6066 $num = 0;
6067
6068 $sql = "SELECT rowid, label, bank, clos as status, currency_code";
6069 $sql .= " FROM " . $this->db->prefix() . "bank_account";
6070 $sql .= " WHERE entity IN (" . getEntity('bank_account') . ")";
6071 if ($status != 2) {
6072 $sql .= " AND clos = " . (int) $status;
6073 }
6074 if ($filtre) {
6075 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6076 }
6077 $sql .= " ORDER BY label";
6078
6079 dol_syslog(get_class($this) . "::select_comptes", LOG_DEBUG);
6080 $result = $this->db->query($sql);
6081 if ($result) {
6082 $num = $this->db->num_rows($result);
6083 $i = 0;
6084
6085 $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6086
6087 if ($num == 0) {
6088 if ($status == 0) {
6089 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoActiveBankAccountDefined") . '</span>';
6090 } else {
6091 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoBankAccountDefined") . '</span>';
6092 }
6093 } else {
6094 if (!empty($useempty) && !is_numeric($useempty)) {
6095 $out .= '<option value="-1">'.$langs->trans($useempty).'</option>';
6096 } elseif ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6097 $out .= '<option value="-1">&nbsp;</option>';
6098 }
6099 }
6100
6101 while ($i < $num) {
6102 $obj = $this->db->fetch_object($result);
6103
6104 $labeltoshow = trim($obj->label);
6105 $labeltoshowhtml = trim($obj->label);
6106 if ($showcurrency) {
6107 $labeltoshow .= ' (' . $obj->currency_code . ')';
6108 $labeltoshowhtml .= ' <span class="opacitymedium">(' . $obj->currency_code . ')</span>';
6109 }
6110 if ($status == 2 && $obj->status == 1) {
6111 $labeltoshow .= ' (' . $langs->trans("Closed") . ')';
6112 $labeltoshowhtml .= ' <span class="opacitymedium">(' . $langs->trans("Closed") . ')</span>';
6113 }
6114
6115 if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
6116 $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml).'" selected>';
6117 } else {
6118 $out .= '<option value="' . $obj->rowid . '" data-currency-code="' . $obj->currency_code . '" data-html="'.dolPrintHTMLForAttribute($labeltoshowhtml).'">';
6119 }
6120 $out .= $labeltoshow;
6121 $out .= '</option>';
6122 $i++;
6123 }
6124
6125 if (!empty($addentrynone)) {
6126 $out .= '<option value="-2"'.($selected == -2 ? ' selected="selected"' : '').' data-html="'.dolPrintHTMLForAttribute('<span class="opacitymedium">'.$langs->trans("None").'</span>').'">'.$langs->trans("None").'</option>';
6127 }
6128
6129 $out .= "</select>";
6130 $out .= ajax_combobox('select' . $htmlname);
6131 } else {
6132 dol_print_error($this->db);
6133 }
6134
6135 // Output or return
6136 if (empty($nooutput)) {
6137 print $out;
6138 } else {
6139 return $out;
6140 }
6141
6142 return $num;
6143 }
6144
6158 public function selectRib($selected = '', $htmlname = 'ribcompanyid', $filtre = '', $useempty = 0, $moreattrib = '', $showibanbic = 0, $morecss = '', $nooutput = 0)
6159 {
6160 // phpcs:enable
6161 global $langs;
6162
6163 $out = '';
6164
6165 $langs->loadLangs(array("admin", "banks"));
6166 $num = 0;
6167
6168 $sql = "SELECT rowid, label, bank, status, iban_prefix, bic, default_rib";
6169 $sql .= " FROM " . $this->db->prefix() . "societe_rib";
6170 $sql .= " WHERE type = 'ban'";
6171 if ($filtre) {
6172 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6173 }
6174 $sql .= " ORDER BY label";
6175 dol_syslog(get_class($this) . "::select_comptes", LOG_DEBUG);
6176 $result = $this->db->query($sql);
6177 if ($result) {
6178 $num = $this->db->num_rows($result);
6179 $i = 0;
6180
6181 $out .= '<select id="select' . $htmlname . '" class="flat selectbankaccount' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6182
6183 if ($num == 0) {
6184 $out .= '<option class="opacitymedium" value="-1">' . $langs->trans("NoBankAccountDefined") . '</span>';
6185 } else {
6186 if (!empty($useempty) && !is_numeric($useempty)) {
6187 $out .= '<option value="-1">'.$langs->trans($useempty).'</option>';
6188 } elseif ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6189 $out .= '<option value="-1">&nbsp;</option>';
6190 }
6191 }
6192
6193 while ($i < $num) {
6194 $obj = $this->db->fetch_object($result);
6195 $iban = dolDecrypt($obj->iban_prefix);
6196 if ($selected == $obj->rowid || ($useempty == 2 && $num == 1 && empty($selected))) {
6197 $out .= '<option value="' . $obj->rowid . '" data-iban-prefix="' . $iban . ' data-bic="' . $obj->bic . '" selected>';
6198 } else {
6199 $out .= '<option value="' . $obj->rowid . '" data-iban-prefix="' . $iban . ' data-bic="' . $obj->bic . '">';
6200 }
6201 $out .= trim($obj->label);
6202 if ($showibanbic) {
6203 $out .= ' (' . $iban . '/' .$obj->bic. ')' . ($obj->default_rib ? ' ['.$langs->trans("ByDefault").']' : '');
6204 }
6205 $out .= '</option>';
6206 $i++;
6207 }
6208 $out .= "</select>";
6209 $out .= ajax_combobox('select' . $htmlname);
6210 } else {
6211 dol_print_error($this->db);
6212 }
6213
6214 // Output or return
6215 if (empty($nooutput)) {
6216 print $out;
6217 } else {
6218 return $out;
6219 }
6220
6221 return $num;
6222 }
6223
6235 public function selectEstablishments($selected = '', $htmlname = 'entity', $status = 0, $filtre = '', $useempty = 0, $moreattrib = '')
6236 {
6237 global $langs;
6238
6239 $langs->load("admin");
6240 $num = 0;
6241
6242 $sql = "SELECT rowid, name, fk_country, status, entity";
6243 $sql .= " FROM " . $this->db->prefix() . "establishment";
6244 $sql .= " WHERE 1=1";
6245 if ($status != 2) {
6246 $sql .= " AND status = " . (int) $status;
6247 }
6248 if ($filtre) {
6249 $sql .= forgeSQLFromUniversalSearchCriteria($filtre);
6250 }
6251 $sql .= " ORDER BY name";
6252
6253 dol_syslog(get_class($this) . "::select_establishment", LOG_DEBUG);
6254 $result = $this->db->query($sql);
6255 if ($result) {
6256 $num = $this->db->num_rows($result);
6257 $i = 0;
6258 if ($num) {
6259 print '<select id="select' . $htmlname . '" class="flat selectestablishment" name="' . $htmlname . '"' . ($moreattrib ? ' ' . $moreattrib : '') . '>';
6260 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6261 print '<option value="-1">&nbsp;</option>';
6262 }
6263
6264 while ($i < $num) {
6265 $obj = $this->db->fetch_object($result);
6266 if ($selected == $obj->rowid) {
6267 print '<option value="' . $obj->rowid . '" selected>';
6268 } else {
6269 print '<option value="' . $obj->rowid . '">';
6270 }
6271 print trim($obj->name);
6272 if ($status == 2 && $obj->status == 1) {
6273 print ' (' . $langs->trans("Closed") . ')';
6274 }
6275 print '</option>';
6276 $i++;
6277 }
6278 print "</select>";
6279 } else {
6280 if ($status == 0) {
6281 print '<span class="opacitymedium">' . $langs->trans("NoActiveEstablishmentDefined") . '</span>';
6282 } else {
6283 print '<span class="opacitymedium">' . $langs->trans("NoEstablishmentFound") . '</span>';
6284 }
6285 }
6286
6287 return $num;
6288 } else {
6289 dol_print_error($this->db);
6290 return -1;
6291 }
6292 }
6293
6303 public function formSelectAccount($page, $selected = '', $htmlname = 'fk_account', $addempty = 0)
6304 {
6305 global $langs;
6306 if ($htmlname != "none") {
6307 print '<form method="POST" action="' . $page . '">';
6308 print '<input type="hidden" name="action" value="setbankaccount">';
6309 print '<input type="hidden" name="token" value="' . newToken() . '">';
6310 print img_picto('', 'bank_account', 'class="pictofixedwidth"');
6311 $nbaccountfound = $this->select_comptes($selected, $htmlname, 0, '', $addempty);
6312 if ($nbaccountfound > 0) {
6313 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6314 }
6315 print '</form>';
6316 } else {
6317 $langs->load('banks');
6318
6319 if ($selected) {
6320 require_once DOL_DOCUMENT_ROOT . '/compta/bank/class/account.class.php';
6321 $bankstatic = new Account($this->db);
6322 $result = $bankstatic->fetch((int) $selected);
6323 if ($result) {
6324 print $bankstatic->getNomUrl(1);
6325 }
6326 } else {
6327 print "&nbsp;";
6328 }
6329 }
6330 }
6331
6343 public function formRib($page, $selected = '', $htmlname = 'ribcompanyid', $filtre = '', $addempty = 0, $showibanbic = 0)
6344 {
6345 global $langs;
6346 if ($htmlname != "none") {
6347 print '<form method="POST" action="' . $page . '">';
6348 print '<input type="hidden" name="action" value="setbankaccountcustomer">';
6349 print '<input type="hidden" name="token" value="' . newToken() . '">';
6350 $nbaccountfound = $this->selectRib($selected, $htmlname, $filtre, $addempty, '', $showibanbic);
6351 if ($nbaccountfound > 0) {
6352 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
6353 }
6354 print '</form>';
6355 } else {
6356 $langs->load('banks');
6357
6358 if ($selected) {
6359 require_once DOL_DOCUMENT_ROOT . '/societe/class/companybankaccount.class.php';
6360 $bankstatic = new CompanyBankAccount($this->db);
6361 $result = $bankstatic->fetch((int) $selected);
6362 if ($result) {
6363 print $bankstatic->label;
6364 if ($showibanbic) {
6365 print ' (' . $bankstatic->iban . '/' .$bankstatic->bic. ')';
6366 }
6367 }
6368 } else {
6369 print "&nbsp;";
6370 }
6371 }
6372 }
6373
6383 public function selectCategories($categtype, $htmlname, $object = null)
6384 {
6385 global $langs;
6386
6387 $out = '';
6388
6389 $cate_arbo = $this->select_all_categories($categtype, '', '', 64, 0, 3);
6390
6391 $arrayselected = array();
6392 if (GETPOSTISARRAY($htmlname)) {
6393 $arrayselected = GETPOST($htmlname, 'array:int');
6394 } elseif (is_object($object) && $object->id > 0) {
6395 $c = new Categorie($this->db);
6396 $cats = $c->containing($object->id, $categtype);
6397 $arrayselected = array();
6398 foreach ($cats as $cat) {
6399 $arrayselected[] = $cat->id;
6400 }
6401 }
6402
6403 $out .= img_picto('', 'category', 'class="pictofixedwidth"');
6404 $out .= $this->multiselectarray($htmlname, $cate_arbo, $arrayselected, 0, 0, 'minwidth100 widthcentpercentminusxx', 0, 0);
6405
6406 if (!getDolGlobalString('CATEGORY_EDIT_IN_MENU_NOT_IN_POPUP')) {
6407 // Add html code to add the edit button and go back
6408 $jsonclose = 'doJsCodeAfterPopupClose'.dol_sanitizeKeyCode($htmlname).'()';
6409 $urltoopen = '/categories/categorie_list.php?type='.urlencode($categtype).'&nosearch=1';
6410
6411 $s = dolButtonToOpenUrlInDialogPopup($htmlname, $langs->transnoentitiesnoconv("Categories"), img_picto('', 'add', 'class="editfielda"'), $urltoopen, '', '', '', $jsonclose);
6412 $out .= $s;
6413 // Add js code to add the edit button and go back
6414 $out .= '<!-- Add js code to open the popup for category/edit/add -->'."\n";
6415 $out .= '<script>function doJsCodeAfterPopupClose'.dol_sanitizeKeyCode($htmlname).'() {
6416 console.log("doJsCodeAfterPopupClose'.dol_sanitizeKeyCode($htmlname).' has been called, we refresh the combo content + refresh select2...");
6417
6418 // Call an ajax to reload values and update the select
6419
6420 $.ajax({
6421 url: \''.DOL_URL_ROOT.'/core/ajax/fetchCategories.php\',
6422 data: {
6423 action: \'getCategories\',
6424 type: \''.dol_escape_htmltag($categtype).'\'
6425 },
6426 type: \'GET\',
6427 dataType: \'json\',
6428 success: function (data) {
6429 var $select = $(\'#'.dol_sanitizeKeyCode($htmlname).'\');
6430 var selectedValues = $select.val(); // This is an array of selected values
6431 console.log(selectedValues);
6432 $select.empty();
6433 $.each(data, function (index, item) {
6434 $select.append(\'<option value="\' + item.id + \'" data-html="\' + item.htmlforattribute + \'">\' + item.htmlforoption + \'</option>\');
6435 });
6436 $select.val(selectedValues);
6437 },
6438 error: function (xhr, status, error) {
6439 console.log("Error when loading ajax page : " + error);
6440 }
6441 });
6442
6443 // Refresh select2 to take account of new values (enough for small change)
6444 $("#'.dol_sanitizeKeyCode($htmlname).'").trigger("change");
6445
6446 // Alternative if change in select is complex
6447 /*
6448 $("#'.dol_sanitizeKeyCode($htmlname).'").select2("destroy");
6449 $("#'.dol_sanitizeKeyCode($htmlname).'").select2();
6450 */
6451 }</script>';
6452 }
6453
6454 return $out;
6455 }
6456
6457
6458 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6478 public function select_all_categories($type, $selected = '', $htmlname = "parent", $maxlength = 64, $fromid = 0, $outputmode = 0, $include = 0, $morecss = '', $useempty = 1)
6479 {
6480 // phpcs:enable
6481 global $langs;
6482
6483 include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
6484
6485 $cat = new Categorie($this->db);
6486
6487 if (is_numeric($type)) {
6488 $type = array_search($type, $cat->MAP_ID); // For backward compatibility
6489 }
6490
6491 $cate_arbo = $cat->get_full_arbo($type, $fromid, $include);
6492
6493 $outarray = array();
6494 $outarrayrichhtml = array();
6495
6496
6497 $output = '<select class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
6498 $num = 0;
6499 if (is_array($cate_arbo)) {
6500 $num = count($cate_arbo);
6501
6502 if (!$num) {
6503 $langs->load("categories");
6504 $output .= '<option value="-1" disabled>' . $langs->trans("NoCategoriesDefined") . '</option>';
6505 } else {
6506 if ($useempty == 1 || ($useempty == 2 && $num > 1)) {
6507 $output .= '<option value="-1">&nbsp;</option>';
6508 }
6509 foreach ($cate_arbo as $key => $value) {
6510 if ($cate_arbo[$key]['id'] == $selected || ($selected === 'auto' && count($cate_arbo) == 1)) {
6511 $add = 'selected ';
6512 } else {
6513 $add = '';
6514 }
6515
6516 $labeltoshow = img_picto('', 'category', 'class="pictofixedwidth"'.(empty($cate_arbo[$key]['color']) ? '' : ' style="color: #' . $cate_arbo[$key]['color'] . '"'));
6517 $labeltoshow .= dol_trunc($cate_arbo[$key]['fulllabel'], $maxlength, 'middle');
6518
6519 $outarray[$cate_arbo[$key]['id']] = $cate_arbo[$key]['fulllabel'];
6520
6521 $outarrayrichhtml[$cate_arbo[$key]['id']] = $labeltoshow;
6522
6523 $output .= '<option ' . $add . 'value="' . $cate_arbo[$key]['id'] . '"';
6524 $output .= ' data-html="' . dol_escape_htmltag($labeltoshow) . '"';
6525 $output .= '>';
6526 // The visible (truncated) label is rendered via data-html in
6527 // templateResult of the select2 combobox; the bare option text
6528 // must keep the full label so that the select2 search matcher
6529 // (ajax_combobox in core/lib/ajax.lib.php) can find a hit on
6530 // characters that lie outside the truncated portion.
6531 $output .= dol_escape_htmltag($cate_arbo[$key]['fulllabel']);
6532 $output .= '</option>';
6533
6534 $cate_arbo[$key]['data-html'] = $labeltoshow;
6535 }
6536 }
6537 }
6538 $output .= '</select>';
6539 $output .= "\n";
6540
6541 $this->num = $num;
6542
6543 if ($outputmode == 2) {
6544 // TODO: handle error when $cate_arbo is not an array
6545 return $cate_arbo;
6546 } elseif ($outputmode == 1) {
6547 return $outarray;
6548 } elseif ($outputmode == 3) {
6549 return $outarrayrichhtml;
6550 }
6551 return $output;
6552 }
6553
6562 public function getHelpBlock($content, $icon = 'fa-question-circle')
6563 {
6564 global $langs;
6565
6566 // Sanitize content (assuming it might contain HTML, but escaping text nodes if needed)
6567 // We trust the caller to pass safe HTML or translated strings.
6568
6569 $html = '<details class="dolibarr-help-block" style="margin-top:8px;">';
6570 $html .= '<summary style="cursor:pointer; color:#0056b3; font-weight:normal; list-style:none; font-size:0.9em; display:flex; align-items:center;">';
6571 $html .= '<span class="fa ' . $icon . '" style="margin-right:6px;"></span>';
6572 $html .= $langs->trans("Help"); // Standardized title
6573 $html .= '</summary>';
6574 $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;">';
6575 $html .= $content;
6576 $html .= '</div>';
6577 $html .= '</details>';
6578
6579 return $html;
6580 }
6581
6582 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6583
6602 public function form_confirm($page, $title, $question, $action, $formquestion = array(), $selectedchoice = "", $useajax = 0, $height = 170, $width = 500)
6603 {
6604 // phpcs:enable
6605 dol_syslog(__METHOD__ . ': using form_confirm is deprecated. Use formconfim instead.', LOG_WARNING);
6606 print $this->formconfirm($page, $title, $question, $action, $formquestion, $selectedchoice, $useajax, $height, $width);
6607 }
6608
6638 public function formconfirm($page, $title, $question, $action, $formquestion = '', $selectedchoice = '', $useajax = 0, $height = 0, $width = 600, $disableformtag = 0, $labelbuttonyes = 'Yes', $labelbuttonno = 'No', $helpContent = '')
6639 {
6640 global $langs, $conf;
6641
6642 $more = '';
6643 $formconfirm = '<!-- formconfirm - before call, page=' . dol_escape_htmltag($page) . ' -->';
6644
6645 $inputok = array();
6646 $inputko = array();
6647
6648 // Clean parameters
6649 $newselectedchoice = empty($selectedchoice) ? "no" : $selectedchoice;
6650 if ($conf->browser->layout == 'phone') {
6651 $width = '95%';
6652 }
6653
6654 // Set height automatically if not defined
6655 if (empty($height)) {
6656 $height = 185;
6657 if (is_array($formquestion)) {
6658 $height += (count($formquestion) * 40);
6659 }
6660 if ($question) {
6661 $height += dol_nboflines_bis($question, 80) * 40;
6662 }
6663 }
6664
6665 if (is_array($formquestion) && !empty($formquestion)) {
6666 // First add hidden fields and value
6667 foreach ($formquestion as $key => $input) {
6668 if (is_array($input) && !empty($input)) {
6669 if ($input['type'] == 'hidden') {
6670 $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
6671 $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
6672
6673 $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
6674 }
6675 }
6676 }
6677
6678 // Now add questions
6679 $moreonecolumn = '';
6680 $more .= '<div class="tagtable paddingtopbottomonly centpercent noborderspacing">' . "\n";
6681 foreach ($formquestion as $key => $input) {
6682 if (is_array($input) && !empty($input)) {
6683 $size = (!empty($input['size']) ? ' size="' . $input['size'] . '"' : ''); // deprecated. Use morecss instead.
6684 $moreattr = (!empty($input['moreattr']) ? ' ' . $input['moreattr'] : '');
6685 $morecss = (!empty($input['morecss']) ? ' ' . $input['morecss'] : '');
6686
6687 if ($input['type'] == 'text' || $input['type'] == 'input') { // traditional input
6688 $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";
6689 } elseif ($input['type'] == 'password') {
6690 $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";
6691 } elseif ($input['type'] == 'textarea') {
6692 $moreonecolumn .= '<div class="margintoponly">';
6693 $moreonecolumn .= $input['label'] . '<br>';
6694 $moreonecolumn .= '<textarea name="' . dol_escape_htmltag($input['name']) . '" id="' . dol_escape_htmltag($input['name']) . '" class="' . $morecss . '"' . $moreattr . '>';
6695 $moreonecolumn .= $input['value'];
6696 $moreonecolumn .= '</textarea>';
6697 $moreonecolumn .= '</div>';
6698 } elseif (in_array($input['type'], ['select', 'multiselect'])) {
6699 if (empty($morecss)) {
6700 $morecss = 'minwidth100';
6701 }
6702
6703 $show_empty = isset($input['select_show_empty']) ? $input['select_show_empty'] : 1;
6704 $key_in_label = isset($input['select_key_in_label']) ? $input['select_key_in_label'] : 0;
6705 $value_as_key = isset($input['select_value_as_key']) ? $input['select_value_as_key'] : 0;
6706 $translate = isset($input['select_translate']) ? $input['select_translate'] : 0;
6707 $maxlen = isset($input['select_maxlen']) ? $input['select_maxlen'] : 0;
6708 $disabled = isset($input['select_disabled']) ? $input['select_disabled'] : 0;
6709 $sort = isset($input['select_sort']) ? $input['select_sort'] : '';
6710
6711 $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">';
6712 if (!empty($input['label'])) {
6713 $more .= $input['label'] . '</div><div class="tagtd left">';
6714 }
6715 if ($input['type'] == 'select') {
6716 $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);
6717 } else {
6718 $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);
6719 }
6720 $more .= '</div></div>' . "\n";
6721 } elseif ($input['type'] == 'checkbox') {
6722 $more .= '<div class="tagtr">';
6723 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '"><label for="' . dol_escape_htmltag($input['name']) . '">' . $input['label'] . '</label></div><div class="tagtd">';
6724 $more .= '<input type="checkbox" class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . dol_escape_htmltag($input['name']) . '" name="' . dol_escape_htmltag($input['name']) . '"' . $moreattr;
6725 if (isset($input['value']) && !is_bool($input['value']) && $input['value'] != 'false' && $input['value'] != '0' && $input['value'] != '') {
6726 $more .= ' checked';
6727 }
6728 if (isset($input['value']) && is_bool($input['value']) && $input['value']) {
6729 $more .= ' checked';
6730 }
6731 if (isset($input['disabled'])) {
6732 $more .= ' disabled';
6733 }
6734 $more .= ' /></div>';
6735 $more .= '</div>' . "\n";
6736 } elseif ($input['type'] == 'radio') {
6737 $i = 0;
6738 foreach ($input['values'] as $selkey => $selval) {
6739 $more .= '<div class="tagtr">';
6740 if (isset($input['label'])) {
6741 if ($i == 0) {
6742 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? ' tdtop' : (' tdtop ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
6743 } else {
6744 $more .= '<div class="tagtd' . (empty($input['tdclass']) ? '' : (' "' . $input['tdclass'])) . '">&nbsp;</div>';
6745 }
6746 }
6747 $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;
6748 if (!empty($input['disabled'])) {
6749 $more .= ' disabled';
6750 }
6751 if (isset($input['default']) && $input['default'] === $selkey) {
6752 $more .= ' checked="checked"';
6753 }
6754 $more .= ' /> ';
6755 $more .= '<label for="' . dol_escape_htmltag($input['name'] . $selkey) . '" class="valignmiddle">' . $selval . '</label>';
6756 $more .= '</div></div>' . "\n";
6757 $i++;
6758 }
6759 } elseif ($input['type'] == 'date' || $input['type'] == 'datetime') {
6760 $more .= '<div class="tagtr"><div class="tagtd' . (empty($input['tdclass']) ? '' : (' ' . $input['tdclass'])) . '">' . $input['label'] . '</div>';
6761 $more .= '<div class="tagtd">';
6762 $addnowlink = (empty($input['datenow']) ? 0 : 1);
6763 $h = $m = 0;
6764 if ($input['type'] == 'datetime') {
6765 $h = isset($input['hours']) ? $input['hours'] : 1;
6766 $m = isset($input['minutes']) ? $input['minutes'] : 1;
6767 }
6768 $more .= $this->selectDate(isset($input['value']) ? $input['value'] : -1, $input['name'], $h, $m, 0, '', 1, $addnowlink);
6769 $more .= '</div></div>'."\n";
6770 $formquestion[] = array('name' => $input['name'].'day');
6771 $formquestion[] = array('name' => $input['name'].'month');
6772 $formquestion[] = array('name' => $input['name'].'year');
6773 $formquestion[] = array('name' => $input['name'].'hour');
6774 $formquestion[] = array('name' => $input['name'].'min');
6775 } elseif ($input['type'] == 'other') { // can be 1 column or 2 depending if label is set or not
6776 $more .= '<div class="tagtr"><div class="tagtd'.(empty($input['tdclass']) ? '' : (' '.$input['tdclass'])).'">';
6777 if (!empty($input['label'])) {
6778 $more .= $input['label'] . '</div><div class="tagtd">';
6779 }
6780 if (!empty($input['value'])) {
6781 $more .= $input['value'];
6782 }
6783 $more .= '</div></div>' . "\n";
6784 } elseif ($input['type'] == 'onecolumn') {
6785 $moreonecolumn .= '<div class="margintoponly">';
6786 $moreonecolumn .= $input['value'];
6787 $moreonecolumn .= '</div>' . "\n";
6788 } elseif ($input['type'] == 'hidden') {
6789 // Do nothing more, already added by a previous loop
6790 } elseif ($input['type'] == 'separator') {
6791 $more .= '<br>';
6792 } else {
6793 $more .= 'Error type ' . $input['type'] . ' for the confirm box is not a supported type';
6794 }
6795 }
6796 }
6797 $more .= '</div>' . "\n";
6798 $more .= $moreonecolumn;
6799 }
6800
6801 // JQUERY method dialog is broken with smartphone, we use standard HTML.
6802 // 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
6803 // See page product/card.php for example
6804 if (!empty($conf->dol_use_jmobile)) {
6805 $useajax = 0;
6806 }
6807 if (empty($conf->use_javascript_ajax)) {
6808 $useajax = 0;
6809 }
6810
6811 if ($useajax) {
6812 $autoOpen = true;
6813 $dialogconfirm = 'dialog-confirm';
6814 $button = '';
6815 if (!is_numeric($useajax)) {
6816 $button = $useajax;
6817 $useajax = 1;
6818 $autoOpen = false;
6819 $dialogconfirm .= '-' . $button;
6820 }
6821 $pageyes = $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=yes';
6822 $pageno = ($useajax == 2 ? $page . (preg_match('/\?/', $page) ? '&' : '?') . 'action=' . urlencode($action) . '&confirm=no' : '');
6823
6824 // Add input fields into list of fields to read during submit (inputok and inputko)
6825 if (is_array($formquestion)) {
6826 foreach ($formquestion as $key => $input) {
6827 //print "xx ".$key." rr ".is_array($input)."<br>\n";
6828 // Add name of fields to propagate with the GET when submitting the form with button OK.
6829 if (is_array($input) && isset($input['name'])) {
6830 if (strpos($input['name'], ',') > 0) {
6831 $inputok = array_merge($inputok, explode(',', $input['name']));
6832 } else {
6833 array_push($inputok, $input['name']);
6834 }
6835 }
6836 // Add name of fields to propagate with the GET when submitting the form with button KO.
6837 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
6838 if (is_array($input) && isset($input['inputko']) && $input['inputko'] == 1 && isset($input['name'])) {
6839 array_push($inputko, $input['name']);
6840 }
6841 }
6842 }
6843
6844 // Show JQuery confirm box.
6845 // Add 'flex-direction: column' and 'justify-content: space-between' to push content to top and buttons to bottom
6846 $formconfirm .= '<div id="' . $dialogconfirm . '" title="' . dol_escape_htmltag($title) . '" style="display: none;">';
6847 $formconfirm .= '<div style="display: flex; flex-direction: column; height: 100%;">';
6848 if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
6849 $formconfirm .= '<div class="confirmtext">' . $formquestion['text'] . '</div>' . "\n";
6850 }
6851 if (!empty($more)) {
6852 $formconfirm .= '<div class="confirmquestions">' . $more . '</div>' . "\n";
6853 }
6854 // NEW: Add help block if content provided
6855 if (!empty($helpContent)) {
6856 $formconfirm .= '<div style="text-align:left; margin-top:12px; padding-top:8px; border-top:1px solid #eee; clear:both;">';
6857 $formconfirm .= $this->getHelpBlock($helpContent);
6858 $formconfirm .= '</div>';
6859 }
6860 if (!empty($question)) {
6861 $formconfirm .= '<div class="confirmmessage" style="padding-top: 15px;">';
6862 $formconfirm .= img_help(0, '') . ' ' . $question;
6863 $formconfirm .= '</div>';
6864 }
6865 $formconfirm .= '</div>';
6866 $formconfirm .= '</div>' . "\n";
6867
6868 $formconfirm .= "\n<!-- begin code of popup for formconfirm page=" . $page . " -->\n";
6869 $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
6870 $formconfirm .= "/* Code for the jQuery('#dialogforpopup').dialog() */\n";
6871 $formconfirm .= 'jQuery(document).ready(function() {
6872 $(function() {
6873 $( "#' . $dialogconfirm . '" ).dialog({
6874 autoOpen: ' . ($autoOpen ? "true" : "false") . ',';
6875 if ($newselectedchoice == 'no') {
6876 $formconfirm .= '
6877 open: function() {
6878 $(this).parent().find("button.ui-button:eq(2)").focus();
6879 },';
6880 }
6881
6882 $jsforcursor = '';
6883 if ($useajax == 1) {
6884 $jsforcursor = '// The call to urljump can be slow, so we set the wait cursor' . "\n";
6885 $jsforcursor .= 'jQuery("html,body,#id-container").addClass("cursorwait");' . "\n";
6886 }
6887
6888 $postconfirmas = 'GET';
6889
6890 $formconfirm .= '
6891 resizable: false,
6892 height: \'' . dol_escape_js($height) . '\',
6893 width: \'' . dol_escape_js($width) . '\',
6894 modal: true,
6895 closeOnEscape: false,
6896 buttons: {
6897 "' . dol_escape_js($langs->transnoentities($labelbuttonyes)) . '": function() {
6898 var options = "token=' . urlencode(newToken()) . '";
6899 var inputok = ' . json_encode($inputok) . '; /* List of fields into form */
6900 var page = \'' . dol_escape_js(!empty($page) ? $page : '') . '\';
6901 var pageyes = \'' . dol_escape_js(!empty($pageyes) ? $pageyes : '') . '\';
6902
6903 if (inputok.length > 0) {
6904 $.each(inputok, function(i, inputname) {
6905 var more = "";
6906 var inputvalue;
6907 if ($("input[name=\'" + inputname + "\']").attr("type") == "radio") {
6908 inputvalue = $("input[name=\'" + inputname + "\']:checked").val();
6909 } else {
6910 if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
6911 inputvalue = $("#" + inputname + more).val();
6912 }
6913 if (typeof inputvalue == "undefined") { inputvalue=""; }
6914 console.log("formconfirm check inputname="+inputname+" inputvalue="+inputvalue);
6915 options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
6916 });
6917 }
6918 var urljump = pageyes + (pageyes.indexOf("?") < 0 ? "?" : "&") + options;
6919 if (pageyes.length > 0) {';
6920 if ($postconfirmas == 'GET') {
6921 $formconfirm .= 'location.href = urljump;';
6922 } else {
6923 $formconfirm .= $jsforcursor;
6924 $formconfirm .= 'var post = $.post(
6925 pageyes,
6926 options,
6927 function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
6928 );';
6929 }
6930 $formconfirm .= '
6931 console.log("after post ok");
6932 }
6933 $(this).dialog("close");
6934 },
6935 "' . dol_escape_js($langs->transnoentities($labelbuttonno)) . '": function() {
6936 var options = "token=' . urlencode(newToken()) . '";
6937 var inputko = ' . json_encode($inputko) . '; /* List of fields into form */
6938 var page = "' . dol_escape_js(!empty($page) ? $page : '') . '";
6939 var pageno="' . dol_escape_js(!empty($pageno) ? $pageno : '') . '";
6940 if (inputko.length > 0) {
6941 $.each(inputko, function(i, inputname) {
6942 var more = "";
6943 if ($("#" + inputname).attr("type") == "checkbox") { more = ":checked"; }
6944 var inputvalue = $("#" + inputname + more).val();
6945 if (typeof inputvalue == "undefined") { inputvalue=""; }
6946 options += "&" + inputname + "=" + encodeURIComponent(inputvalue);
6947 });
6948 }
6949 var urljump=pageno + (pageno.indexOf("?") < 0 ? "?" : "&") + options;
6950 //alert(urljump);
6951 if (pageno.length > 0) {';
6952 if ($postconfirmas == 'GET') {
6953 $formconfirm .= 'location.href = urljump;';
6954 } else {
6955 $formconfirm .= $jsforcursor;
6956 $formconfirm .= 'var post = $.post(
6957 pageno,
6958 options,
6959 function(data) { $("body").html(data); jQuery("html,body,#id-container").removeClass("cursorwait"); }
6960 );';
6961 }
6962 $formconfirm .= '
6963 console.log("after post ko");
6964 }
6965 $(this).dialog("close");
6966 }
6967 }
6968 }
6969 );
6970
6971 var button = "' . $button . '";
6972 if (button.length > 0) {
6973 $( "#" + button ).click(function() {
6974 $("#' . $dialogconfirm . '").dialog("open");
6975 });
6976 }
6977 });
6978 });
6979 </script>';
6980 $formconfirm .= "<!-- end ajax formconfirm -->\n";
6981 } else {
6982 $formconfirm .= "\n<!-- begin formconfirm page=" . dol_escape_htmltag($page) . " -->\n";
6983
6984 if (empty($disableformtag)) {
6985 $formconfirm .= '<form method="POST" action="' . $page . '" class="notoptoleftnoright">' . "\n";
6986 }
6987
6988 $formconfirm .= '<input type="hidden" name="action" value="' . $action . '">' . "\n";
6989 $formconfirm .= '<input type="hidden" name="token" value="' . newToken() . '">' . "\n";
6990
6991 $formconfirm .= '<div class="valid">' . "\n";
6992
6993 // Line title
6994 $formconfirm .= '<div class="validtitre">';
6995 $formconfirm .= img_picto('', 'pictoconfirm') . ' ' . $title;
6996 $formconfirm .= '</div>' . "\n";
6997
6998 // Line text
6999 if (is_array($formquestion) && array_key_exists('text', $formquestion) && !empty($formquestion['text'])) {
7000 $formconfirm .= '<div class="valid">' . $formquestion['text'] . '</div>' . "\n";
7001 }
7002
7003 // Line form fields
7004 if ($more) {
7005 $formconfirm .= '<div>' . "\n";
7006 $formconfirm .= $more;
7007 $formconfirm .= '</div>' . "\n";
7008 }
7009
7010 // NEW: Help block row (between form fields and question)
7011 if (!empty($helpContent)) {
7012 $formconfirm .= '<div style="padding-top:8px; border-top:1px solid #888;">';
7013 $formconfirm .= $this->getHelpBlock($helpContent);
7014 $formconfirm .= '</div>' . "\n";
7015 }
7016
7017 // Let's add a row that acts as a spacer.
7018 $formconfirm .= '<div style="padding-top: 20px;"></div>' . "\n";
7019
7020 // Question row
7021 $formconfirm .= '<div class="inline-block">' . $question . '</div>';
7022
7023 $formconfirm .= '<div class="inline-block">';
7024 $formconfirm .= $this->selectyesno("confirm", $newselectedchoice, 0, false, 0, 0, 'marginleftonly marginrightonly', $labelbuttonyes, $labelbuttonno);
7025 $formconfirm .= '<input class="button valignmiddle confirmvalidatebutton small" type="submit" value="' . $langs->trans("Validate") . '">';
7026 $formconfirm .= '</div>';
7027
7028 $formconfirm .= '</div>';
7029
7030 if (empty($disableformtag)) {
7031 $formconfirm .= "</form>\n";
7032 }
7033 $formconfirm .= '<br>';
7034
7035 if (!empty($conf->use_javascript_ajax)) {
7036 $formconfirm .= '<!-- code to disable button to avoid double clic -->';
7037 $formconfirm .= '<script nonce="' . getNonce() . '" type="text/javascript">' . "\n";
7038 $formconfirm .= '
7039 $(document).ready(function () {
7040 $(".confirmvalidatebutton").on("click", function() {
7041 console.log("We click on button confirmvalidatebutton");
7042 $(this).attr("disabled", "disabled");
7043 setTimeout(\'$(".confirmvalidatebutton").removeAttr("disabled")\', 3000);
7044 //console.log($(this).closest("form"));
7045 $(this).closest("form").submit();
7046 });
7047 });
7048 ';
7049 $formconfirm .= '</script>' . "\n";
7050 }
7051
7052 $formconfirm .= "<!-- end formconfirm -->\n";
7053 }
7054
7055 return $formconfirm;
7056 }
7057
7058 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7059
7076 public function form_project($page, $socid, $selected = '', $htmlname = 'projectid', $discard_closed = 0, $maxlength = 20, $forcefocus = 0, $nooutput = 0, $textifnoproject = '', $morecss = '', $option = '')
7077 {
7078 // phpcs:enable
7079 global $langs;
7080
7081 require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
7082 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
7083
7084 $out = '';
7085
7086 $formproject = new FormProjets($this->db);
7087
7088 $langs->load("project");
7089 if ($htmlname != "none") {
7090 $out .= '<form method="post" action="' . $page . '">';
7091 $out .= '<input type="hidden" name="action" value="classin">';
7092 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7093 $out .= $formproject->select_projects($socid, $selected, $htmlname, $maxlength, 0, 1, $discard_closed, $forcefocus, 0, 0, '', 1, 0, $morecss);
7094 $out .= '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7095 $out .= '</form>';
7096 } else {
7097 $out .= '<span class="project_head_block">';
7098 if ($selected instanceof Project) {
7099 $out .= $selected->getNomUrl(0, $option, 1);
7100 } elseif (is_numeric($selected)) {
7101 $projet = new Project($this->db);
7102 $projet->fetch((int) $selected);
7103 $out .= $projet->getNomUrl(0, $option, 1);
7104 } else {
7105 $out .= '<span class="opacitymedium">' . $textifnoproject . '</span>';
7106 }
7107 $out .= '</span>';
7108 }
7109
7110 if (empty($nooutput)) {
7111 print $out;
7112 return '';
7113 }
7114 return $out;
7115 }
7116
7117 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7118
7134 public function form_conditions_reglement($page, $selected = '', $htmlname = 'cond_reglement_id', $addempty = 0, $type = '', $filtertype = -1, $deposit_percent = -1, $nooutput = 0)
7135 {
7136 // phpcs:enable
7137 global $langs;
7138
7139 $selected = (int) $selected;
7140
7141 $out = '';
7142
7143 if ($htmlname != "none") {
7144 $out .= '<form method="POST" action="' . $page . '">';
7145 $out .= '<input type="hidden" name="action" value="setconditions">';
7146 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7147 if ($type) {
7148 $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7149 }
7150 $out .= $this->getSelectConditionsPaiements($selected, $htmlname, $filtertype, $addempty, 0, '', $deposit_percent);
7151 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7152 $out .= '</form>';
7153 } else {
7154 if ($selected) {
7155 $this->load_cache_conditions_paiements();
7156
7157 if (isset($this->cache_conditions_paiements[$selected])) {
7158 $label = $this->cache_conditions_paiements[$selected]['label'];
7159
7160 if (!empty($this->cache_conditions_paiements[$selected]['deposit_percent'])) {
7161 $label = str_replace('__DEPOSIT_PERCENT__', $deposit_percent > 0 ? $deposit_percent : $this->cache_conditions_paiements[$selected]['deposit_percent'], $label);
7162 }
7163
7164 $out .= $label;
7165 } else {
7166 $langs->load('errors');
7167 $out .= $langs->trans('ErrorNotInDictionaryPaymentConditions', $selected);
7168 }
7169 } else {
7170 $out .= '&nbsp;';
7171 }
7172 }
7173
7174 if (empty($nooutput)) {
7175 print $out;
7176 return '';
7177 }
7178 return $out;
7179 }
7180
7181 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7182
7193 public function form_rule_for_lines_dates($page, $selected = '', $htmlname = 'rule_for_lines_dates', $addempty = 0, $nooutput = 0): string
7194 {
7195 // phpcs:enable
7196 global $langs;
7197
7198 $out = '';
7199
7200 if ($htmlname != 'none') {
7201 $out .= '<form method="POST" action="' . $page . '">';
7202 $out .= '<input type="hidden" name="action" value="setruleforlinesdates">';
7203 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7204 $out .= $this->getSelectRuleForLinesDates($selected, $htmlname, $addempty);
7205 $out .= '<input type="submit" class="button valignmiddle smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7206 $out .= '</form>';
7207 } else {
7208 if (isset($selected)) {
7209 $this->load_cache_rule_for_lines_dates();
7210 if (isset($this->cache_rule_for_lines_dates[$selected])) {
7211 $label = $this->cache_rule_for_lines_dates[$selected];
7212 $out .= $langs->trans($label);
7213 }
7214 } else {
7215 $out .= '&nbsp;';
7216 }
7217 }
7218
7219 if (empty($nooutput)) {
7220 print $out;
7221 return '';
7222 }
7223
7224 return $out;
7225 }
7226
7227 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7228
7238 public function form_availability($page, $selected = '', $htmlname = 'availability', $addempty = 0)
7239 {
7240 dol_syslog(__METHOD__, LOG_DEBUG);
7241 // phpcs:enable
7242 global $langs;
7243 if ($htmlname != "none") {
7244 print '<form method="post" action="' . $page . '">';
7245 print '<input type="hidden" name="action" value="setavailability">';
7246 print '<input type="hidden" name="token" value="' . newToken() . '">';
7247 print $this->selectAvailabilityDelay($selected, $htmlname, '', $addempty, '', 1);
7248 print '<input type="submit" name="modify" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7249 print '<input type="submit" name="cancel" class="button smallpaddingimp" value="' . $langs->trans("Cancel") . '">';
7250 print '</form>';
7251 } else {
7252 if ($selected) {
7253 $this->load_cache_availability();
7254 // @phan-suppress-next-line PhanTypeMismatchProperty
7255 if (isset($this->cache_availability[$selected])) {
7256 print $this->cache_availability[$selected]['label'];
7257 } else {
7258 print "&nbsp;";
7259 }
7260 } else {
7261 print "&nbsp;";
7262 }
7263 }
7264 }
7265
7277 public function formInputReason($page, $selected = '', $htmlname = 'demandreason', $addempty = 0, $morecss = '')
7278 {
7279 global $langs;
7280 if ($htmlname != "none") {
7281 print '<form method="post" action="' . $page . '">';
7282 print '<input type="hidden" name="action" value="setdemandreason">';
7283 print '<input type="hidden" name="token" value="' . newToken() . '">';
7284 $this->selectInputReason($selected, $htmlname, '-1', $addempty, $morecss);
7285 print '<input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '">';
7286 print '</form>';
7287 } else {
7288 if ($selected) {
7289 $this->loadCacheInputReason();
7290 foreach ($this->cache_demand_reason as $key => $val) {
7291 if ($val['id'] == $selected) {
7292 print $val['label'];
7293 break;
7294 }
7295 }
7296 } else {
7297 print "&nbsp;";
7298 }
7299 }
7300 }
7301
7302 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7303
7317 public function form_date($page, $selected, $htmlname, $displayhour = 0, $displaymin = 0, $nooutput = 0, $type = '')
7318 {
7319 // phpcs:enable
7320 global $langs;
7321
7322 $ret = '';
7323
7324 if ($htmlname != "none") {
7325 $ret .= '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
7326 $ret .= '<input type="hidden" name="action" value="set' . $htmlname . '">';
7327 $ret .= '<input type="hidden" name="token" value="' . newToken() . '">';
7328 if ($type) {
7329 $ret .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7330 }
7331 $ret .= '<table class="nobordernopadding">';
7332 $ret .= '<tr><td>';
7333 $ret .= $this->selectDate($selected, $htmlname, $displayhour, $displaymin, 1, 'form' . $htmlname, 1, 0);
7334 $ret .= '</td>';
7335 $ret .= '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
7336 $ret .= '</tr></table></form>';
7337 } else {
7338 if ($displayhour) {
7339 $ret .= dol_print_date($selected, 'dayhour');
7340 } else {
7341 $ret .= dol_print_date($selected, 'day');
7342 }
7343 }
7344
7345 if (empty($nooutput)) {
7346 print $ret;
7347 }
7348 return $ret;
7349 }
7350
7351
7352 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7353
7364 public function form_users($page, $selected = '', $htmlname = 'userid', $exclude = array(), $include = array())
7365 {
7366 // phpcs:enable
7367 global $langs;
7368
7369 if ($htmlname != "none") {
7370 print '<form method="POST" action="' . $page . '" name="form' . $htmlname . '">';
7371 print '<input type="hidden" name="action" value="set' . $htmlname . '">';
7372 print '<input type="hidden" name="token" value="' . newToken() . '">';
7373 print $this->select_dolusers($selected, $htmlname, 1, $exclude, 0, $include);
7374 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7375 print '</form>';
7376 } else {
7377 if ($selected) {
7378 require_once DOL_DOCUMENT_ROOT . '/user/class/user.class.php';
7379 $theuser = new User($this->db);
7380 $theuser->fetch((int) $selected);
7381 print $theuser->getNomUrl(1);
7382 } else {
7383 print "&nbsp;";
7384 }
7385 }
7386 }
7387
7388
7389 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7390
7404 public function form_modes_reglement($page, $selected = '', $htmlname = 'mode_reglement_id', $filtertype = '', $active = 1, $addempty = 0, $type = '', $nooutput = 0)
7405 {
7406 // phpcs:enable
7407 global $langs;
7408
7409 $out = '';
7410 if ($htmlname != "none") {
7411 $out .= '<form method="POST" action="' . $page . '">';
7412 $out .= '<input type="hidden" name="action" value="setmode">';
7413 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7414 if ($type) {
7415 $out .= '<input type="hidden" name="type" value="' . dol_escape_htmltag($type) . '">';
7416 }
7417 $out .= $this->select_types_paiements($selected, $htmlname, $filtertype, 0, $addempty, 0, 0, $active, '', 1);
7418 $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7419 $out .= '</form>';
7420 } else {
7421 if ($selected) {
7422 $this->load_cache_types_paiements();
7423 // @phan-suppress-next-line PhanTypeMismatchProperty
7424 $out .= isset($this->cache_types_paiements[(int) $selected]['label']) ? $this->cache_types_paiements[(int) $selected]['label'] : '';
7425 } else {
7426 $out .= "&nbsp;";
7427 }
7428 }
7429
7430 if ($nooutput) {
7431 return $out;
7432 } else {
7433 print $out;
7434 }
7435 return '';
7436 }
7437
7448 public function formSelectTransportMode($page, $selected = '', $htmlname = 'transport_mode_id', $active = 1, $addempty = 0)
7449 {
7450 global $langs;
7451 if ($htmlname != "none") {
7452 print '<form method="POST" action="' . $page . '">';
7453 print '<input type="hidden" name="action" value="settransportmode">';
7454 print '<input type="hidden" name="token" value="' . newToken() . '">';
7455 $this->selectTransportMode($selected, $htmlname, 0, $addempty, 0, 0, $active);
7456 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7457 print '</form>';
7458 } else {
7459 if ($selected) {
7460 $this->load_cache_transport_mode();
7461 print $this->cache_transport_mode[$selected]['label'];
7462 } else {
7463 print "&nbsp;";
7464 }
7465 }
7466 }
7467
7468 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7469
7478 public function form_multicurrency_code($page, $selected = '', $htmlname = 'multicurrency_code')
7479 {
7480 // phpcs:enable
7481 global $langs;
7482 if ($htmlname != "none") {
7483 print '<form method="POST" action="' . $page . '">';
7484 print '<input type="hidden" name="action" value="setmulticurrencycode">';
7485 print '<input type="hidden" name="token" value="' . newToken() . '">';
7486 print $this->selectMultiCurrency($selected, $htmlname, 0);
7487 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7488 print '</form>';
7489 } else {
7490 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
7491 print !empty($selected) ? currency_name($selected, 1) : '&nbsp;';
7492 }
7493 }
7494
7495 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7496
7507 public function form_multicurrency_rate($page, $rate = 0.0, $htmlname = 'multicurrency_tx', $currency = '', $rate_direct = 0.0)
7508 {
7509 // phpcs:enable
7510 global $langs, $conf;
7511
7512 if ($htmlname != "none") {
7513 print '<form method="POST" action="' . $page . '">';
7514 print '<input type="hidden" name="action" value="setmulticurrencyrate">';
7515 print '<input type="hidden" name="token" value="' . newToken() . '">';
7516 print '<input type="text" class="maxwidth75" name="' . $htmlname . '" value="' . (!empty($rate) ? price(price2num($rate, 'CU')) : 1) . '" spellcheck="false" /> ';
7517 print '<select name="calculation_mode" id="calculation_mode">';
7518 print '<option value="1">Change ' . $langs->trans("PriceUHT") . ' of lines</option>';
7519 print '<option value="2">Change ' . $langs->trans("PriceUHTCurrency") . ' of lines</option>';
7520 print '</select> ';
7521 print ajax_combobox("calculation_mode");
7522 print '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7523 print '</form>';
7524 } else {
7525 if (!empty($rate)) {
7526 print price($rate, 1, $langs, 0, 0);
7527 if ($currency && $rate != 1) {
7533 if (getDolGlobalString('MULTICURRENCY_USE_RATE_DIRECT')) {
7534 print ' &nbsp; <span class="opacitymedium">(' . price($rate_direct, 1, $langs, 0, 0) . ' ' . $conf->currency . ' = 1 ' . $currency . ')</span>';
7535 } else {
7536 print ' &nbsp; <span class="opacitymedium">(' . price($rate, 1, $langs, 0, 0) . ' ' . $currency . ' = 1 ' . $conf->currency . ')</span>';
7537 }
7538 }
7539 } else {
7540 print 1;
7541 }
7542 }
7543 }
7544
7545 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7546
7564 public function form_remise_dispo($page, $selected, $htmlname, $socid, $amount, $filter = '', $maxvalue = 0, $more = '', $hidelist = 0, $discount_type = 0, $filterabsolutediscount = 0, $filtercreditnote = 0)
7565 {
7566 // phpcs:enable
7567 global $conf, $langs;
7568
7569 if ($htmlname != "none") {
7570 print '<form method="post" action="' . $page . '" class="inline-block">';
7571 print '<input type="hidden" name="action" value="setabsolutediscount">';
7572 print '<input type="hidden" name="token" value="' . newToken() . '">';
7573 print '<div class="inline-block">';
7574 if (!empty($discount_type)) {
7575 if (getDolGlobalString('FACTURE_SUPPLIER_DEPOSITS_ARE_JUST_PAYMENTS')) {
7576 if (!$filter || $filter == "fk_invoice_supplier_source IS NULL") {
7577 $translationKey = 'HasAbsoluteDiscountFromSupplier'; // If we want deposit to be subtracted to payments only and not to total of final invoice
7578 } else {
7579 $translationKey = 'HasCreditNoteFromSupplier';
7580 }
7581 } else {
7582 if (!$filter || $filter == "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
7583 $translationKey = 'HasAbsoluteDiscountFromSupplier';
7584 } else {
7585 $translationKey = 'HasCreditNoteFromSupplier';
7586 }
7587 }
7588 } else {
7589 if (getDolGlobalString('FACTURE_DEPOSITS_ARE_JUST_PAYMENTS')) {
7590 if (!$filter || $filter == "fk_facture_source IS NULL") {
7591 $translationKey = 'CompanyHasAbsoluteDiscount'; // If we want deposit to be subtracted to payments only and not to total of final invoice
7592 } else {
7593 $translationKey = 'CompanyHasCreditNote';
7594 }
7595 } else {
7596 if (!$filter || $filter == "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
7597 $translationKey = 'CompanyHasAbsoluteDiscount';
7598 } else {
7599 $translationKey = 'CompanyHasCreditNote';
7600 }
7601 }
7602 }
7603 print $langs->trans($translationKey, price($amount, 0, $langs, 0, 0, -1, $conf->currency));
7604 if (empty($hidelist)) {
7605 print ' ';
7606 }
7607 print '</div>';
7608 if (empty($hidelist)) {
7609 print '<div class="inline-block" style="padding-right: 10px">';
7610 $newfilter = 'discount_type = ' . intval($discount_type);
7611 if (!empty($discount_type)) {
7612 $newfilter .= ' AND fk_invoice_supplier IS NULL AND fk_invoice_supplier_line IS NULL'; // Supplier discounts available
7613 } else {
7614 $newfilter .= ' AND fk_facture IS NULL AND fk_facture_line IS NULL'; // Customer discounts available
7615 }
7616 if ($filter) {
7617 $sanitizedfilter = $filter; // @phan-suppress-current-line SqlInjection
7618 $newfilter .= ' AND (' . $sanitizedfilter . ')';
7619 }
7620 // output the combo of discounts
7621 $nbqualifiedlines = $this->select_remises((string) $selected, $htmlname, $newfilter, $socid, $maxvalue);
7622 if ($nbqualifiedlines > 0) {
7623 print ' &nbsp; <input type="submit" class="button smallpaddingimp" value="' . dol_escape_htmltag($langs->trans("UseLine")) . '"';
7624 if (!empty($discount_type) && $filter && $filter != "fk_invoice_supplier_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS PAID)%')") {
7625 print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
7626 }
7627 if (empty($discount_type) && $filter && $filter != "fk_facture_source IS NULL OR (description LIKE '(DEPOSIT)%' AND description NOT LIKE '(EXCESS RECEIVED)%')") {
7628 print ' title="' . $langs->trans("UseCreditNoteInInvoicePayment") . '"';
7629 }
7630
7631 print '>';
7632 }
7633 print '</div>';
7634 }
7635 if ($more) {
7636 print '<div class="inline-block">';
7637 print $more;
7638 print '</div>';
7639 }
7640 print '</form>';
7641 } else {
7642 if ($selected) {
7643 print $selected;
7644 } else {
7645 print "0";
7646 }
7647 }
7648 }
7649
7650
7651 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7652
7662 public function form_contacts($page, $societe, $selected = '', $htmlname = 'contactid')
7663 {
7664 // phpcs:enable
7665 global $langs;
7666
7667 if ($htmlname != "none") {
7668 print '<form method="post" action="' . $page . '">';
7669 print '<input type="hidden" name="action" value="set_contact">';
7670 print '<input type="hidden" name="token" value="' . newToken() . '">';
7671 print '<table class="nobordernopadding">';
7672 print '<tr><td>';
7673 print $this->selectcontacts($societe->id, $selected, $htmlname);
7674 $num = $this->num;
7675 if ($num == 0) {
7676 $addcontact = (getDolGlobalString('SOCIETE_ADDRESSES_MANAGEMENT') ? $langs->trans("AddContact") : $langs->trans("AddContactAddress"));
7677 print '<a href="' . DOL_URL_ROOT . '/contact/card.php?socid=' . $societe->id . '&action=create&backtoreferer=1">' . $addcontact . '</a>';
7678 }
7679 print '</td>';
7680 print '<td class="left"><input type="submit" class="button smallpaddingimp" value="' . $langs->trans("Modify") . '"></td>';
7681 print '</tr></table></form>';
7682 } else {
7683 if ($selected) {
7684 require_once DOL_DOCUMENT_ROOT . '/contact/class/contact.class.php';
7685 $contact = new Contact($this->db);
7686 $contact->fetch((int) $selected);
7687 print $contact->getFullName($langs);
7688 } else {
7689 print "&nbsp;";
7690 }
7691 }
7692 }
7693
7694 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7695
7712 public function form_thirdparty($page, $selected = '', $htmlname = 'socid', $filter = '', $showempty = 0, $showtype = 0, $forcecombo = 0, $events = array(), $nooutput = 0, $excludeids = array(), $textifnothirdparty = '')
7713 {
7714 // phpcs:enable
7715 global $langs;
7716
7717 $out = '';
7718 if ($htmlname != "none") {
7719 $limit = getDolGlobalInt('THIRDPARTY_LIMIT_SIZE');
7720
7721 $out .= '<form method="post" action="' . $page . '">';
7722 $out .= '<input type="hidden" name="action" value="set_thirdparty">';
7723 $out .= '<input type="hidden" name="token" value="' . newToken() . '">';
7724 $out .= $this->select_company($selected, $htmlname, $filter, $showempty, $showtype, $forcecombo, $events, $limit, 'minwidth100', '', '', 1, array(), false, $excludeids);
7725 $out .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans("Modify") . '">';
7726 $out .= '</form>';
7727 } else {
7728 if ($selected) {
7729 require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
7730 $soc = new Societe($this->db);
7731 $soc->fetch((int) $selected);
7732 $out .= $soc->getNomUrl(0, '');
7733 } else {
7734 $out .= '<span class="opacitymedium">' . $textifnothirdparty . '</span>';
7735 }
7736 }
7737
7738 if ($nooutput) {
7739 return $out;
7740 } else {
7741 print $out;
7742 }
7743
7744 return '';
7745 }
7746
7747 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7748
7757 public function select_currency($selected = '', $htmlname = 'currency_id')
7758 {
7759 // phpcs:enable
7760 print $this->selectCurrency($selected, $htmlname);
7761 }
7762
7772 public function selectCurrency($selected = '', $htmlname = 'currency_id', $mode = 0, $useempty = '')
7773 {
7774 global $langs, $user;
7775
7776 $langs->loadCacheCurrencies('');
7777
7778 $out = '';
7779
7780 if ($selected == 'euro' || $selected == 'euros') {
7781 $selected = 'EUR'; // Pour compatibilite
7782 }
7783
7784 $out .= '<select class="flat maxwidth200onsmartphone minwidth300" name="' . $htmlname . '" id="' . $htmlname . '">';
7785 if ($useempty) {
7786 $out .= '<option value="-1" selected></option>';
7787 }
7788 foreach ($langs->cache_currencies as $code_iso => $currency) {
7789 $labeltoshow = $currency['label'];
7790 if ($mode == 1) {
7791 $labeltoshow .= ' <span class="opacitymedium">(' . $code_iso . ')</span>';
7792 } elseif ($mode == 2) {
7793 $labeltoshow .= ' <span class="opacitymedium">(' . $code_iso.' - '.$langs->getCurrencySymbol($code_iso) . ')</span>';
7794 } else {
7795 $labeltoshow .= ' <span class="opacitymedium">(' . $langs->getCurrencySymbol($code_iso) . ')</span>';
7796 }
7797
7798 if ($selected && $selected == $code_iso) {
7799 $out .= '<option value="' . $code_iso . '" selected data-html="' . dol_escape_htmltag($labeltoshow) . '">';
7800 } else {
7801 $out .= '<option value="' . $code_iso . '" data-html="' . dol_escape_htmltag($labeltoshow) . '">';
7802 }
7803 $out .= dol_string_nohtmltag($labeltoshow);
7804 $out .= '</option>';
7805 }
7806 $out .= '</select>';
7807 if ($user->admin) {
7808 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
7809 }
7810
7811 // Make select dynamic
7812 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7813 $out .= ajax_combobox($htmlname);
7814
7815 return $out;
7816 }
7817
7830 public function selectMultiCurrency($selected = '', $htmlname = 'multicurrency_code', $useempty = 0, $filter = '', $excludeConfCurrency = false, $morecss = 'maxwidth200 widthcentpercentminusx')
7831 {
7832 global $conf, $langs;
7833
7834 $langs->loadCacheCurrencies(''); // Load ->cache_currencies
7835
7836 $TCurrency = array();
7837
7838 $sql = "SELECT code FROM " . $this->db->prefix() . "multicurrency";
7839 $sql .= " WHERE entity IN ('" . getEntity('multicurrency') . "')";
7840 if ($filter) {
7841 $sql .= forgeSQLFromUniversalSearchCriteria($filter);
7842 }
7843 $resql = $this->db->query($sql);
7844 if ($resql) {
7845 while ($obj = $this->db->fetch_object($resql)) {
7846 $TCurrency[$obj->code] = $obj->code;
7847 }
7848 }
7849
7850 $out = '';
7851 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
7852 if ($useempty) {
7853 $out .= '<option value="">&nbsp;</option>';
7854 }
7855 // If company current currency not in table, we add it into list. Should always be available.
7856 if (!in_array($conf->currency, $TCurrency) && !$excludeConfCurrency) {
7857 $TCurrency[$conf->currency] = $conf->currency;
7858 }
7859 if (count($TCurrency) > 0) {
7860 foreach ($langs->cache_currencies as $code_iso => $currency) {
7861 if (isset($TCurrency[$code_iso])) {
7862 if (!empty($selected) && $selected == $code_iso) {
7863 $out .= '<option value="' . $code_iso . '" selected="selected">';
7864 } else {
7865 $out .= '<option value="' . $code_iso . '">';
7866 }
7867
7868 $out .= $currency['label'];
7869 $out .= ' (' . $langs->getCurrencySymbol($code_iso) . ')';
7870 $out .= '</option>';
7871 }
7872 }
7873 }
7874
7875 $out .= '</select>';
7876
7877 // Make select dynamic
7878 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
7879 $out .= ajax_combobox($htmlname);
7880
7881 return $out;
7882 }
7883
7884 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7885
7892 public function load_cache_vatrates($country_code)
7893 {
7894 // phpcs:enable
7895 global $langs, $user;
7896
7897 $num = count($this->cache_vatrates);
7898 if ($num > 0) {
7899 return $num; // Cache already loaded
7900 }
7901
7902 dol_syslog(__METHOD__, LOG_DEBUG);
7903
7904 $sql = "SELECT t.rowid, t.type_vat, t.code, t.taux, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.recuperableonly, t.einvoice_vatex";
7905 $sql .= " FROM ".$this->db->prefix()."c_tva as t, ".$this->db->prefix()."c_country as c";
7906 $sql .= " WHERE t.fk_pays = c.rowid";
7907 $sql .= " AND t.active > 0";
7908 $sql .= " AND t.entity IN (".getEntity('c_tva').")";
7909 $sql .= " AND c.code IN (" . $this->db->sanitize($country_code, 1) . ")";
7910 $sql .= " ORDER BY t.code ASC, t.taux ASC, t.recuperableonly ASC";
7911
7912 $resql = $this->db->query($sql);
7913 if ($resql) {
7914 $num = $this->db->num_rows($resql);
7915 if ($num) {
7916 for ($i = 0; $i < $num; $i++) {
7917 $obj = $this->db->fetch_object($resql);
7918
7919 $tmparray = array();
7920 $tmparray['rowid'] = (int) $obj->rowid;
7921 $tmparray['type_vat'] = ($obj->type_vat <= 0 ? 0 : $obj->type_vat); // Some version have type_vat corrupted with value -1
7922 $tmparray['code'] = $obj->code;
7923 $tmparray['txtva'] = $obj->taux;
7924 $tmparray['nprtva'] = $obj->recuperableonly;
7925 $tmparray['localtax1'] = $obj->localtax1;
7926 $tmparray['localtax1_type'] = $obj->localtax1_type;
7927 $tmparray['localtax2'] = $obj->localtax2;
7928 $tmparray['localtax2_type'] = $obj->localtax1_type;
7929 $tmparray['einvoice_vatex'] = $obj->einvoice_vatex;
7930
7931 $tmparray['label'] = $obj->taux . '%' . ($obj->code ? ' (' . $obj->code . ')' : ''); // Label must contains only 0-9 , . % or *
7932 $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
7933 $positiverates = '';
7934 if ($obj->taux) {
7935 $positiverates .= ($positiverates ? '/' : '') . $obj->taux;
7936 }
7937 if ($obj->localtax1) {
7938 $positiverates .= ($positiverates ? '/' : '') . $obj->localtax1;
7939 }
7940 if ($obj->localtax2) {
7941 $positiverates .= ($positiverates ? '/' : '') . $obj->localtax2;
7942 }
7943 if (empty($positiverates)) {
7944 $positiverates = '0';
7945 }
7946 $tmparray['labelpositiverates'] = $positiverates . ($obj->code ? ' (' . $obj->code . ')' : ''); // Must never be used as key, only label
7947
7948 $this->cache_vatrates[$obj->rowid] = $tmparray;
7949 }
7950
7951 return $num;
7952 } else {
7953 $this->error = '<span class="error">';
7954 $this->error .= $langs->trans("ErrorNoVATRateDefinedForSellerCountry", $country_code);
7955 $reg = array();
7956 if (!empty($user) && $user->admin && preg_match('/\'(..)\'/', $country_code, $reg)) {
7957 $langs->load("errors");
7958 $new_country_code = $reg[1];
7959 $country_id = dol_getIdFromCode($this->db, $new_country_code, 'c_country', 'code', 'rowid');
7960 $this->error .= '<br>'.$langs->trans("ErrorFixThisHere", DOL_URL_ROOT.'/admin/dict.php?id=10'.($country_id > 0 ? '&countryidforinsert='.$country_id : ''));
7961 }
7962 $this->error .= '</span>';
7963 return -1;
7964 }
7965 } else {
7966 $this->error = '<span class="error">' . $this->db->error() . '</span>';
7967 return -2;
7968 }
7969 }
7970
7971 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7972
7995 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)
7996 {
7997 // phpcs:enable
7998 global $langs, $mysoc, $hookmanager;
7999
8000 $langs->load('errors');
8001
8002 $return = '';
8003 // Bypass the default method
8004 $hookmanager->initHooks(array('commonobject'));
8005 $info_bits == 1 ? $is_npr = 1 : $is_npr = 0;
8006 $parameters = array(
8007 'htmlname' => $htmlname,
8008 'selectedrate' => $selectedrate,
8009 'seller' => $societe_vendeuse,
8010 'buyer' => $societe_acheteuse,
8011 'idprod' => $idprod,
8012 'is_npr' => $is_npr,
8013 'type' => $type,
8014 'options_only' => $options_only,
8015 'mode' => $mode,
8016 'type_vat' => $type_vat
8017 );
8018 $reshook = $hookmanager->executeHooks('load_tva', $parameters);
8019 if ($reshook > 0) {
8020 return $hookmanager->resPrint;
8021 } elseif ($reshook === 0) {
8022 $return .= $hookmanager->resPrint;
8023 }
8024
8025 // Define defaultnpr, defaultttx and defaultcode
8026 $defaultnpr = ($info_bits & 0x01);
8027 $defaultnpr = (preg_match('/\*/', $selectedrate) ? 1 : $defaultnpr);
8028 $defaulttx = str_replace('*', '', $selectedrate);
8029 $defaultcode = '';
8030 $reg = array();
8031 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8032 $defaultcode = $reg[1];
8033 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8034 }
8035 //var_dump($selectedrate.'-'.$defaulttx.'-'.$defaultnpr.'-'.$defaultcode);
8036
8037 // Check parameters
8038 if (is_object($societe_vendeuse) && !$societe_vendeuse->country_code) {
8039 if ($societe_vendeuse->id == $mysoc->id) {
8040 $return .= '<span class="error">' . $langs->trans("ErrorYourCountryIsNotDefined") . '</span>';
8041 } else {
8042 $return .= '<span class="error">' . $langs->trans("ErrorSupplierCountryIsNotDefined") . '</span>';
8043 }
8044 return $return;
8045 }
8046
8047 //var_dump($societe_acheteuse);
8048 //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";
8049 //exit;
8050
8051 // Define list of countries to use to search VAT rates to show
8052 // First we defined code_country to use to find list.
8053 // country_code must be a c_country ISO code (e.g. 'FR', 'CH'). In some setups it may hold a
8054 // country label (e.g. 'Suisse') instead, which would make the "c.code IN (...)" lookup done by
8055 // load_cache_vatrates() match nothing and wrongly force the VAT rate to 0%. A valid ISO code is
8056 // always 2 chars, so when the value is not a well formed ISO code (empty or a label) and we have
8057 // a valid country id, we recover the ISO code from the authoritative country id. This way we do
8058 // not run any SQL on each page access when we already have a valid ISO code.
8059 $sellercountrycode = is_object($societe_vendeuse) ? $societe_vendeuse->country_code : $mysoc->country_code;
8060 $sellercountryid = is_object($societe_vendeuse) ? $societe_vendeuse->country_id : $mysoc->country_id;
8061 if ((int) $sellercountryid > 0 && strlen((string) $sellercountrycode) != 2) {
8062 $tmpcountrycode = dol_getIdFromCode($this->db, (string) $sellercountryid, 'c_country', 'rowid', 'code');
8063 if (!empty($tmpcountrycode) && !is_numeric($tmpcountrycode)) {
8064 $sellercountrycode = $tmpcountrycode;
8065 }
8066 }
8067 $code_country = "'" . $sellercountrycode . "'"; // Pour compatibilite ascendente
8068
8069 if ($societe_vendeuse == $mysoc && getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC')) { // If option to have vat for end customer for services is on
8070 require_once DOL_DOCUMENT_ROOT . '/core/lib/company.lib.php';
8071 // If SERVICE_ARE_ECOMMERCE_200238EC=1 combo list vat rate of purchaser and seller countries
8072 // If SERVICE_ARE_ECOMMERCE_200238EC=2 combo list only the vat rate of the purchaser country
8073 $selectVatComboMode = getDolGlobalString('SERVICE_ARE_ECOMMERCE_200238EC');
8074 if (is_object($societe_vendeuse) && is_object($societe_acheteuse) && isInEEC($societe_vendeuse) && isInEEC($societe_acheteuse) && !$societe_acheteuse->isACompany()) {
8075 // We also add the buyer country code
8076 if (is_numeric($type)) {
8077 if ($type == 1) { // We know product is a service
8078 switch ($selectVatComboMode) {
8079 case '1':
8080 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8081 break;
8082 case '2':
8083 $code_country = "'" . $societe_acheteuse->country_code . "'";
8084 break;
8085 }
8086 }
8087 } elseif (!$idprod) { // We don't know type of product
8088 switch ($selectVatComboMode) {
8089 case '1':
8090 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8091 break;
8092 case '2':
8093 $code_country = "'" . $societe_acheteuse->country_code . "'";
8094 break;
8095 }
8096 } else {
8097 $prodstatic = new Product($this->db);
8098 $prodstatic->fetch($idprod);
8099 if ($prodstatic->type == Product::TYPE_SERVICE) { // We know product is a service
8100 $code_country .= ",'" . $societe_acheteuse->country_code . "'";
8101 }
8102 }
8103 }
8104 }
8105
8106 // Now we load the list of VAT
8107 $this->load_cache_vatrates($code_country); // If no vat defined, return -1 with message into this->error
8108
8109 // Keep only the VAT qualified for $type_vat
8110 $arrayofvatrates = array();
8111 foreach ($this->cache_vatrates as $cachevalue) {
8112 if (empty($cachevalue['type_vat']) || $cachevalue['type_vat'] == $type_vat) {
8113 $arrayofvatrates[] = $cachevalue;
8114 }
8115 }
8116
8117 $num = count($arrayofvatrates);
8118 if ($num > 0) {
8119 // Define the vat rate to preselect (if defaulttx not forced so is -1 or '')
8120 if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
8121 // Define a default thirdparty to use if the seller or buyer is not defined
8122 $tmpthirdparty = new Societe($this->db);
8123 $tmpthirdparty->country_code = $mysoc->country_code;
8124
8125 $defaulttx = get_default_tva(is_object($societe_vendeuse) ? $societe_vendeuse : $tmpthirdparty, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
8126 $defaultnpr = get_default_npr(is_object($societe_vendeuse) ? $societe_vendeuse : $tmpthirdparty, (is_object($societe_acheteuse) ? $societe_acheteuse : $tmpthirdparty), $idprod);
8127
8128 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8129 $defaultcode = $reg[1];
8130 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8131 }
8132 if (empty($defaulttx)) {
8133 $defaultnpr = 0;
8134 }
8135 }
8136
8137 // If we fails to find a default vat rate, we take the last one in list
8138 // Because they are sorted in ascending order, the last one will be the higher one (we suppose the higher one is the current rate)
8139 if ($defaulttx < 0 || dol_strlen($defaulttx) == 0) {
8140 if (!getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS')) {
8141 // We take the last one found in list
8142 $defaulttx = $arrayofvatrates[$num - 1]['txtva'];
8143 } else {
8144 // We will use the rate defined into MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS
8145 $defaulttx = '';
8146 if (getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS') != 'none') {
8147 $defaulttx = getDolGlobalString('MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS');
8148 }
8149 if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
8150 $defaultcode = $reg[1];
8151 $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
8152 }
8153 }
8154 }
8155
8156 // Disabled is true if the seller is not subject to VAT
8157 $disabled = false;
8158 $title = '';
8159 if (is_object($societe_vendeuse) && $societe_vendeuse->id == $mysoc->id && empty($societe_vendeuse->tva_assuj)) {
8160 // 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
8161 // EXPENSEREPORT_OVERRIDE_VAT is a strange option that allow to override/enable VAT regardless of sellet vat option - needed for expense report if
8162 // expense report used for business expenses instead of using supplier invoices (but this is a very bad idea !)
8163 if (!getDolGlobalString('EXPENSEREPORT_OVERRIDE_VAT')) {
8164 $title = ' title="' . dol_escape_htmltag($langs->trans('VATIsNotUsed')) . '"';
8165 $disabled = true;
8166 }
8167 }
8168
8169 if (!$options_only) {
8170 $return .= '<select class="flat valignmiddle minwidth75imp maxwidth100 right" id="' . $htmlname . '" name="' . $htmlname . '"' . ($disabled ? ' disabled' : '') . $title . '>';
8171 }
8172
8173 $selectedfound = false;
8174 foreach ($arrayofvatrates as $rate) {
8175 // Keep only 0 if seller is not subject to VAT
8176 if ($disabled && $rate['txtva'] != 0) {
8177 continue;
8178 }
8179
8180 // Define key to use into select list
8181 $key = $rate['txtva'];
8182 $key .= $rate['nprtva'] ? '*' : '';
8183 if ($mode > 0 && $rate['code']) {
8184 $key .= ' (' . $rate['code'] . ')';
8185 }
8186 if ($mode < 0) {
8187 $key = $rate['rowid'];
8188 }
8189
8190 $return .= '<option value="' . $key . '" data-vatid="'.$rate['rowid'].'"';
8191 if (!$selectedfound) {
8192 if ($defaultcode) { // If defaultcode is defined, we used it in priority to select combo option instead of using rate+npr flag
8193 if ($defaultcode == $rate['code']) {
8194 $return .= ' selected';
8195 $selectedfound = true;
8196 }
8197 } elseif ($rate['txtva'] == $defaulttx && $rate['nprtva'] == $defaultnpr) {
8198 $return .= ' selected';
8199 $selectedfound = true;
8200 }
8201 }
8202 $return .= '>';
8203
8204 // Show label of VAT
8205 if ($mysoc->country_code == 'IN' || getDolGlobalString('MAIN_VAT_LABEL_IS_POSITIVE_RATES')) {
8206 // Label with all localtax and code. For example: x.y / a.b / c.d (CODE)'
8207 $return .= $rate['labelpositiverates'];
8208 } else {
8209 // Simple label
8210 $return .= vatrate($rate['label']);
8211 }
8212
8213 //$return.=($rate['code']?' '.$rate['code']:'');
8214 $return .= (empty($rate['code']) && $rate['nprtva']) ? ' *' : ''; // We show the * (old behaviour only if new vat code is not used)
8215
8216 $return .= '</option>';
8217 }
8218
8219 if (!$options_only) {
8220 $return .= '</select>';
8221 //$return .= ajax_combobox($htmlname); // This break for the moment the dynamic autoselection of a value when selecting a product in object lines
8222 }
8223 } else {
8224 $return .= $this->error;
8225 }
8226
8227 $this->num = $num;
8228 return $return;
8229 }
8230
8231
8232 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8233
8258 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 = '')
8259 {
8260 // phpcs:enable
8261 dol_syslog(__METHOD__ . ': using select_date is deprecated. Use selectDate instead.', LOG_WARNING);
8262 $retstring = $this->selectDate($set_time, $prefix, $h, $m, $empty, $form_name, $d, $addnowlink, $disabled, $fullday, $addplusone, $adddateof);
8263 if (!empty($nooutput)) {
8264 return $retstring;
8265 }
8266 print $retstring;
8267
8268 return '';
8269 }
8270
8286 public function selectDateToDate($set_time = '', $set_time_end = '', $prefix = 're', $empty = 0, $forcenewline = 0)
8287 {
8288 global $langs;
8289
8290 $ret = $this->selectDate($set_time, $prefix . '_start', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("from"), 'tzuserrel');
8291 if ($forcenewline) {
8292 $ret .= '<br>';
8293 }
8294 $ret .= $this->selectDate($set_time_end, $prefix . '_end', 0, 0, $empty, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans("to"), 'tzuserrel');
8295 return $ret;
8296 }
8297
8326 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 = '')
8327 {
8328 global $conf, $langs;
8329
8330 if ($gm === 'auto') {
8331 $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
8332 }
8333
8334 $retstring = '';
8335
8336 if ($prefix == '') {
8337 $prefix = 're';
8338 }
8339 if ($h == '') {
8340 $h = 0;
8341 }
8342 if ($m == '') {
8343 $m = 0;
8344 }
8345 $emptydate = 0;
8346 $emptyhours = 0;
8347 if ($stepminutes <= 0 || $stepminutes > 30) {
8348 $stepminutes = 1;
8349 }
8350 if ($empty == 1) {
8351 $emptydate = 1;
8352 $emptyhours = 1;
8353 }
8354 if ($empty == 2) {
8355 $emptydate = 0;
8356 $emptyhours = 1;
8357 }
8358 $orig_set_time = $set_time;
8359
8360 if ($set_time === '' && $emptydate == 0) {
8361 include_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
8362 if ($gm == 'tzuser' || $gm == 'tzuserrel') {
8363 $set_time = dol_now($gm);
8364 } else {
8365 $set_time = dol_now('tzuser') - (getServerTimeZoneInt('now') * 3600); // set_time must be relative to PHP server timezone
8366 }
8367 }
8368
8369 // Analysis of the preselected date
8370 $reg = array();
8371 $shour = '';
8372 $smin = '';
8373 $ssec = '';
8374 if (!empty($set_time) && preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/', (string) $set_time, $reg)) { // deprecated usage
8375 // Date format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
8376 $syear = (!empty($reg[1]) ? $reg[1] : '');
8377 $smonth = (!empty($reg[2]) ? $reg[2] : '');
8378 $sday = (!empty($reg[3]) ? $reg[3] : '');
8379 $shour = (!empty($reg[4]) ? $reg[4] : '');
8380 $smin = (!empty($reg[5]) ? $reg[5] : '');
8381 } elseif (strval($set_time) != '' && $set_time != -1) {
8382 // set_time est un timestamps (0 possible)
8383 $syear = dol_print_date($set_time, "%Y", $gm);
8384 $smonth = dol_print_date($set_time, "%m", $gm);
8385 $sday = dol_print_date($set_time, "%d", $gm);
8386 if ($orig_set_time != '') {
8387 $shour = dol_print_date($set_time, "%H", $gm);
8388 $smin = dol_print_date($set_time, "%M", $gm);
8389 $ssec = dol_print_date($set_time, "%S", $gm);
8390 }
8391 } else {
8392 // Date est '' ou vaut -1
8393 $syear = '';
8394 $smonth = '';
8395 $sday = '';
8396 $shour = getDolGlobalString('MAIN_DEFAULT_DATE_HOUR', ($h == -1 ? '23' : ''));
8397 $smin = getDolGlobalString('MAIN_DEFAULT_DATE_MIN', ($h == -1 ? '59' : ''));
8398 $ssec = getDolGlobalString('MAIN_DEFAULT_DATE_SEC', ($h == -1 ? '59' : ''));
8399 }
8400 if ($h == 3 || $h == 4) {
8401 $shour = '';
8402 }
8403 if ($m == 3) {
8404 $smin = '';
8405 }
8406
8407 $nowgmt = dol_now('gmt');
8408 //var_dump(dol_print_date($nowgmt, 'dayhourinputnoreduce', 'tzuserrel'));
8409
8410 // You can set MAIN_POPUP_CALENDAR to 'eldy' or 'jquery'
8411 $usecalendar = 'combo';
8412 if (!empty($conf->use_javascript_ajax) && (!getDolGlobalString('MAIN_POPUP_CALENDAR') || getDolGlobalString('MAIN_POPUP_CALENDAR') != "none")) {
8413 $usecalendar = ((!getDolGlobalString('MAIN_POPUP_CALENDAR') || getDolGlobalString('MAIN_POPUP_CALENDAR') == 'eldy') ? 'jquery' : getDolGlobalString("MAIN_POPUP_CALENDAR"));
8414 }
8415 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
8416 // If we use a text browser or screen reader, we use the 'combo' date selector
8417 $usecalendar = 'html';
8418 }
8419
8420 if ($d) {
8421 // Show date with popup
8422 if ($usecalendar != 'combo') {
8423 // Set $format and $formatjs and $formatjquery
8424 $reduceformat = (!empty($conf->dol_optimize_smallscreen) ? 1 : 0); // Test on original $format param.
8425 if ($reduceformat) {
8426 $format = str_replace('%Y', '%y', $langs->transnoentitiesnoconv("FormatDateShortInput")); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
8427 $formatjslong = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // don't trust the name
8428 $formatjs = str_replace('yyyy', 'yy', $langs->transnoentitiesnoconv("FormatDateShortJavaInput"));
8429 $formatjquery = str_replace('yyyy', 'yy', $langs->trans("FormatDateShortJQueryInput"));
8430 } else {
8431 $format = $langs->transnoentitiesnoconv("FormatDateShortInput"); // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8432 $formatjslong = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // don't trust the name
8433 $formatjs = $langs->transnoentitiesnoconv("FormatDateShortJavaInput"); // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8434 $formatjquery = $langs->trans("FormatDateShortJQueryInput");
8435 }
8436
8437 // Set formatted_date (for example: '%d/%m/%Y', '%m-%d-%y', ...
8438 $formatted_date = '';
8439 if (strval($set_time) != '' && $set_time != -1) {
8440 $formatted_date = dol_print_date($set_time, $format, $gm); // FormatDateShortInput for dol_print_date / FormatDateShortJavaInput that is same for javascript
8441 }
8442
8443 // Calendrier popup version eldy
8444 if ($usecalendar == "eldy") {
8445 // To have this manager working back, you must retrieve all functions showDP child found into the lib_head.js of v4 for example
8446 // and load the js that contains them so the call of showDP will works.
8447 /*
8448 // Input area to enter date manually
8449 $retstring .= '<!-- datepicker usecalendar=eldy --><input id="' . $prefix . '" name="' . $prefix . '" type="text" class="maxwidthdate center" maxlength="11" value="' . $formatted_date . '"';
8450 $retstring .= ($disabled ? ' disabled' : '');
8451 $retstring .= ' onChange="dpChangeDay(\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($formatjslong")) . '\'); "'; // FormatDateShortInput for dol_print_date is same than FormatDateShortJavaInput for javascript
8452 $retstring .= ' autocomplete="off">';
8453
8454 // Icon calendar
8455 $retstringbuttom = '';
8456 if (!$disabled) {
8457 $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons"';
8458 $base = DOL_URL_ROOT . '/core/';
8459 $retstringbuttom .= ' onClick="showDP(\'' . dol_escape_js($base) . '\',\'' . dol_escape_js($prefix) . '\',\'' . dol_escape_js($langs->trans("FormatDateShortJavaInput")) . '\',\'' . dol_escape_js($langs->defaultlang) . '\');"';
8460 $retstringbuttom .= '>' . img_object($langs->trans("SelectDate"), 'calendarday', 'class="datecallink paddingright"') . '</button>';
8461 } else {
8462 $retstringbuttom = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink paddingright"') . '</button>';
8463 }
8464 $retstring = $retstringbuttom . $retstring;
8465
8466 $retstring .= '<input type="hidden" id="' . $prefix . 'day" name="' . $prefix . 'day" value="' . $sday . '">' . "\n";
8467 $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
8468 $retstring .= '<input type="hidden" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">' . "\n";
8469 */
8470 } elseif ($usecalendar == 'jquery' || $usecalendar == 'html') {
8471 if (!$disabled && $usecalendar != 'html') {
8472 // Output javascript for datepicker
8473 $minYear = getDolGlobalInt('MIN_YEAR_SELECT_DATE', (idate('Y') - 100));
8474 $maxYear = getDolGlobalInt('MAX_YEAR_SELECT_DATE', (idate('Y') + 100));
8475
8476 $retstring .= '<!-- datepicker usecalendar='.$usecalendar.' --><script nonce="' . getNonce() . '" type="text/javascript">';
8477 $retstring .= "$(function(){ $('#" . $prefix . "').datepicker({
8478 dateFormat: '" . dol_escape_js($formatjquery) . "',
8479 autoclose: true,
8480 todayHighlight: true,
8481 yearRange: '" . $minYear . ":" . $maxYear . "',";
8482 if (!empty($conf->dol_use_jmobile)) {
8483 $retstring .= "
8484 beforeShow: function (input, datePicker) {
8485 input.disabled = true;
8486 },
8487 onClose: function (dateText, datePicker) {
8488 this.disabled = false;
8489 },
8490 ";
8491 }
8492 // Note: We don't need monthNames, monthNamesShort, dayNames, dayNamesShort, dayNamesMin, they are set globally on datepicker component in lib_head.js.php
8493 if (!getDolGlobalString('MAIN_POPUP_CALENDAR_ON_FOCUS')) {
8494 $buttonImage = $calendarpicto ?: DOL_URL_ROOT . "/theme/" . dol_escape_js($conf->theme) . "/img/object_calendarday.png";
8495 $retstring .= "
8496 showOn: 'button', /* both has problem with autocompletion */
8497 buttonImage: '" . $buttonImage . "',
8498 buttonImageOnly: true";
8499 }
8500 $retstring .= "
8501 }) });";
8502 $retstring .= "</script>";
8503 }
8504
8505 // Input area to enter date manually
8506 $retstring .= '<div class="nowraponall inline-block divfordateinput">';
8507 $retstring .= '<input id="'.$prefix.'" name="'.$prefix.'" type="'.($usecalendar == 'html' ? "date" : "text").'" class="maxwidthdate'.(getDolUserString('MAIN_OPTIMIZEFORTEXTBROWSER') ? ' textbrowser' : '').' center" maxlength="11" value="'.$formatted_date.'"';
8508 $retstring .= ($disabled ? ' disabled' : '');
8509 $retstring .= ($placeholder ? ' placeholder="' . dol_escape_htmltag($placeholder) . '"' : '');
8510 $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
8511 $retstring .= ' autocomplete="off">';
8512
8513 // Icon calendar
8514 if ($disabled) {
8515 $retstringbutton = '<button id="' . $prefix . 'Button" type="button" class="dpInvisibleButtons">' . img_object($langs->trans("Disabled"), 'calendarday', 'class="datecallink ui-datepicker-notrigger"') . '</button>';
8516 $retstring .= $retstringbutton;
8517 }
8518
8519 $retstring .= '</div>';
8520 $retstring .= '<input type="hidden" id="' . $prefix . 'day" name="' . $prefix . 'day" value="' . $sday . '">' . "\n";
8521 $retstring .= '<input type="hidden" id="' . $prefix . 'month" name="' . $prefix . 'month" value="' . $smonth . '">' . "\n";
8522 $retstring .= '<input type="hidden" id="' . $prefix . 'year" name="' . $prefix . 'year" value="' . $syear . '">' . "\n";
8523 } else {
8524 $retstring .= "Bad value of MAIN_POPUP_CALENDAR";
8525 }
8526 } else {
8527 // Show date with combo selects
8528 // Day
8529 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth50imp" id="' . $prefix . 'day" name="' . $prefix . 'day">';
8530
8531 if ($emptydate || $set_time == -1) {
8532 $retstring .= '<option value="0" selected>&nbsp;</option>';
8533 }
8534
8535 for ($day = 1; $day <= 31; $day++) {
8536 $retstring .= '<option value="' . $day . '"' . ($day == $sday ? ' selected' : '') . '>' . $day . '</option>';
8537 }
8538
8539 $retstring .= "</select>";
8540
8541 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'month" name="' . $prefix . 'month">';
8542 if ($emptydate || $set_time == -1) {
8543 $retstring .= '<option value="0" selected>&nbsp;</option>';
8544 }
8545
8546 // Month
8547 for ($month = 1; $month <= 12; $month++) {
8548 $retstring .= '<option value="' . $month . '"' . ($month == $smonth ? ' selected' : '') . '>';
8549 $retstring .= dol_print_date(mktime(12, 0, 0, $month, 1, 2000), "%b");
8550 $retstring .= "</option>";
8551 }
8552 $retstring .= "</select>";
8553
8554 // Year
8555 if ($emptydate || $set_time == -1) {
8556 $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 . '">';
8557 } else {
8558 $retstring .= '<select' . ($disabled ? ' disabled' : '') . ' class="flat valignmiddle maxwidth75imp" id="' . $prefix . 'year" name="' . $prefix . 'year">';
8559
8560 $syear = (int) $syear;
8561 for ($year = $syear - 10; $year < (int) $syear + 10; $year++) {
8562 $retstring .= '<option value="' . $year . '"' . ($year == $syear ? ' selected' : '') . '>' . $year . '</option>';
8563 }
8564 $retstring .= "</select>\n";
8565 }
8566 }
8567 }
8568
8569 if ($d && $h) {
8570 $retstring .= (($h == 2 || $h == 4) ? '<br>' : ' ');
8571 $retstring .= '<span class="nowraponall">';
8572 }
8573
8574 if ($h) {
8575 $hourstart = 0;
8576 $hourend = 24;
8577 if ($openinghours != '') {
8578 $openinghours = explode(',', $openinghours);
8579 $hourstart = $openinghours[0];
8580 $hourend = $openinghours[1];
8581 if ($hourend < $hourstart) {
8582 $hourend = $hourstart;
8583 }
8584 }
8585
8586 // Show hour
8587 $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
8588 $retstring .= ($fullday ? $fullday . 'hour' : '') . '" id="' . $prefix . 'hour" name="' . $prefix . 'hour">';
8589 if ($emptyhours) {
8590 $retstring .= '<option value="-1">&nbsp;</option>';
8591 }
8592 for ($hour = $hourstart; $hour < $hourend; $hour++) {
8593 if (strlen($hour) < 2) {
8594 $hour = "0" . $hour;
8595 }
8596 $retstring .= '<option value="' . $hour . '"' . (($hour == $shour) ? ' selected' : '') . '>' . $hour;
8597 $retstring .= '</option>';
8598 }
8599 $retstring .= '</select>';
8600
8601 if ($disabled) {
8602 $retstring .= '<input type="hidden" id="' . $prefix . 'hour" name="' . $prefix . 'hour" value="' . $shour . '">' . "\n";
8603 }
8604 if ($m) {
8605 $retstring .= ":";
8606 }
8607 }
8608
8609 if ($m) {
8610 // Show minutes
8611 $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
8612 $retstring .= ($fullday ? $fullday . 'min' : '') . '" id="' . $prefix . 'min" name="' . $prefix . 'min">';
8613 if ($emptyhours) {
8614 $retstring .= '<option value="-1">&nbsp;</option>';
8615 }
8616 for ($min = 0; $min < 60; $min += $stepminutes) {
8617 $min_str = sprintf("%02d", $min);
8618 $retstring .= '<option value="' . $min_str . '"' . (($min_str == $smin) ? ' selected' : '') . '>' . $min_str . '</option>';
8619 }
8620 $retstring .= '</select>';
8621 if ($disabled) {
8622 $retstring .= '<input type="hidden" id="' . $prefix . 'min" name="' . $prefix . 'min" value="' . $smin . '">' . "\n";
8623 }
8624 // Add also seconds
8625 $retstring .= '<input type="hidden" name="' . $prefix . 'sec" value="' . $ssec . '">';
8626 }
8627
8628 if ($d && $h) {
8629 $retstring .= '</span>';
8630 }
8631
8632 // Add a "Now" link
8633 if (!empty($conf->use_javascript_ajax) && $addnowlink && !$disabled) {
8634 // Script which will be inserted in the onClick of the "Now" link
8635 $reset_scripts = "";
8636 if ($addnowlink == 2) { // local computer time
8637 // pad add leading 0 on numbers
8638 $reset_scripts .= "Number.prototype.pad = function(size) {
8639 var s = String(this);
8640 while (s.length < (size || 2)) {s = '0' + s;}
8641 return s;
8642 };
8643 var d = new Date();";
8644 }
8645
8646 // Generate the date part, depending on the use or not of the javascript calendar
8647 if ($addnowlink == 1) { // server time expressed in user time setup
8648 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
8649 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8650 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8651 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8652 } elseif ($addnowlink == 2) {
8653 /* Disabled because the output does not use the string format defined by FormatDateShort key to forge the value into #prefix.
8654 * This break application for foreign languages.
8655 $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(d.toLocaleDateString(\''.str_replace('_', '-', $langs->defaultlang).'\'));';
8656 $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(d.getDate().pad());';
8657 $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(parseInt(d.getMonth().pad()) + 1);';
8658 $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(d.getFullYear());';
8659 */
8660 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'day', 'tzuserrel') . '\');';
8661 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8662 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8663 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8664 }
8665 /*if ($usecalendar == "eldy")
8666 {
8667 $base=DOL_URL_ROOT.'/core/';
8668 $reset_scripts .= 'resetDP(\''.$base.'\',\''.$prefix.'\',\''.$langs->trans("FormatDateShortJavaInput").'\',\''.$langs->defaultlang.'\');';
8669 }
8670 else
8671 {
8672 $reset_scripts .= 'this.form.elements[\''.$prefix.'day\'].value=formatDate(new Date(), \'d\'); ';
8673 $reset_scripts .= 'this.form.elements[\''.$prefix.'month\'].value=formatDate(new Date(), \'M\'); ';
8674 $reset_scripts .= 'this.form.elements[\''.$prefix.'year\'].value=formatDate(new Date(), \'yyyy\'); ';
8675 }*/
8676 // Update the hour part
8677 if ($h) {
8678 if ($fullday) {
8679 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8680 }
8681 //$reset_scripts .= 'this.form.elements[\''.$prefix.'hour\'].value=formatDate(new Date(), \'HH\'); ';
8682 if ($addnowlink == 1) {
8683 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
8684 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
8685 } elseif ($addnowlink == 2) {
8686 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(d.getHours().pad());';
8687 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').change();';
8688 }
8689
8690 if ($fullday) {
8691 $reset_scripts .= ' } ';
8692 }
8693 }
8694 // Update the minute part
8695 if ($m) {
8696 if ($fullday) {
8697 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8698 }
8699 //$reset_scripts .= 'this.form.elements[\''.$prefix.'min\'].value=formatDate(new Date(), \'mm\'); ';
8700 if ($addnowlink == 1) {
8701 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
8702 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
8703 } elseif ($addnowlink == 2) {
8704 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(d.getMinutes().pad());';
8705 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').change();';
8706 }
8707 if ($fullday) {
8708 $reset_scripts .= ' } ';
8709 }
8710 }
8711 // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
8712 if ($reset_scripts && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
8713 $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonNow" type="button" name="_useless" value="now" onClick="' . $reset_scripts . '">';
8714 $retstring .= $langs->trans("Now");
8715 $retstring .= '</button> ';
8716 }
8717 }
8718
8719 // Add a "Plus one hour" link
8720 if ($conf->use_javascript_ajax && $addplusone && !$disabled) {
8721 // Script which will be inserted in the onClick of the "Add plusone" link
8722 $reset_scripts = "";
8723
8724 // Generate the date part, depending on the use or not of the javascript calendar
8725 $reset_scripts .= 'jQuery(\'#' . $prefix . '\').val(\'' . dol_print_date($nowgmt, 'dayinputnoreduce', 'tzuserrel') . '\');';
8726 $reset_scripts .= 'jQuery(\'#' . $prefix . 'day\').val(\'' . dol_print_date($nowgmt, '%d', 'tzuserrel') . '\');';
8727 $reset_scripts .= 'jQuery(\'#' . $prefix . 'month\').val(\'' . dol_print_date($nowgmt, '%m', 'tzuserrel') . '\');';
8728 $reset_scripts .= 'jQuery(\'#' . $prefix . 'year\').val(\'' . dol_print_date($nowgmt, '%Y', 'tzuserrel') . '\');';
8729 // Update the hour part
8730 if ($h) {
8731 if ($fullday) {
8732 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8733 }
8734 $reset_scripts .= 'jQuery(\'#' . $prefix . 'hour\').val(\'' . dol_print_date($nowgmt, '%H', 'tzuserrel') . '\');';
8735 if ($fullday) {
8736 $reset_scripts .= ' } ';
8737 }
8738 }
8739 // Update the minute part
8740 if ($m) {
8741 if ($fullday) {
8742 $reset_scripts .= " if (jQuery('#fullday:checked').val() == null) {";
8743 }
8744 $reset_scripts .= 'jQuery(\'#' . $prefix . 'min\').val(\'' . dol_print_date($nowgmt, '%M', 'tzuserrel') . '\');';
8745 if ($fullday) {
8746 $reset_scripts .= ' } ';
8747 }
8748 }
8749 // If reset_scripts is not empty, print the link with the reset_scripts in the onClick
8750 if ($reset_scripts && empty($conf->dol_optimize_smallscreen)) {
8751 $retstring .= ' <button class="dpInvisibleButtons datenowlink" id="' . $prefix . 'ButtonPlusOne" type="button" name="_useless2" value="plusone" onClick="' . $reset_scripts . '">';
8752 $retstring .= $langs->trans("DateStartPlusOne");
8753 $retstring .= '</button> ';
8754 }
8755 }
8756
8757 // Add a link to set data
8758 if ($conf->use_javascript_ajax && !empty($adddateof) && !$disabled) {
8759 if (!is_array($adddateof)) {
8760 $arrayofdateof = array(array('adddateof' => $adddateof, 'labeladddateof' => $labeladddateof));
8761 } else {
8762 $arrayofdateof = $adddateof;
8763 }
8764 foreach ($arrayofdateof as $valuedateof) {
8765 $tmpadddateof = empty($valuedateof['adddateof']) ? 0 : $valuedateof['adddateof'];
8766 $tmplabeladddateof = empty($valuedateof['labeladddateof']) ? '' : $valuedateof['labeladddateof'];
8767 $tmparray = dol_getdate($tmpadddateof);
8768 if (empty($tmplabeladddateof)) {
8769 $tmplabeladddateof = $langs->trans("DateInvoice");
8770 }
8771 $reset_scripts = 'console.log(\'Click on now link\'); ';
8772 $reset_scripts .= 'jQuery(\'#'.$prefix.'\').val(\''.dol_print_date($tmpadddateof, 'dayinputnoreduce').'\');';
8773 $reset_scripts .= 'jQuery(\'#'.$prefix.'day\').val(\''.$tmparray['mday'].'\');';
8774 $reset_scripts .= 'jQuery(\'#'.$prefix.'month\').val(\''.$tmparray['mon'].'\');';
8775 $reset_scripts .= 'jQuery(\'#'.$prefix.'year\').val(\''.$tmparray['year'].'\');';
8776 $retstring .= ' - <button class="dpInvisibleButtons datenowlink" id="dateofinvoice" type="button" name="_dateofinvoice" value="now" onclick="'.$reset_scripts.'">'.$tmplabeladddateof.'</button>';
8777 }
8778 }
8779
8780 return $retstring;
8781 }
8782
8792 public function selectTypeDuration($prefix, $selected = 'i', $excludetypes = array(), $morecss = 'minwidth75 maxwidth100')
8793 {
8794 global $langs;
8795
8796 $TDurationTypes = $this->getDurationTypes($langs);
8797
8798 // Removed undesired duration types
8799 foreach ($excludetypes as $value) {
8800 unset($TDurationTypes[$value]);
8801 }
8802
8803 $retstring = '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $prefix . 'type_duration" name="' . $prefix . 'type_duration">';
8804 foreach ($TDurationTypes as $key => $typeduration) {
8805 $retstring .= '<option value="' . $key . '"';
8806 if ($key == $selected) {
8807 $retstring .= " selected";
8808 }
8809 $retstring .= ">" . $typeduration . "</option>";
8810 }
8811 $retstring .= "</select>";
8812
8813 $retstring .= ajax_combobox('select_' . $prefix . 'type_duration');
8814
8815 return $retstring;
8816 }
8817
8818 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8819
8833 public function select_duration($prefix, $iSecond = '', $disabled = 0, $typehour = 'select', $minunderhours = 0, $nooutput = 0)
8834 {
8835 // phpcs:enable
8836 global $langs;
8837
8838 $retstring = '<span class="nowraponall">';
8839
8840 $hourSelected = '';
8841 $minSelected = '';
8842
8843 // Hours
8844 if ($iSecond != '') {
8845 require_once DOL_DOCUMENT_ROOT . '/core/lib/date.lib.php';
8846
8847 $hourSelected = convertSecondToTime($iSecond, 'allhour');
8848 $minSelected = convertSecondToTime($iSecond, 'min');
8849 }
8850
8851 if ($typehour == 'select') {
8852 $retstring .= '<select class="flat" id="select_' . $prefix . 'hour" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . '>';
8853 for ($hour = 0; $hour < 25; $hour++) { // For a duration, we allow 24 hours
8854 $retstring .= '<option value="' . $hour . '"';
8855 if (is_numeric($hourSelected) && $hourSelected == $hour) {
8856 $retstring .= " selected";
8857 }
8858 $retstring .= ">" . $hour . "</option>";
8859 }
8860 $retstring .= "</select>";
8861 } elseif ($typehour == 'text' || $typehour == 'textselect') {
8862 $retstring .= '<input placeholder="' . $langs->trans('HourShort') . '" type="number" min="0" name="' . $prefix . 'hour"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputhour right" value="' . (($hourSelected != '') ? ((int) $hourSelected) : '') . '">';
8863 } else {
8864 return 'BadValueForParameterTypeHour';
8865 }
8866
8867 if ($typehour != 'text') {
8868 $retstring .= ' ' . $langs->trans('HourShort');
8869 } else {
8870 $retstring .= '<span class="">:</span>';
8871 }
8872
8873 // Minutes
8874 if ($minunderhours) {
8875 $retstring .= '<br>';
8876 } else {
8877 if ($typehour != 'text') {
8878 $retstring .= '<span class="hideonsmartphone">&nbsp;</span>';
8879 }
8880 }
8881
8882 if ($typehour == 'select' || $typehour == 'textselect') {
8883 $retstring .= '<select class="flat" id="select_' . $prefix . 'min" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . '>';
8884 $step = getDolGlobalInt('MAIN_DURATION_STEP');
8885 $duration_step = ($step > 0) ? $step : 5;
8886 for ($min = 0; $min <= 59; $min += $duration_step) {
8887 $retstring .= '<option value="' . $min . '"';
8888 if (is_numeric($minSelected) && $minSelected == $min) {
8889 $retstring .= ' selected';
8890 }
8891 $retstring .= '>' . $min . '</option>';
8892 }
8893 $retstring .= "</select>";
8894 } elseif ($typehour == 'text') {
8895 $retstring .= '<input placeholder="' . $langs->trans('MinuteShort') . '" type="number" min="0" name="' . $prefix . 'min"' . ($disabled ? ' disabled' : '') . ' class="flat maxwidth50 inputminute right" value="' . (($minSelected != '') ? ((int) $minSelected) : '') . '">';
8896 }
8897
8898 if ($typehour != 'text') {
8899 $retstring .= ' ' . $langs->trans('MinuteShort');
8900 }
8901
8902 $retstring .= "</span>";
8903
8904 if (!empty($nooutput)) {
8905 return $retstring;
8906 }
8907
8908 print $retstring;
8909
8910 return '';
8911 }
8912
8932 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)
8933 {
8934 global $langs, $conf;
8935
8936 $out = '';
8937
8938 // check parameters
8939 if (is_null($ajaxoptions)) {
8940 $ajaxoptions = array();
8941 }
8942
8943 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
8944 $placeholder = '';
8945
8946 if ($selected && empty($selected_input_value)) {
8947 require_once DOL_DOCUMENT_ROOT . '/ticket/class/ticket.class.php';
8948 $tickettmpselect = new Ticket($this->db);
8949 $tickettmpselect->fetch((int) $selected);
8950 $selected_input_value = $tickettmpselect->ref;
8951 unset($tickettmpselect);
8952 }
8953
8954 $urloption = '';
8955 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/ticket/ajax/tickets.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
8956
8957 if (empty($hidelabel)) {
8958 $out .= $langs->trans("RefOrLabel") . ' : ';
8959 } elseif ($hidelabel > 1) {
8960 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
8961 if ($hidelabel == 2) {
8962 $out .= img_picto($langs->trans("Search"), 'search');
8963 }
8964 }
8965 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
8966 if ($hidelabel == 3) {
8967 $out .= img_picto($langs->trans("Search"), 'search');
8968 }
8969 } else {
8970 $out .= $this->selectTicketsList($selected, $htmlname, $filtertype, $limit, '', $status, 0, $showempty, $forcecombo, $morecss);
8971 }
8972
8973 if (empty($nooutput)) {
8974 print $out;
8975 } else {
8976 return $out;
8977 }
8978 return '';
8979 }
8980
8981
8998 public function selectTicketsList($selected = '', $htmlname = 'ticketid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
8999 {
9000 global $langs;
9001
9002 $out = '';
9003 $outarray = array();
9004
9005 $selectFields = " p.rowid, p.ref, p.message";
9006
9007 $sql = "SELECT ";
9008 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9009 $sql .= " FROM " . $this->db->prefix() . "ticket as p";
9010 $sql .= ' WHERE p.entity IN (' . getEntity('ticket') . ')';
9011
9012 // Add criteria on ref/label
9013 if ($filterkey != '') {
9014 $sql .= ' AND (';
9015 $prefix = getDolGlobalString('TICKET_DONOTSEARCH_ANYWHERE') ? '' : '%'; // Can use index if TICKET_DONOTSEARCH_ANYWHERE is on
9016 // For natural search
9017 $search_crit = explode(' ', $filterkey);
9018 $i = 0;
9019 if (count($search_crit) > 1) {
9020 $sql .= "(";
9021 }
9022 foreach ($search_crit as $crit) {
9023 if ($i > 0) {
9024 $sql .= " AND ";
9025 }
9026 $sql .= "(p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%' OR p.subject LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9027 $sql .= ")";
9028 $i++;
9029 }
9030 if (count($search_crit) > 1) {
9031 $sql .= ")";
9032 }
9033 $sql .= ')';
9034 }
9035
9036 $sql .= $this->db->plimit($limit, 0);
9037
9038 // Build output string
9039 dol_syslog(get_class($this) . "::selectTicketsList search tickets", LOG_DEBUG);
9040 $result = $this->db->query($sql);
9041 if ($result) {
9042 require_once DOL_DOCUMENT_ROOT . '/ticket/class/ticket.class.php';
9043 require_once DOL_DOCUMENT_ROOT . '/core/lib/ticket.lib.php';
9044
9045 $num = $this->db->num_rows($result);
9046
9047 $events = array();
9048
9049 if (!$forcecombo) {
9050 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9051 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('TICKET_USE_SEARCH_TO_SELECT'));
9052 }
9053
9054 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9055
9056 $textifempty = '';
9057 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9058 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9059 if (getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9060 if ($showempty && !is_numeric($showempty)) {
9061 $textifempty = $langs->trans($showempty);
9062 } else {
9063 $textifempty .= $langs->trans("All");
9064 }
9065 } else {
9066 if ($showempty && !is_numeric($showempty)) {
9067 $textifempty = $langs->trans($showempty);
9068 }
9069 }
9070 if ($showempty) {
9071 $out .= '<option value="0" selected>' . $textifempty . '</option>';
9072 }
9073
9074 $i = 0;
9075 while ($num && $i < $num) {
9076 $opt = '';
9077 $optJson = array();
9078 $objp = $this->db->fetch_object($result);
9079
9080 $this->constructTicketListOption($objp, $opt, $optJson, $selected, $filterkey);
9081 '@phan-var-force array{key:string,value:mixed,type:int} $optJson';
9082 // Add new entry
9083 // "key" value of json key array is used by jQuery automatically as selected value
9084 // "label" value of json key array is used by jQuery automatically as text for combo box
9085 $out .= $opt;
9086 array_push($outarray, $optJson);
9087
9088 $i++;
9089 }
9090
9091 $out .= '</select>';
9092
9093 $this->db->free($result);
9094
9095 if (empty($outputmode)) {
9096 return $out;
9097 }
9098 return $outarray;
9099 } else {
9100 dol_print_error($this->db);
9101 }
9102
9103 return array();
9104 }
9105
9117 protected function constructTicketListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9118 {
9119 $outkey = '';
9120 $outref = '';
9121 $outtype = '';
9122
9123 $outkey = $objp->rowid;
9124 $outref = $objp->ref;
9125
9126 $opt = '<option value="' . $objp->rowid . '"';
9127 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9128 $opt .= '>';
9129 $opt .= $objp->ref;
9130 $objRef = $objp->ref;
9131 if (!empty($filterkey) && $filterkey != '') {
9132 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $objRef, 1);
9133 }
9134
9135 $opt .= "</option>\n";
9136 $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
9137 }
9138
9158 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)
9159 {
9160 global $langs, $conf;
9161
9162 $out = '';
9163
9164 // check parameters
9165 if (is_null($ajaxoptions)) {
9166 $ajaxoptions = array();
9167 }
9168
9169 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9170 $placeholder = '';
9171
9172 if ($selected && empty($selected_input_value)) {
9173 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
9174 $projecttmpselect = new Project($this->db);
9175 $projecttmpselect->fetch((int) $selected);
9176 $selected_input_value = $projecttmpselect->ref;
9177 unset($projecttmpselect);
9178 }
9179
9180 $urloption = '';
9181 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/projet/ajax/projects.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
9182
9183 if (empty($hidelabel)) {
9184 $out .= $langs->trans("RefOrLabel") . ' : ';
9185 } elseif ($hidelabel > 1) {
9186 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
9187 if ($hidelabel == 2) {
9188 $out .= img_picto($langs->trans("Search"), 'search');
9189 }
9190 }
9191 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
9192 if ($hidelabel == 3) {
9193 $out .= img_picto($langs->trans("Search"), 'search');
9194 }
9195 } else {
9196 $out .= $this->selectProjectsList($selected, $htmlname, $filtertype, $limit, '', $status, 0, $showempty, $forcecombo, $morecss);
9197 }
9198
9199 if (empty($nooutput)) {
9200 print $out;
9201 } else {
9202 return $out;
9203 }
9204 return '';
9205 }
9206
9223 public function selectProjectsList($selected = '', $htmlname = 'projectid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '')
9224 {
9225 global $langs, $conf;
9226
9227 $out = '';
9228 $outarray = array();
9229
9230 $selectFields = " p.rowid, p.ref";
9231
9232 $sql = "SELECT ";
9233 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9234 $sql .= " FROM " . $this->db->prefix() . "projet as p";
9235 $sql .= ' WHERE p.entity IN (' . getEntity('project') . ')';
9236
9237 // Add criteria on ref/label
9238 if ($filterkey != '') {
9239 $sql .= ' AND (';
9240 $prefix = !getDolGlobalString('TICKET_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
9241 // For natural search
9242 $search_crit = explode(' ', $filterkey);
9243 $i = 0;
9244 if (count($search_crit) > 1) {
9245 $sql .= "(";
9246 }
9247 foreach ($search_crit as $crit) {
9248 if ($i > 0) {
9249 $sql .= " AND ";
9250 }
9251 $sql .= "p.ref LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9252 $sql .= "";
9253 $i++;
9254 }
9255 if (count($search_crit) > 1) {
9256 $sql .= ")";
9257 }
9258 $sql .= ')';
9259 }
9260
9261 $sql .= $this->db->plimit($limit, 0);
9262
9263 // Build output string
9264 dol_syslog(get_class($this) . "::selectProjectsList search projects", LOG_DEBUG);
9265 $result = $this->db->query($sql);
9266 if ($result) {
9267 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
9268 require_once DOL_DOCUMENT_ROOT . '/core/lib/project.lib.php';
9269
9270 $num = $this->db->num_rows($result);
9271
9272 $events = array();
9273
9274 if (!$forcecombo) {
9275 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9276 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('PROJECT_USE_SEARCH_TO_SELECT'));
9277 }
9278
9279 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9280
9281 $textifempty = '';
9282 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9283 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9284 if (getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT')) {
9285 if ($showempty && !is_numeric($showempty)) {
9286 $textifempty = $langs->trans($showempty);
9287 } else {
9288 $textifempty .= $langs->trans("All");
9289 }
9290 } else {
9291 if ($showempty && !is_numeric($showempty)) {
9292 $textifempty = $langs->trans($showempty);
9293 }
9294 }
9295 if ($showempty) {
9296 $out .= '<option value="0" selected>' . $textifempty . '</option>';
9297 }
9298
9299 $i = 0;
9300 while ($num && $i < $num) {
9301 $opt = '';
9302 $optJson = array();
9303 $objp = $this->db->fetch_object($result);
9304
9305 $this->constructProjectListOption($objp, $opt, $optJson, $selected, $filterkey);
9306 // Add new entry
9307 // "key" value of json key array is used by jQuery automatically as selected value
9308 // "label" value of json key array is used by jQuery automatically as text for combo box
9309 $out .= $opt;
9310 array_push($outarray, $optJson);
9311
9312 $i++;
9313 }
9314
9315 $out .= '</select>';
9316
9317 $this->db->free($result);
9318
9319 if (empty($outputmode)) {
9320 return $out;
9321 }
9322 return $outarray;
9323 } else {
9324 dol_print_error($this->db);
9325 }
9326
9327 return array();
9328 }
9329
9343 protected function constructProjectListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9344 {
9345 $outkey = '';
9346 $outref = '';
9347 $outtype = '';
9348
9349 $label = $objp->label;
9350
9351 $outkey = $objp->rowid;
9352 $outref = $objp->ref;
9353 $outlabel = $objp->label;
9354 $outtype = $objp->fk_product_type;
9355
9356 $opt = '<option value="' . $objp->rowid . '"';
9357 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9358 $opt .= '>';
9359 $opt .= $objp->ref;
9360 $objRef = $objp->ref;
9361 if (!empty($filterkey) && $filterkey != '') {
9362 $objRef = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', (string) $objRef, 1);
9363 }
9364
9365 $opt .= "</option>\n";
9366 $optJson = array('key' => $outkey, 'value' => $outref, 'type' => $outtype);
9367 }
9368
9369
9390 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())
9391 {
9392 global $langs, $conf;
9393
9394 $out = '';
9395
9396 // check parameters
9397 if (is_null($ajaxoptions)) {
9398 $ajaxoptions = array();
9399 }
9400
9401 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('TICKET_USE_SEARCH_TO_SELECT')) {
9402 $placeholder = '';
9403
9404 if ($selected && empty($selected_input_value)) {
9405 require_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
9406 $adherenttmpselect = new Adherent($this->db);
9407 $adherenttmpselect->fetch((int) $selected);
9408 $selected_input_value = $adherenttmpselect->ref;
9409 unset($adherenttmpselect);
9410 }
9411
9412 $urloption = '';
9413
9414 $out .= ajax_autocompleter($selected, $htmlname, DOL_URL_ROOT . '/adherents/ajax/adherents.php', $urloption, $conf->global->PRODUIT_USE_SEARCH_TO_SELECT, 1, $ajaxoptions);
9415
9416 if (empty($hidelabel)) {
9417 $out .= $langs->trans("RefOrLabel") . ' : ';
9418 } elseif ($hidelabel > 1) {
9419 $placeholder = ' placeholder="' . $langs->trans("RefOrLabel") . '"';
9420 if ($hidelabel == 2) {
9421 $out .= img_picto($langs->trans("Search"), 'search');
9422 }
9423 }
9424 $out .= '<input type="text" class="minwidth100" name="search_' . $htmlname . '" id="search_' . $htmlname . '" value="' . $selected_input_value . '"' . $placeholder . ' ' . (getDolGlobalString('PRODUCT_SEARCH_AUTOFOCUS') ? 'autofocus' : '') . ' />';
9425 if ($hidelabel == 3) {
9426 $out .= img_picto($langs->trans("Search"), 'search');
9427 }
9428 } else {
9429 $filterkey = '';
9430
9431 $out .= $this->selectMembersList($selected, $htmlname, $filtertype, $limit, $filterkey, $status, 0, $showempty, $forcecombo, $morecss, $excludeids);
9432 }
9433
9434 if (empty($nooutput)) {
9435 print $out;
9436 } else {
9437 return $out;
9438 }
9439 return '';
9440 }
9441
9459 public function selectMembersList($selected = '', $htmlname = 'adherentid', $filtertype = '', $limit = 20, $filterkey = '', $status = 1, $outputmode = 0, $showempty = '1', $forcecombo = 0, $morecss = '', $excludeids = array())
9460 {
9461 global $langs, $conf;
9462
9463 $out = '';
9464 $outarray = array();
9465
9466 $selectFields = " p.rowid, p.ref, p.firstname, p.lastname, p.fk_adherent_type";
9467
9468 $sql = "SELECT ";
9469 $sql .= $this->db->sanitize($selectFields, 0, 0, 1);
9470 $sql .= " FROM " . $this->db->prefix() . "adherent as p";
9471 $sql .= ' WHERE p.entity IN (' . getEntity('adherent') . ')';
9472
9473 // Add criteria on ref/label
9474 if ($filterkey != '') {
9475 $sql .= ' AND (';
9476 $prefix = !getDolGlobalString('MEMBER_DONOTSEARCH_ANYWHERE') ? '%' : ''; // Can use index if PRODUCT_DONOTSEARCH_ANYWHERE is on
9477 // For natural search
9478 $search_crit = explode(' ', $filterkey);
9479 $i = 0;
9480 if (count($search_crit) > 1) {
9481 $sql .= "(";
9482 }
9483 foreach ($search_crit as $crit) {
9484 if ($i > 0) {
9485 $sql .= " AND ";
9486 }
9487 $sql .= "(p.firstname LIKE '" . $this->db->escape($prefix . $crit) . "%'";
9488 $sql .= " OR p.lastname LIKE '" . $this->db->escape($prefix . $crit) . "%')";
9489 $i++;
9490 }
9491 if (count($search_crit) > 1) {
9492 $sql .= ")";
9493 }
9494 $sql .= ')';
9495 }
9496 if ($status != -1) {
9497 $sql .= ' AND statut = ' . ((int) $status);
9498 }
9499 if (!empty($excludeids)) {
9500 $sql .= " AND p.rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeids)) . ")";
9501 }
9502 $sql .= $this->db->plimit($limit, 0);
9503
9504 // Build output string
9505 dol_syslog(get_class($this) . "::selectMembersList search adherents", LOG_DEBUG);
9506 $result = $this->db->query($sql);
9507 if ($result) {
9508 require_once DOL_DOCUMENT_ROOT . '/adherents/class/adherent.class.php';
9509 require_once DOL_DOCUMENT_ROOT . '/core/lib/member.lib.php';
9510
9511 $num = $this->db->num_rows($result);
9512
9513 $events = array();
9514
9515 if (!$forcecombo) {
9516 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
9517 $out .= ajax_combobox($htmlname, $events, getDolGlobalInt('PROJECT_USE_SEARCH_TO_SELECT'));
9518 }
9519
9520 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '" id="' . $htmlname . '">';
9521
9522 $textifempty = '';
9523 // Do not use textifempty = ' ' or '&nbsp;' here, or search on key will search on ' key'.
9524 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9525 if (getDolGlobalString('PROJECT_USE_SEARCH_TO_SELECT')) {
9526 if ($showempty && !is_numeric($showempty)) {
9527 $textifempty = $langs->trans($showempty);
9528 } else {
9529 $textifempty .= $langs->trans("All");
9530 }
9531 } else {
9532 if ($showempty && !is_numeric($showempty)) {
9533 $textifempty = $langs->trans($showempty);
9534 }
9535 }
9536 if ($showempty) {
9537 $out .= '<option value="-1" selected>' . $textifempty . '</option>';
9538 }
9539
9540 $i = 0;
9541 while ($num && $i < $num) {
9542 $opt = '';
9543 $optJson = array();
9544 $objp = $this->db->fetch_object($result);
9545
9546 $this->constructMemberListOption($objp, $opt, $optJson, $selected, $filterkey);
9547
9548 // Add new entry
9549 // "key" value of json key array is used by jQuery automatically as selected value
9550 // "label" value of json key array is used by jQuery automatically as text for combo box
9551 $out .= $opt;
9552 array_push($outarray, $optJson);
9553
9554 $i++;
9555 }
9556
9557 $out .= '</select>';
9558
9559 $this->db->free($result);
9560
9561 if (empty($outputmode)) {
9562 return $out;
9563 }
9564 return $outarray;
9565 } else {
9566 dol_print_error($this->db);
9567 }
9568
9569 return array();
9570 }
9571
9583 protected function constructMemberListOption(&$objp, &$opt, &$optJson, $selected, $filterkey = '')
9584 {
9585 $outkey = '';
9586 $outlabel = '';
9587 $outtype = '';
9588
9589 $outkey = $objp->rowid;
9590 $outlabel = dolGetFirstLastname($objp->firstname, $objp->lastname);
9591 $outtype = $objp->fk_adherent_type;
9592
9593 $opt = '<option value="' . $objp->rowid . '"';
9594 $opt .= ($objp->rowid == $selected) ? ' selected' : '';
9595 $opt .= '>';
9596 if (!empty($filterkey) && $filterkey != '') {
9597 $outlabel = preg_replace('/(' . preg_quote($filterkey, '/') . ')/i', '<strong>$1</strong>', $outlabel, 1);
9598 }
9599 $opt .= $outlabel;
9600 $opt .= "</option>\n";
9601
9602 $optJson = array('key' => $outkey, 'value' => $outlabel, 'type' => $outtype);
9603 }
9604
9626 public function selectForForms($objectdesc, $htmlname, $preSelectedValue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $disabled = 0, $selected_input_value = '', $objectfield = '')
9627 {
9628 global $conf, $extrafields, $user, $hookmanager, $action;
9629
9630 // Example of common usage for a link to a thirdparty
9631
9632 // We got this in a modulebuilder form of "MyObject" of module "mymodule".
9633 // 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
9634 // $objectdesc = 'Societe'
9635 // $objectfield = Method 1: 'myobject@mymodule:fk_soc' ('fk_soc' is code to retrieve myobject->fields['fk_soc'])
9636 // 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__))" ...)
9637
9638 // We got this when showing an extrafields on resource that is a link to societe
9639 // 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
9640 // $objectdesc = 'Societe'
9641 // $objectfield = Method 1: 'resource:options_link_to_societe'
9642 // Method 2 recommended (it can be the array): array("type"=>'Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))" ...)
9643
9644 // With old usage:
9645 // $objectdesc = 'Societe:societe/class/societe.class.php:1:((status:=:1) AND (entity:IN:__SHARED_ENTITIES__))'
9646 // $objectfield = ''
9647
9648 //var_dump($objectdesc.' '.$objectfield);
9649 //debug_print_backtrace();
9650
9651 $objectdescorig = $objectdesc;
9652 $objecttmp = null;
9653 $InfoFieldList = array();
9654 $classname = '';
9655 $filter = ''; // Ensure filter has value (for static analysis)
9656 $sortfield = ''; // Ensure filter has value (for static analysis)
9657
9658 if (is_array($objectfield)) { // objectfield is an array
9659 $objectdesc = $objectfield['type'];
9660 $objectdesc = preg_replace('/^integer[^:]*:/', '', $objectdesc);
9661 } 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.
9662 // Example: $objectfield = 'product:options_package' or 'myobject@mymodule:options_myfield'
9663 $tmparray = explode(':', $objectfield);
9664
9665 // Get instance of object from $element
9666 $objectforfieldstmp = fetchObjectByElement(0, strtolower($tmparray[0]));
9667
9668 if (is_object($objectforfieldstmp)) {
9669 $objectdesc = '';
9670
9671 $reg = array();
9672 if (preg_match('/^options_(.*)$/', $tmparray[1], $reg)) {
9673 // For a property in extrafields
9674 $key = $reg[1];
9675 // fetch optionals attributes and labels
9676 $extrafields->fetch_name_optionals_label($objectforfieldstmp->table_element);
9677
9678 if (!empty($extrafields->attributes[$objectforfieldstmp->table_element]['type'][$key]) && $extrafields->attributes[$objectforfieldstmp->table_element]['type'][$key] == 'link') {
9679 if (!empty($extrafields->attributes[$objectforfieldstmp->table_element]['param'][$key]['options'])) {
9680 $tmpextrafields = array_keys($extrafields->attributes[$objectforfieldstmp->table_element]['param'][$key]['options']);
9681 $objectdesc = $tmpextrafields[0];
9682 }
9683 }
9684 } else {
9685 // For a property in ->fields
9686 if (array_key_exists($tmparray[1], $objectforfieldstmp->fields)) {
9687 $objectdesc = $objectforfieldstmp->fields[$tmparray[1]]['type'];
9688 $objectdesc = preg_replace('/^integer[^:]*:/', '', $objectdesc);
9689 }
9690 }
9691 }
9692 }
9693
9694 if ($objectdesc) {
9695 // Example of value for $objectdesc:
9696 // Bom:bom/class/bom.class.php:0:t.status=1
9697 // Bom:bom/class/bom.class.php:0:t.status=1:ref
9698 // Bom:bom/class/bom.class.php:0:(t.status:=:1) OR (t.field2:=:2):ref
9699 $InfoFieldList = explode(":", $objectdesc, 4);
9700 $vartmp = (empty($InfoFieldList[3]) ? '' : $InfoFieldList[3]);
9701 $reg = array();
9702 if (preg_match('/^.*:(\w*)$/', $vartmp, $reg)) {
9703 $InfoFieldList[4] = $reg[1]; // take the sort field
9704 }
9705 $InfoFieldList[3] = preg_replace('/:\w*$/', '', $vartmp); // take the filter field
9706
9707 $classname = $InfoFieldList[0];
9708 $classpath = empty($InfoFieldList[1]) ? '' : $InfoFieldList[1];
9709 //$addcreatebuttonornot = empty($InfoFieldList[2]) ? 0 : $InfoFieldList[2];
9710 $filter = empty($InfoFieldList[3]) ? '' : $InfoFieldList[3];
9711 $sortfield = empty($InfoFieldList[4]) ? '' : $InfoFieldList[4];
9712
9713 // Load object according to $id and $element
9714 $objecttmp = fetchObjectByElement(0, strtolower($InfoFieldList[0]));
9715
9716 // Fallback to another solution to get $objecttmp
9717 if (empty($objecttmp) && !empty($classpath)) {
9718 dol_include_once($classpath);
9719
9720 if ($classname && class_exists($classname)) {
9721 $objecttmp = new $classname($this->db);
9722 }
9723 }
9724 }
9725
9726 // Make some replacement in $filter. May not be used if we used the ajax mode with $objectfield. In such a case
9727 // we propagate the $objectfield and not the filter and replacement is done by the ajax/selectobject.php component.
9728 $sharedentities = (is_object($objecttmp) && property_exists($objecttmp, 'element')) ? getEntity($objecttmp->element) : strtolower($classname);
9729 $filter = str_replace(
9730 array('__ENTITY__', '__SHARED_ENTITIES__', '__USER_ID__'),
9731 array($conf->entity, $sharedentities, $user->id),
9732 $filter
9733 );
9734
9735 if (!is_object($objecttmp)) {
9736 dol_syslog('selectForForms: Error bad setup of field objectdescorig=' . $objectdescorig.', objectfield='.(is_array($objectfield) ? 'array' : $objectfield).', objectdesc='.$objectdesc, LOG_WARNING);
9737 return 'selectForForms: Error bad setup of field objectdescorig=' . $objectdescorig.', objectfield='.(is_array($objectfield) ? 'array' : $objectfield).', objectdesc='.$objectdesc;
9738 }
9739 '@phan-var-force CommonObject $objecttmp';
9741 //var_dump($filter);
9742 $prefixforautocompletemode = $objecttmp->element;
9743 if ($prefixforautocompletemode == 'societe') {
9744 $prefixforautocompletemode = 'company';
9745 }
9746 if ($prefixforautocompletemode == 'product') {
9747 $prefixforautocompletemode = 'produit';
9748 }
9749
9750 $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
9751
9752 dol_syslog(get_class($this) . "::selectForForms filter=" . $filter, LOG_DEBUG);
9753
9754 // Generate the combo HTML component
9755 $out = '';
9756 if (!empty($conf->use_javascript_ajax) && getDolGlobalString($confkeyforautocompletemode) && !$forcecombo) {
9757 // No immediate load of all database
9758 $placeholder = '';
9759
9760 if ($preSelectedValue && empty($selected_input_value)) {
9761 $objecttmp->fetch($preSelectedValue);
9762 $selected_input_value = ($prefixforautocompletemode == 'company' ? $objecttmp->name : $objecttmp->ref);
9763
9764 $oldValueForShowOnCombobox = 0;
9765 foreach ($objecttmp->fields as $fieldK => $fielV) {
9766 if (!array_key_exists('showoncombobox', $fielV) || !$fielV['showoncombobox'] || empty($objecttmp->$fieldK)) {
9767 continue;
9768 }
9769
9770 if (!$oldValueForShowOnCombobox) {
9771 $selected_input_value = '';
9772 }
9773
9774 $selected_input_value .= $oldValueForShowOnCombobox ? ' - ' : '';
9775 $selected_input_value .= $objecttmp->$fieldK;
9776 $oldValueForShowOnCombobox = empty($fielV['showoncombobox']) ? 0 : $fielV['showoncombobox'];
9777 }
9778 }
9779
9780 // Set url and param to call to get json of the search results
9781 $urlforajaxcall = DOL_URL_ROOT . '/core/ajax/selectobject.php';
9782 $urloption = 'htmlname=' . urlencode($htmlname) . '&outjson=1&objectdesc=' . urlencode($objectdescorig) . (is_scalar($objectfield) ? '&objectfield='.urlencode($objectfield) : '') . ($sortfield ? '&sortfield=' . urlencode($sortfield) : '');
9783 //$urloption = 'htmlname=' . urlencode($htmlname) . '&outjson=1'.(is_scalar($objectfield) ? '&objectfield='.urlencode($objectfield) : '') . ($sortfield ? '&sortfield=' . urlencode($sortfield) : '');
9784
9785 // Hook 'selectForFormsListUrl' - Added to allow modules to modify the AJAX URL
9786 $parameters = array(
9787 'urloption' => $urloption,
9788 'object' => $objecttmp,
9789 'htmlname' => $htmlname,
9790 'filter' => $filter,
9791 'searchkey' => $searchkey,
9792 );
9793 $reshook = $hookmanager->executeHooks('selectForFormsListUrl', $parameters, $objecttmp, $action);
9794 if (!empty($reshook)) {
9795 $urloption = $hookmanager->resPrint;
9796 $hookmanager->resPrint = '';
9797 }
9798
9799 // Activate the auto complete using ajax call.
9800 $out .= ajax_autocompleter((string) $preSelectedValue, $htmlname, $urlforajaxcall, $urloption, getDolGlobalInt($confkeyforautocompletemode), 0);
9801 $out .= '<!-- force css to be higher than dialog popup --><style type="text/css">.ui-autocomplete { z-index: 1010; }</style>';
9802 $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) . '"' : '') . ' />';
9803 } else {
9804 // Immediate load of table record.
9805 $out .= $this->selectForFormsList($objecttmp, $htmlname, $preSelectedValue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, 0, $disabled, $sortfield, $filter);
9806 }
9807
9808 return $out;
9809 }
9810
9811
9833 public function selectForFormsList($objecttmp, $htmlname, $preselectedvalue, $showempty = '', $searchkey = '', $placeholder = '', $morecss = '', $moreparams = '', $forcecombo = 0, $outputmode = 0, $disabled = 0, $sortfield = '', $filter = '', $sortorder = 'ASC')
9834 {
9835 global $langs, $user, $hookmanager;
9836
9837 //print "$htmlname, $preselectedvalue, $showempty, $searchkey, $placeholder, $morecss, $moreparams, $forcecombo, $outputmode, $disabled";
9838
9839 $prefixforautocompletemode = $objecttmp->element;
9840 if ($prefixforautocompletemode == 'societe') {
9841 $prefixforautocompletemode = 'company';
9842 }
9843 $confkeyforautocompletemode = strtoupper($prefixforautocompletemode) . '_USE_SEARCH_TO_SELECT'; // For example COMPANY_USE_SEARCH_TO_SELECT
9844
9845 $fieldstoshow = '';
9846 if (!empty($objecttmp->fields)) { // For object that declare it, it is better to use declared fields (like societe, contact, ...)
9847 $tmpfieldstoshow = '';
9848 foreach ($objecttmp->fields as $key => $val) {
9849 if (! (int) dol_eval((string) $val['enabled'], 1, 1, '1')) {
9850 continue;
9851 }
9852 if (!empty($val['showoncombobox'])) {
9853 $tmpfieldstoshow .= ($tmpfieldstoshow ? ',' : '') . 't.' . $key;
9854 }
9855 }
9856 if ($tmpfieldstoshow) {
9857 $fieldstoshow = $tmpfieldstoshow;
9858 }
9859 } elseif ($objecttmp->element === 'category') {
9860 $fieldstoshow = 't.label';
9861 } else {
9862 // For backward compatibility
9863 $objecttmp->fields['ref'] = array('type' => 'varchar(30)', 'label' => 'Ref', 'enabled' => 1, 'position' => 10, 'visible' => 4, 'showoncombobox' => 1);
9864 }
9865
9866 if (empty($fieldstoshow)) {
9867 if (!empty($objecttmp->parent_element)) {
9868 $fieldstoshow = 'o.ref';
9869 if (empty($sortfield)) {
9870 $sortfield = 'o.ref';
9871 }
9872 if (in_array($objecttmp->element, ['commandedet', 'propaldet', 'facturedet', 'expeditiondet'])) {
9873 $fieldstoshow .= ',p.ref AS p_ref,p.label,t.description';
9874 $sortfield .= ', p.ref';
9875 }
9876 } elseif (isset($objecttmp->fields['ref'])) {
9877 $fieldstoshow = 't.ref';
9878 } else {
9879 $langs->load("errors");
9880 $this->error = $langs->trans("ErrorNoFieldWithAttributeShowoncombobox");
9881 return $langs->trans('ErrorNoFieldWithAttributeShowoncombobox');
9882 }
9883 }
9884
9885 $out = '';
9886 $outarray = array();
9887 $tmparray = array();
9888
9889 $num = 0;
9890
9891 $sanitizedfieldstoshow = $fieldstoshow;
9892
9893 // Search data
9894 $sql = "SELECT t.rowid, " . $sanitizedfieldstoshow . " FROM " . $this->db->prefix() . $this->db->sanitize($objecttmp->table_element) . " as t";
9895 if (!empty($objecttmp->isextrafieldmanaged)) {
9896 $extrafieldTable = $objecttmp->table_element;
9897 if ($extrafieldTable == 'categorie') {
9898 $extrafieldTable = 'categories'; // For compatibility
9899 }
9900 $sql .= " LEFT JOIN " . $this->db->prefix() . $this->db->sanitize($extrafieldTable) . "_extrafields as e ON t.rowid = e.fk_object";
9901 }
9902 if (!empty($objecttmp->parent_element)) { // If parent_element is defined
9903 '@phan-var-force CommonObjectLine $objecttmp';
9904 $parent_properties = getElementProperties($objecttmp->parent_element);
9905 // @phan-suppress-next-line SqlInjection
9906 $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);
9907 }
9908 if (!empty($objecttmp->parent_element) && in_array($objecttmp->parent_element, ['commande', 'propal', 'facture', 'expedition'])) {
9909 $sql .= " LEFT JOIN " . $this->db->prefix() . "product as p ON p.rowid = t.fk_product";
9910 }
9911 if (!empty($objecttmp->ismultientitymanaged)) {
9912 if ($objecttmp->ismultientitymanaged == 1) { // @phan-suppress-current-line PhanPluginEmptyStatementIf
9913 // No need to join/link another table
9914 }
9915 if (!is_numeric($objecttmp->ismultientitymanaged)) {
9916 $tmparray = explode('@', $objecttmp->ismultientitymanaged);
9917 $sql .= " INNER JOIN " . $this->db->prefix() . $this->db->sanitize($tmparray[1]) . " as parenttable ON parenttable.rowid = t." . $this->db->sanitize($tmparray[0]);
9918 }
9919 }
9920
9921 // Add where from hooks
9922 $parameters = array(
9923 'object' => $objecttmp,
9924 'htmlname' => $htmlname,
9925 'filter' => $filter,
9926 'searchkey' => $searchkey
9927 );
9928
9929 $reshook = $hookmanager->executeHooks('selectForFormsListWhere', $parameters); // Note that $action and $object may have been modified by hook
9930 if (!empty($hookmanager->resPrint)) {
9931 $sql .= $hookmanager->resPrint;
9932 } else {
9933 $sql .= " WHERE 1=1";
9934
9935 // If table need a multientity restriction
9936 if (!empty($objecttmp->ismultientitymanaged)) {
9937 if ($objecttmp->ismultientitymanaged == 1) {
9938 $sql .= " AND t.entity IN (" . getEntity($objecttmp->element) . ")";
9939 }
9940 if (!is_numeric($objecttmp->ismultientitymanaged)) {
9941 $sql .= " AND parenttable.entity = t." . $this->db->sanitize($tmparray[0]);
9942 }
9943 // If the parent table is llx_societe and user is not an external user (a more robust test done later for external users),
9944 // then we must also check that user has permissions
9945 if ($objecttmp->ismultientitymanaged === 'fk_soc@societe') {
9946 if (!$user->hasRight('societe', 'client', 'voir') && empty($user->socid)) {
9947 $sql .= " AND EXISTS (SELECT sc.rowid FROM ".$this->db->prefix() . "societe_commerciaux as sc";
9948 $sql .= " WHERE sc.fk_soc = t.fk_soc AND sc.fk_user = ".((int) $user->id).")";
9949 }
9950 }
9951 }
9952
9953 // If user is external user, we must also make a test on thirdparty
9954 if (!empty($user->socid)) {
9955 if ($objecttmp->element == 'societe') {
9956 $sql .= " AND t.rowid = " . ((int) $user->socid);
9957 } elseif (!empty($objecttmp->fields['fk_soc']) || !empty($objecttmp->fields['t.fk_soc']) || property_exists($objecttmp, 'fk_soc') || property_exists($objecttmp, 'socid')) {
9958 $sql .= " AND t.fk_soc = " . ((int) $user->socid);
9959 } elseif (!empty($objecttmp->parent_element)) {
9960 $tmpparent = fetchObjectByElement(0, $objecttmp->parent_element, '', 1);
9961 if (is_object($tmpparent) && (!empty($tmpparent->fields['fk_soc']) || !empty($tmpparent->fields['t.fk_soc']) || property_exists($tmpparent, 'fk_soc') || property_exists($tmpparent, 'socid'))) {
9962 $sql .= " AND o.fk_soc = " . ((int) $user->socid);
9963 }
9964 }
9965 }
9966
9967 $splittedfieldstoshow = explode(',', $fieldstoshow);
9968 foreach ($splittedfieldstoshow as &$field2) {
9969 if (is_numeric($pos = strpos($field2, ' '))) {
9970 $field2 = substr($field2, 0, $pos);
9971 }
9972 }
9973 if ($searchkey != '') {
9974 $sql .= natural_search($splittedfieldstoshow, $searchkey);
9975 }
9976
9977 if ($filter) { // Syntax example "(t.ref:like:'SO-%') and (t.date_creation:>:'20160101')"
9978 $errormessage = '';
9979 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
9980 if ($errormessage) {
9981 return 'Error forging a SQL request from an universal criteria: ' . $errormessage;
9982 }
9983 }
9984 }
9985 $sql .= $this->db->order($sortfield ? $sortfield : $fieldstoshow, $sortorder);
9986 //$sql.=$this->db->plimit($limit, 0);
9987 //print $sql;
9988
9989 // Build output string
9990 $resql = $this->db->query($sql);
9991 if ($resql) {
9992 // Construct $out and $outarray
9993 $out .= '<select id="' . $htmlname . '" class="flat minwidth100' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ($moreparams ? ' ' . $moreparams : '') . ' name="' . $htmlname . '">' . "\n";
9994
9995 // 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
9996 $textifempty = '&nbsp;';
9997
9998 //if (!empty($conf->use_javascript_ajax) || $forcecombo) $textifempty='';
9999 if (getDolGlobalInt($confkeyforautocompletemode)) {
10000 if ($showempty && !is_numeric($showempty)) {
10001 $textifempty = $langs->trans($showempty);
10002 } else {
10003 $textifempty .= $langs->trans("All");
10004 }
10005 }
10006 if ($showempty) {
10007 $out .= '<option value="-1">' . $textifempty . '</option>' . "\n";
10008 }
10009
10010 $num = $this->db->num_rows($resql);
10011 $i = 0;
10012 if ($num) {
10013 while ($i < $num) {
10014 $obj = $this->db->fetch_object($resql);
10015 $label = '';
10016 $labelhtml = '';
10017 $tmparray = explode(',', $fieldstoshow);
10018 $oldvalueforshowoncombobox = 0;
10019 foreach ($tmparray as $key => $val) {
10020 $val = preg_replace('/(t|p|o)\./', '', $val);
10021 $label .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
10022 $labelhtml .= (($label && $obj->$val) ? ($oldvalueforshowoncombobox != $objecttmp->fields[$val]['showoncombobox'] ? ' - ' : ' ') : '');
10023 $label .= $obj->$val;
10024 $labelhtml .= $obj->$val;
10025
10026 $oldvalueforshowoncombobox = empty($objecttmp->fields[$val]['showoncombobox']) ? 0 : $objecttmp->fields[$val]['showoncombobox'];
10027 }
10028 if (empty($outputmode)) {
10029 if ($preselectedvalue > 0 && $preselectedvalue == $obj->rowid) {
10030 $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>';
10031 } else {
10032 $out .= '<option value="' . $obj->rowid . '" data-html="' . dol_escape_htmltag($labelhtml, 0, 0, '', 0, 1) . '">' . dol_escape_htmltag($label, 0, 0, '', 0, 1) . '</option>';
10033 }
10034 } else {
10035 array_push($outarray, array('key' => $obj->rowid, 'value' => $label, 'label' => $label));
10036 }
10037
10038 $i++;
10039 if (($i % 10) == 0) {
10040 $out .= "\n";
10041 }
10042 }
10043 }
10044
10045 $out .= '</select>' . "\n";
10046
10047 if (!$forcecombo) {
10048 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10049 $out .= ajax_combobox($htmlname, array(), getDolGlobalInt($confkeyforautocompletemode, 0));
10050 }
10051 } else {
10052 dol_print_error($this->db);
10053 }
10054
10055 $this->result = array('nbofelement' => $num);
10056
10057 if ($outputmode) {
10058 return $outarray;
10059 }
10060 return $out;
10061 }
10062
10073 public static function radio($htmlName, $radioItems, $selected = '', $moreGlobalParams = [])
10074 {
10075 // Default parameters for each radio input
10076 $defaultParams = [
10077 'disabled' => false,
10078 'attr' => [
10079 'type' => 'radio',
10080 'name' => $htmlName,
10081 ],
10082 'attrLabel' => [],
10083 'labelIsHtml' => false
10084 ];
10085
10086 // Merge global parameters with defaults
10087 $params = array_merge_recursive_distinct($defaultParams, $moreGlobalParams);
10088
10089 $out = '';
10090 if (!empty($radioItems)) {
10091 foreach ($radioItems as $key => $item) {
10092 // Normalize item to array structure if it's a simple string
10093 if (!is_array($item)) {
10094 $item = [
10095 'attr' => [
10096 'value' => $key,
10097 ],
10098 'label' => $item
10099 ];
10100 }
10101
10102 // Default properties for individual item
10103 $defaultItem = [
10104 'attr' => [
10105 'value' => !isset($item['attr']['value']) ? $key : '',
10106 ],
10107 'label' => '',
10108 ];
10109
10110 // Merge defaults with global params and item-specific properties
10111 $defaultItem = array_merge_recursive_distinct($params, $defaultItem);
10112 $item = array_merge_recursive_distinct($defaultItem, $item);
10113
10114 // Determine if this radio should be checked
10115 if ((is_array($selected) && in_array($item['attr']['value'], $selected, true)) || $selected === $item['attr']['value']) {
10116 $item['attr']['checked'] = true;
10117 }
10118
10119 // Build HTML attributes for input and label
10120 $inputAttributes = implode(' ', commonHtmlAttributeBuilder($item['attr']));
10121 $labelAttributes = implode(' ', commonHtmlAttributeBuilder($item['attrLabel']));
10122
10123 // prevent accidental Xss todo : escape $item['label'] but html friendly compatible
10124 $text = $item['labelIsHtml'] ? $item['label'] : htmlspecialchars($item['label'], ENT_QUOTES | ENT_SUBSTITUTE);
10125
10126 // Generate HTML
10127 $out .= '<label ' . $labelAttributes . '><input ' . $inputAttributes . ' /> ' . $text . '</label> ';
10128 }
10129 }
10130
10131 return $out;
10132 }
10133
10134
10158 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)
10159 {
10160 global $conf, $langs;
10161
10162 // Do we want a multiselect ?
10163 //$jsbeautify = 0;
10164 //if (preg_match('/^multi/',$htmlname)) $jsbeautify = 1;
10165 $jsbeautify = 1;
10166
10167 if ($value_as_key) {
10168 $array = array_combine($array, $array);
10169 }
10170
10171 '@phan-var-force array{label:string,data-html:string,disable?:int<0,1>,css?:string} $array'; // Array combine breaks information
10172
10173 $out = '';
10174
10175 if ($addjscombo < 0) {
10176 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10177 $addjscombo = 1;
10178 } else {
10179 $addjscombo = 0;
10180 }
10181 }
10182 $idname = str_replace(array('[', ']'), array('', ''), $htmlname);
10183 $out .= '<select id="' . preg_replace('/^\./', '', $idname) . '" ' . ($disabled ? 'disabled="disabled" ' : '') . 'class="flat ' . (preg_replace('/^\./', '', $htmlname)) . ($morecss ? ' ' . $morecss : '') . ' selectformat"';
10184 $out .= ' name="' . preg_replace('/^\./', '', $htmlname) . '" ' . ($moreparam ? $moreparam : '');
10185 $out .= '>'."\n";
10186
10187 if ($show_empty) {
10188 $textforempty = ' ';
10189 if (!empty($conf->use_javascript_ajax)) {
10190 $textforempty = '&nbsp;'; // If we use ajaxcombo, we need &nbsp; here to avoid to have an empty element that is too small.
10191 }
10192 if (!is_numeric($show_empty)) {
10193 $textforempty = $show_empty;
10194 }
10195 $out .= '<option class="optiongrey" ' . ($moreparamonempty ? $moreparamonempty . ' ' : '') . 'value="' . (((int) $show_empty) < 0 ? $show_empty : -1) . '"' . ($id == $show_empty ? ' selected' : '') . '>' . $textforempty . '</option>' . "\n";
10196 }
10197 if (is_array($array)) {
10198 // Translate
10199 if ($translate) {
10200 foreach ($array as $key => $value) {
10201 if (!is_array($value)) {
10202 $array[$key] = $langs->trans($value);
10203 } else {
10204 $array[$key]['label'] = $langs->trans($value['label']);
10205 }
10206 }
10207 }
10208 // Sort
10209 if ($sort == 'ASC') {
10210 asort($array);
10211 } elseif ($sort == 'DESC') {
10212 arsort($array);
10213 }
10214
10215 foreach ($array as $key => $tmpvalue) {
10216 if (is_array($tmpvalue)) {
10217 $value = $tmpvalue['label'];
10218 //$valuehtml = empty($tmpvalue['data-html']) ? $value : $tmpvalue['data-html'];
10219 $disabled = empty($tmpvalue['disabled']) ? '' : ' disabled';
10220 $style = empty($tmpvalue['css']) ? '' : ' class="' . $tmpvalue['css'] . '"';
10221 } else {
10222 $value = $tmpvalue;
10223 //$valuehtml = $tmpvalue;
10224 $disabled = '';
10225 $style = '';
10226 }
10227 if (!empty($disablebademail)) {
10228 if (($disablebademail == 1 && !preg_match('/&lt;.+@.+&gt;/', $value))
10229 || ($disablebademail == 2 && preg_match('/---/', $value))) {
10230 $disabled = ' disabled';
10231 $style = ' class="warning"';
10232 }
10233 }
10234 if ($key_in_label) {
10235 if (empty($nohtmlescape)) {
10236 $selectOptionValue = dol_escape_htmltag($key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value));
10237 } else {
10238 $selectOptionValue = $key . ' - ' . ($maxlen ? dol_trunc($value, $maxlen) : $value);
10239 }
10240 } else {
10241 if (empty($nohtmlescape)) {
10242 $selectOptionValue = dol_escape_htmltag($maxlen ? dol_trunc($value, $maxlen) : $value);
10243 } else {
10244 $selectOptionValue = $maxlen ? dol_trunc($value, $maxlen) : $value;
10245 }
10246 if ($value == '' || $value == '-') {
10247 $selectOptionValue = '&nbsp;';
10248 }
10249 }
10250 $out .= '<option value="' . $key . '"';
10251 $out .= $style . $disabled;
10252 $out .= is_array($tmpvalue) && !empty($tmpvalue['parent']) ? ' parent="' . dolPrintHTMLForAttribute($tmpvalue['parent']) . '"' : '';
10253 if (is_array($id)) {
10254 if (in_array($key, $id) && !$disabled) {
10255 $out .= ' selected'; // To preselect a value
10256 }
10257 } else {
10258 $id = (string) $id; // if $id = 0, then $id = '0'
10259 if ($id != '' && (($id == (string) $key) || ($id == 'ifone' && count($array) == 1)) && !$disabled) {
10260 $out .= ' selected'; // To preselect a value
10261 }
10262 }
10263
10264 if (is_array($tmpvalue)) {
10265 foreach ($tmpvalue as $keyforvalue => $valueforvalue) {
10266 if ($keyforvalue == 'labelhtml') {
10267 $keyforvalue = 'data-html';
10268 }
10269 if (preg_match('/^data-/', $keyforvalue)) { // The best solution if you want to use HTML values into the list is to use data-html.
10270 $out .= ' '.dol_escape_htmltag($keyforvalue).'="'.dol_escape_htmltag($valueforvalue).'"';
10271 }
10272 }
10273 } elseif (!empty($nohtmlescape)) { // deprecated. Use instead the previous cas, an array with 'data-html', 'data-xxx' ... to use HTML content in the select
10274 $out .= ' data-html="' . dol_escape_htmltag($selectOptionValue) . '"';
10275 }
10276
10277 $out .= '>';
10278 $out .= $selectOptionValue;
10279 $out .= "</option>\n";
10280 }
10281 }
10282 $out .= "</select>";
10283
10284 // Add code for jquery to use multiselect
10285 if ($addjscombo && $jsbeautify) {
10286 // Enhance with select2
10287 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
10288 $out .= ajax_combobox($idname, array(), 0, 0, 'resolve', (((int) $show_empty) < 0 ? (string) $show_empty : '-1'), $morecss);
10289 }
10290
10291 return $out;
10292 }
10293
10312 public static function selectArrayAjax($htmlname, $url, $id = '', $moreparam = '', $moreparamtourl = '', $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0)
10313 {
10314 global $conf;
10315 global $delayedhtmlcontent; // Will be used later outside of this function
10316
10317 // TODO Use an internal dolibarr component instead of select2
10318 if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
10319 return '';
10320 }
10321
10322 $out = '<select type="text" class="' . $htmlname . ($morecss ? ' ' . $morecss : '') . '" ' . ($moreparam ? $moreparam . ' ' : '') . 'name="' . $htmlname . '"></select>';
10323
10324 $outdelayed = '';
10325 if (!empty($conf->use_javascript_ajax)) {
10326 $tmpplugin = 'select2';
10327 $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
10328 <script nonce="' . getNonce() . '">
10329 $(document).ready(function () {
10330
10331 ' . ($callurlonselect ? 'var saveRemoteData = [];' : '') . '
10332
10333 $(".' . $htmlname . '").select2({
10334 ajax: {
10335 dir: "ltr",
10336 url: "' . $url . '",
10337 dataType: \'json\',
10338 delay: 250,
10339 data: function (params) {
10340 return {
10341 q: params.term, // search term
10342 page: params.page
10343 }
10344 },
10345 processResults: function (data) {
10346 // parse the results into the format expected by Select2.
10347 // since we are using custom formatting functions we do not need to alter the remote JSON data
10348 //console.log(data);
10349 saveRemoteData = data;
10350 /* format json result for select2 */
10351 result = []
10352 $.each( data, function( key, value ) {
10353 result.push({id: key, text: value.text});
10354 });
10355 //return {results:[{id:\'none\', text:\'aa\'}, {id:\'rrr\', text:\'Red\'},{id:\'bbb\', text:\'Search a into projects\'}], more:false}
10356 //console.log(result);
10357 return {results: result, more: false}
10358 },
10359 cache: true
10360 },
10361 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage,
10362 containerCssClass: \':all:\', /* Line to add class from the original SELECT propagated to the new <span class="select2-selection...> tag */
10363 placeholder: \'' . dol_escape_js($placeholder) . '\',
10364 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10365 minimumInputLength: ' . ((int) $minimumInputLength) . ',
10366 formatResult: function (result, container, query, escapeMarkup) {
10367 return escapeMarkup(result.text);
10368 },
10369 });
10370
10371 ' . ($callurlonselect ? '
10372 /* Code to execute a GET when we select a value */
10373 $(".' . $htmlname . '").change(function() {
10374 var selected = $(\'.' . dol_escape_js($htmlname) . '\').val();
10375 console.log("We select in selectArrayAjax the entry "+selected)
10376 $(\'.' . dol_escape_js($htmlname) . '\').val(""); /* reset visible combo value */
10377 $.each( saveRemoteData, function( key, value ) {
10378 if (key == selected)
10379 {
10380 console.log("selectArrayAjax - Do a redirect to "+value.url)
10381 location.assign(value.url);
10382 }
10383 });
10384 });' : '') . '
10385
10386 });
10387 </script>';
10388 }
10389
10390 if ($acceptdelayedhtml) {
10391 $delayedhtmlcontent .= $outdelayed;
10392 } else {
10393 $out .= $outdelayed;
10394 }
10395 return $out;
10396 }
10397
10417 public static function selectArrayFilter($htmlname, $array, $id = '', $moreparam = '', $disableFiltering = 0, $disabled = 0, $minimumInputLength = 1, $morecss = '', $callurlonselect = 0, $placeholder = '', $acceptdelayedhtml = 0, $textfortitle = '')
10418 {
10419 global $conf;
10420 global $delayedhtmlcontent; // Will be used later outside of this function
10421
10422 // TODO Use an internal dolibarr component instead of select2
10423 if (!getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') && !defined('REQUIRE_JQUERY_MULTISELECT')) {
10424 return '';
10425 }
10426
10427 $out = '<select type="text"'.($textfortitle ? ' title="'.dol_escape_htmltag($textfortitle).'"' : '').' id="'.$htmlname.'" class="'.$htmlname.($morecss ? ' ' . $morecss : '').'"'.($moreparam ? ' '.$moreparam : '').' name="'.$htmlname.'"><option></option></select>';
10428
10429 $formattedarrayresult = array();
10430
10431 foreach ($array as $key => $value) {
10432 $o = new stdClass();
10433 $o->id = $key;
10434 $o->text = $value['text'];
10435 $o->url = $value['url'];
10436 $formattedarrayresult[] = $o;
10437 }
10438
10439 $outdelayed = '';
10440 if (!empty($conf->use_javascript_ajax)) {
10441 $tmpplugin = 'select2';
10442 $outdelayed = "\n" . '<!-- JS CODE TO ENABLE ' . $tmpplugin . ' for id ' . $htmlname . ' -->
10443 <script nonce="' . getNonce() . '">
10444 $(document).ready(function () {
10445 var data = ' . json_encode($formattedarrayresult) . ';
10446
10447 ' . ($callurlonselect ? 'var saveRemoteData = ' . json_encode($array) . ';' : '') . '
10448
10449 $(\'.' . dol_escape_js($htmlname) . '\').select2({
10450 data: data,
10451 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage,
10452 containerCssClass: \':all:\', /* Line to add class from the original SELECT propagated to the new <span class="select2-selection...> tag */
10453 placeholder: \'' . dol_escape_js($placeholder) . '\',
10454 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10455 minimumInputLength: ' . ((int) $minimumInputLength) . ',
10456 formatResult: function (result, container, query, escapeMarkup) {
10457 return escapeMarkup(result.text);
10458 },
10459 matcher: function (params, data) {
10460
10461 if(! data.id) return null;';
10462
10463 if ($callurlonselect) {
10464 // We forge the url with 'sall='
10465 $outdelayed .= '
10466
10467 var urlBase = data.url;
10468 var separ = urlBase.indexOf("?") >= 0 ? "&" : "?";
10469 /* console.log("params.term="+params.term); */
10470 /* console.log("params.term encoded="+encodeURIComponent(params.term)); */
10471 saveRemoteData[data.id].url = urlBase + separ + "search_all=" + encodeURIComponent(params.term.replace(/\"/g, ""));';
10472 }
10473
10474 if (!$disableFiltering) {
10475 $outdelayed .= '
10476
10477 if(data.text.match(new RegExp(params.term))) {
10478 return data;
10479 }
10480
10481 return null;';
10482 } else {
10483 $outdelayed .= '
10484
10485 return data;';
10486 }
10487
10488 $outdelayed .= '
10489 }
10490 });
10491
10492 ' . ($callurlonselect ? '
10493 /* Code to execute a GET when we select a value */
10494 $(\'.' . dol_escape_js($htmlname) . '\').change(function() {
10495 var selected = $(\'.' . dol_escape_js($htmlname) . '\').val();
10496 console.log("We select "+selected)
10497
10498 $(\'.' . dol_escape_js($htmlname) . '\').val(""); /* reset visible combo value */
10499 $.each( saveRemoteData, function( key, value ) {
10500 if (key == selected)
10501 {
10502 console.log("selectArrayFilter - Do a redirect to "+value.url)
10503 location.assign(value.url);
10504 }
10505 });
10506 });' : '') . '
10507
10508 });
10509 </script>';
10510 }
10511
10512 if ($acceptdelayedhtml) {
10513 $delayedhtmlcontent .= $outdelayed;
10514 } else {
10515 $out .= $outdelayed;
10516 }
10517 return $out;
10518 }
10519
10538 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)
10539 {
10540 global $conf, $langs;
10541 $out = '';
10542
10543 if ($addjscombo < 0) {
10544 if (!getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10545 $addjscombo = 1;
10546 } else {
10547 $addjscombo = 0;
10548 }
10549 }
10550
10551 $useenhancedmultiselect = 0;
10552 if (!empty($conf->use_javascript_ajax) && !defined('MAIN_DO_NOT_USE_JQUERY_MULTISELECT') && (getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT'))) {
10553 if ($addjscombo) {
10554 $useenhancedmultiselect = 1; // Use the js multiselect in one line. Possible only if $addjscombo not 0.
10555 }
10556 }
10557
10558 $out .= '<span class="multiselectarray'.$htmlname.'">';
10559
10560 // We need a hidden field because when using the multiselect, if we unselect all, there is no
10561 // variable submitted at all, so no way to make a difference between variable not submitted and variable
10562 // submitted to nothing.
10563 $out .= '<input type="hidden" name="'.$htmlname.'_multiselect" value="1">';
10564 // Output select component
10565 $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";
10566 if (is_array($array) && !empty($array)) {
10567 if ($value_as_key) {
10568 $array = array_combine($array, $array);
10569 }
10570
10571 if (!empty($array)) {
10572 foreach ($array as $key => $value) {
10573 $tmpkey = $key;
10574 $tmplabel = $value;
10575 $tmplabelhtml = '';
10576 $tmpcolor = '';
10577 $tmppicto = '';
10578 $tmpdisabled = '';
10579 if (is_array($value) && array_key_exists('id', $value) && array_key_exists('label', $value)) {
10580 $tmpkey = $value['id'];
10581 $tmplabel = empty($value['label']) ? '' : $value['label'];
10582 $tmplabelhtml = empty($value['labelhtml']) ? (empty($value['data-html']) ? '' : $value['data-html']) : $value['labelhtml'];
10583 $tmpcolor = empty($value['color']) ? '' : $value['color'];
10584 $tmppicto = empty($value['picto']) ? '' : $value['picto'];
10585 $tmpdisabled = empty($value['disabled']) ? '' : $value['disabled'];
10586 }
10587 $newval = ($translate ? $langs->trans($tmplabel) : $tmplabel);
10588 $newval = ($key_in_label ? $tmpkey . ' - ' . $newval : $newval);
10589
10590 $tmplabelhtml = ($translate ? $langs->trans($tmplabelhtml) : $tmplabelhtml);
10591 $tmplabelhtml = ($key_in_label ? $tmpkey . ' - ' . $tmplabelhtml : $tmplabelhtml);
10592
10593 $out .= '<option value="' . $tmpkey . '"';
10594 if (is_array($selected) && !empty($selected) && in_array((string) $tmpkey, $selected) && ((string) $tmpkey != '')) {
10595 $out .= ' selected';
10596 }
10597 $out .= is_array($value) && array_key_exists('parent', $value) && !empty($value['parent']) ? ' parent="' . dolPrintHTMLForAttribute($value['parent']) . '"' : '';
10598 if ($tmpdisabled) {
10599 $out .= ' disabled="disabled"';
10600 }
10601 if (!empty($tmplabelhtml)) {
10602 $out .= ' data-html="' . dolPrintHTMLForAttribute($tmplabelhtml) . '"';
10603 } else {
10604 $tmplabelhtml = ($tmppicto ? img_picto('', $tmppicto, 'class="pictofixedwidth" style="color: #' . $tmpcolor . '"') : '') . $newval;
10605 $out .= ' data-html="' . dolPrintHTMLForAttribute($tmplabelhtml) . '"';
10606 }
10607 $out .= '>';
10608 $out .= dol_htmlentitiesbr($newval);
10609 $out .= '</option>' . "\n";
10610 }
10611 }
10612 }
10613 $out .= '</select>' . "\n";
10614
10615 $out .= '</span>';
10616
10617 // Add code for jquery to use multiselect
10618 if (!empty($conf->use_javascript_ajax) && getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT') || defined('REQUIRE_JQUERY_MULTISELECT')) {
10619 $out .= "\n" . '<!-- JS CODE TO ENABLE select for id ' . $htmlname . ', addjscombo=' . $addjscombo . ' -->';
10620 $out .= "\n" . '<script nonce="' . getNonce() . '">' . "\n";
10621 if ($addjscombo == 1) {
10622 $tmpplugin = getDolGlobalString('MAIN_USE_JQUERY_MULTISELECT', (defined('REQUIRE_JQUERY_MULTISELECT') ? constant('REQUIRE_JQUERY_MULTISELECT') : 'select2'));
10623
10624 // If property data-html set, we decode html entities and use this.
10625 // Note that HTML content must have been sanitized from js with dol_escape_htmltag(xxx, 0, 0, '', 0, 1) when building the select option.
10626 // TODO Move this into common js ?
10627 $out .= 'function formatResult(record, container) {' . "\n";
10628 $out .= ' if ($(record.element).attr("data-html") != undefined && typeof htmlEntityDecodeJs === "function") {';
10629 $out .= ' return htmlEntityDecodeJs($(record.element).attr("data-html"));';
10630 $out .= ' }'."\n";
10631 $out .= ' return record.text;';
10632 $out .= '}' . "\n";
10633
10634 $out .= 'function formatSelection(record) {' . "\n";
10635 $out .= ' return record.text;';
10636 $out .= '}' . "\n";
10637
10638 // Load the select2 enhancer
10639 //$out .= 'console.log(\'addjscombo=1 for htmlname=' . dol_escape_js($htmlname) . '\');';
10640 $out .= '$(document).ready(function () {
10641 $(\'#' . dol_escape_js($htmlname) . '\').' . $tmpplugin . '({';
10642 if ($placeholder) {
10643 $out .= '
10644 placeholder: {
10645 id: \'-1\',
10646 text: \''.dol_escape_js($placeholder).'\'
10647 },';
10648 }
10649 $out .= ' dir: \'ltr\',
10650 containerCssClass: \':all:\', /* Line to add class of origin SELECT propagated to the new <span class="select2-selection...> tag (ko with multiselect) */
10651 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. */
10652 // Specify format function for dropdown item
10653 formatResult: formatResult,
10654 templateResult: formatResult, /* For 4.0 */
10655 escapeMarkup: function (markup) { return markup; }, // let our custom formatter work
10656 // Specify format function for selected item
10657 formatSelection: formatSelection,
10658 templateSelection: formatSelection, /* For 4.0 */
10659 language: (typeof select2arrayoflanguage === \'undefined\') ? \'en\' : select2arrayoflanguage
10660 });
10661
10662 /* Add also morecss to the css .select2 that is after the #htmlname, for component that are shown dynamically after load, because select2 set
10663 the size only if component is not hidden by default on load */
10664 $(\'#' . dol_escape_js($htmlname) . ' + .select2\').addClass(\'' . dol_escape_js($morecss) . '\');
10665 });' . "\n";
10666 } elseif ($addjscombo == 2 && !defined('DISABLE_MULTISELECT')) {
10667 // Add other js lib
10668 // TODO external lib multiselect/jquery.multi-select.js must have been loaded to use this multiselect plugin
10669 // ...
10670 $out .= 'console.log(\'addjscombo=2 for htmlname=' . dol_escape_js($htmlname) . '\');';
10671 $out .= '$(document).ready(function () {
10672 $(\'#' . dol_escape_js($htmlname) . '\').multiSelect({
10673 containerHTML: \'<div class="multi-select-container">\',
10674 menuHTML: \'<div class="multi-select-menu">\',
10675 buttonHTML: \'<span class="multi-select-button ' . dol_escape_js($morecss) . '">\',
10676 menuItemHTML: \'<label class="multi-select-menuitem">\',
10677 activeClass: \'multi-select-container--open\',
10678 noneText: \'' . dol_escape_js($placeholder) . '\'
10679 });
10680 })';
10681 }
10682 $out .= '</script>';
10683 }
10684
10685 return $out;
10686 }
10687
10688
10702 public static function multiSelectArrayWithCheckbox($htmlname, &$array, $varpage, $pos = '', $draganddrop = 0)
10703 {
10704 global $conf, $langs, $user;
10705
10706 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
10707 return '';
10708 }
10709 if (empty($array)) {
10710 return '';
10711 }
10712
10713 $tmpvar = "MAIN_SELECTEDFIELDS_" . $varpage; // To get list of saved selected fields to show
10714
10715 if (!empty($user->conf->$tmpvar)) { // A list of fields was already customized for user
10716 $tmparray = explode(',', $user->conf->$tmpvar);
10717 foreach ($array as $key => $val) {
10718 //var_dump($key);
10719 //var_dump($tmparray);
10720 if (in_array($key, $tmparray)) {
10721 $array[$key]['checked'] = 1;
10722 } else {
10723 $array[$key]['checked'] = 0;
10724 }
10725 }
10726 } else { // There is no list of fields already customized for user
10727 foreach ($array as $key => $val) {
10728 if (!empty($array[$key]['checked']) && $array[$key]['checked'] < 0) {
10729 $array[$key]['checked'] = 0;
10730 }
10731 }
10732 }
10733
10734 $listoffieldsforselection = '';
10735 $listcheckedstring = '';
10736
10737 foreach ($array as $key => $val) {
10738 // var_dump($val);
10739 // var_dump(array_key_exists('enabled', $val));
10740 // var_dump(!$val['enabled']);
10741 if (array_key_exists('enabled', $val) && isset($val['enabled']) && !$val['enabled']) {
10742 unset($array[$key]); // We don't want this field
10743 continue;
10744 }
10745 if (!empty($val['type']) && $val['type'] == 'separate') {
10746 // Field remains in array but we don't add it into $listoffieldsforselection
10747 //$listoffieldsforselection .= '<li>-----</li>';
10748 continue;
10749 }
10750 if (!empty($val['label']) && $val['label']) {
10751 if (!empty($val['langfile']) && is_object($langs)) {
10752 $langs->load($val['langfile']);
10753 }
10754
10755 // Note: $val['checked'] <> 0 means we must show the field into the combo list @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10756 $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']).'" />';
10757 $listoffieldsforselection .= '<label for="checkbox' . $key . '" class="paddingleft">';
10758 $listoffieldsforselection .= dolPrintHTML(dol_string_nohtmltag($langs->trans($val['label'])));
10759 $listoffieldsforselection .= '</label>';
10760 if (!empty($draganddrop)) {
10761 $listoffieldsforselection .= img_picto($langs->trans("MoveField", !empty($key) ? $key : 'none'), 'grip_title', 'class="opacitymedium boxhandle hideonsmartphone cursormove marginleftonly"');
10762 }
10763 $listoffieldsforselection .= '</li>';
10764 $listcheckedstring .= (empty($val['checked']) ? '' : $key . ',');
10765 }
10766 }
10767
10768 $out = '<!-- Component multiSelectArrayWithCheckbox ' . $htmlname . ' -->
10769
10770 <dl class="dropdown">
10771 <dt>
10772 <a href="#' . $htmlname . '" class="multiselectpicto">
10773 ' . img_picto('', 'list') . '
10774 </a>
10775 <input type="hidden" class="' . $htmlname . '" name="' . $htmlname . '" value="' . $listcheckedstring . '">
10776 </dt>
10777 <dd class="dropdowndd">
10778 <div class="multiselectcheckbox'.$htmlname.'">
10779 <ul class="'.$htmlname.(((string) $pos == '1' || (string) $pos == 'left') ? 'left' : '').(!empty($draganddrop) ? ' sortable' : '').'">
10780 <li class="liinputsearch">
10781 <input class="inputsearch_dropdownselectedfields width90p minwidth200imp" style="width:90%;" type="text" placeholder="'.$langs->trans('Search').'">
10782 </li>
10783 '.$listoffieldsforselection.'
10784 </ul>
10785 </div>
10786 </dd>
10787 </dl>
10788
10789 <script>
10790 function updateFieldOrder() {
10791 var positionfields = $(".sortable").sortable("toArray");
10792 $.ajax({
10793 url: \''.DOL_URL_ROOT.'/core/ajax/changepositionfields.php?positionfields=\'+positionfields+\'&token='.newToken().'&action=listafterchangingpositionfields&contextpage='.$varpage.'&userid='.$user->id.'\',
10794 async: false,
10795 success: function () {
10796 // reload page
10797 window.location.href = "'.$_SERVER["PHP_SELF"].'";
10798 }
10799 });
10800 }
10801 $( ".sortable" ).sortable({
10802 handle: \'.boxhandle\',
10803 revert: \'invalid\',
10804 items: \'.fieldsortable\',
10805 stop: function(event, ui) {
10806 console.log("We moved box so we call updateBoxOrder with ajax actions");
10807 updateFieldOrder(); /* 1 to avoid message after a move */
10808 }
10809 });
10810 </script>
10811
10812 <script nonce="' . getNonce() . '" type="text/javascript">
10813 jQuery(document).ready(function () {
10814 $(\'.multiselectcheckbox' . $htmlname . ' input[type="checkbox"]\').on("click", function () {
10815 console.log("A new field was added/removed, we edit field input[name=formfilteraction]");
10816
10817 $("input:hidden[name=formfilteraction]").val(\'listafterchangingselectedfields\'); // Update field so we know we changed something on selected fields after POST
10818
10819 var title = $(this).val() + ",";
10820 if ($(this).is(\':checked\')) {
10821 $(\'.' . $htmlname . '\').val(title + $(\'.' . $htmlname . '\').val());
10822 }
10823 else {
10824 $(\'.' . $htmlname . '\').val( $(\'.' . $htmlname . '\').val().replace(title, \'\') )
10825 }
10826 // Now, we submit page
10827 //$(this).parents(\'form:first\').submit();
10828 });
10829
10830 $("input.inputsearch_dropdownselectedfields").on("keyup", function() {
10831 console.log("keyup on inputsearch_dropdownselectedfields");
10832 var value = $(this).val().toLowerCase();
10833 $(\'.multiselectcheckbox'.$htmlname.' li > label\').filter(function() {
10834 $(this).parent().toggle($(this).text().toLowerCase().indexOf(value) > -1)
10835 });
10836 });
10837 ';
10838 if (empty($conf->browser->layout) || $conf->browser->layout != 'phone') {
10839 $out .= '
10840 $(".dropdown dt a").on("click", function () {
10841 console.log("Click on dropdown, we set focus to search field");
10842 setTimeout(() => { $(\'.inputsearch_dropdownselectedfields\').focus(); }, 200);
10843 });';
10844 }
10845 $out .= '
10846 });
10847 </script>
10848
10849 ';
10850 return $out;
10851 }
10852
10862 public function showCategories($id, $type, $rendermode = 0, $nolink = 0)
10863 {
10864 global $conf;
10865
10866 include_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
10867
10868 $cat = new Categorie($this->db);
10869 $categories = $cat->containing($id, $type);
10870
10871 if ($rendermode == 1 || $rendermode == 2) {
10872 $toprint = array();
10873 foreach ($categories as $c) {
10874 $ways = $c->print_all_ways('auto', ($nolink ? 'none' : ''), 0, 1, ($rendermode == 2 ? 0 : 1)); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
10875 foreach ($ways as $way) {
10876 $color = $c->color;
10877 $sfortag = '<li class="select2-search-choice-dolibarr noborderoncategories'.(empty($toprint) ? ' nomarginleft' : '');
10878 $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.
10879 if ($c->color && colorIsLight($c->color)) {
10880 $forced_color = 'categtextblack';
10881 }
10882 $sfortag .= ' '.$forced_color;
10883 $sfortag .= '"';
10884 $sfortag .= ($color ? ' style="background: #' . $color . ';"' : ' style="background: #bbb"');
10885 $titlestring = $ways[0];
10886 $titlestring = str_replace('>', ' - ', dol_string_nohtmltag($titlestring));
10887 $sfortag .= ' title="' . dolPrintHTMLForAttribute($titlestring) . '"';
10888 $sfortag .= '>';
10889 if ($rendermode == 1) {
10890 $sfortag .= '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.((int) $c->id).'&type='.urlencode($c->type).'" class="'.$forced_color.'">';
10891 $sfortag .= img_picto('', 'category', 'class="paddingright"');
10892 if ($conf->dol_optimize_smallscreen) {
10893 $sfortag .= dolPrintHTML(dol_trunc($c->label, 8));
10894 } else {
10895 $sfortag .= dolPrintHTML($c->label);
10896 }
10897 $sfortag .= '</a>';
10898 } else {
10899 $sfortag .= $way;
10900 }
10901 $sfortag .= '</li>';
10902
10903 $toprint[] = $sfortag; // Add tag in list of tag to show
10904 }
10905 }
10906 if (empty($toprint)) {
10907 return '';
10908 } else {
10909 return '<div class="select2-container-multi-dolibarr"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
10910 }
10911 }
10912
10913 if ($rendermode == 0) {
10914 $arrayselected = array();
10915 $cate_arbo = $this->select_all_categories($type, '', 'parent', 64, 0, 3);
10916 foreach ($categories as $c) {
10917 $arrayselected[(string) $c->id] = (string) $c->id;
10918 }
10919
10920 return $this->multiselectarray('categories', $cate_arbo, $arrayselected, 0, 0, '', 0, '100%', 'disabled', 'category');
10921 }
10922
10923 return 'ErrorBadValueForParameterRenderMode'; // Should not happened
10924 }
10925
10935 public function showLinkedObjectBlock($object, $morehtmlright = '', $compatibleImportElementsList = array(), $title = 'RelatedObjects')
10936 {
10937 global $conf, $langs, $hookmanager;
10938 global $action;
10939 global $db, $user; // Will be used into tpl
10940
10941 dol_syslog(__METHOD__, LOG_DEBUG);
10942
10943 $object->fetchObjectLinked();
10944
10945 // Bypass the default method
10946 $hookmanager->initHooks(array('commonobject'));
10947 $parameters = array(
10948 'morehtmlright' => $morehtmlright,
10949 'compatibleImportElementsList' => &$compatibleImportElementsList,
10950 );
10951 $reshook = $hookmanager->executeHooks('showLinkedObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
10952
10953 $nbofdifferenttypes = count($object->linkedObjects);
10954
10955 if (empty($reshook)) {
10956 print '<!-- showLinkedObjectBlock -->';
10957 print load_fiche_titre($langs->trans($title), $morehtmlright, '', 0, '', 'showlinkedobjectblock');
10958
10959
10960 print '<div class="div-table-responsive-no-min">';
10961 print '<table class="noborder allwidth" data-block="showLinkedObject" data-element="' . $object->element . '" data-elementid="' . $object->id . '" >';
10962
10963 print '<tr class="liste_titre">';
10964 print '<td>' . $langs->trans("Type") . '</td>';
10965 print '<td>' . $langs->trans("Ref") . '</td>';
10966 print '<td></td>';
10967 print '<td></td>';
10968 print '<td class="right">' . $langs->trans("AmountHTShort") . '</td>';
10969 print '<td class="right">' . $langs->trans("Status") . '</td>';
10970 print '<td></td>';
10971 print '</tr>';
10972
10973 $nboftypesoutput = 0;
10974
10975 foreach ($object->linkedObjects as $objecttype => $objects) {
10976 $tplpath = $element = $subelement = $objecttype;
10977
10978 // to display import button on tpl
10979 global $showImportButton; // Will be used into tpl
10980 $showImportButton = false;
10981 if (!empty($compatibleImportElementsList) && in_array($element, $compatibleImportElementsList)) {
10982 $showImportButton = true;
10983 }
10984
10985 $regs = array();
10986
10987 if ($objecttype != 'supplier_proposal' && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
10988 $element = $regs[1];
10989 $subelement = $regs[2];
10990 $tplpath = $element . '/' . $subelement;
10991 }
10992 $tplname = 'linkedobjectblock';
10993
10994 // If we ask a resource form external module (instead of default path)
10995 if (preg_match('/^([^@]+)@([^@]+)$/i', $objecttype, $regs)) { // 'myobject@mymodule'
10996 $element = $regs[1];
10997 $module = $regs[2];
10998 $tplpath = $module. '/' . $element;
10999 $tplname = $tplname.'_'.$element;
11000 }
11001
11002 // To work with non standard path
11003 if ($objecttype == 'facture') {
11004 $tplpath = 'compta/' . $element;
11005 if (!isModEnabled('invoice')) {
11006 continue; // Do not show if module disabled
11007 }
11008 } elseif ($objecttype == 'facturerec') {
11009 $tplpath = 'compta/facture';
11010 $tplname = 'linkedobjectblockForRec';
11011 if (!isModEnabled('invoice')) {
11012 continue; // Do not show if module disabled
11013 }
11014 } elseif ($objecttype == 'propal') {
11015 $tplpath = 'comm/' . $element;
11016 if (!isModEnabled('propal')) {
11017 continue; // Do not show if module disabled
11018 }
11019 } elseif ($objecttype == 'supplier_proposal') {
11020 if (!isModEnabled('supplier_proposal')) {
11021 continue; // Do not show if module disabled
11022 }
11023 } elseif ($objecttype == 'shipping' || $objecttype == 'shipment' || $objecttype == 'expedition') {
11024 $tplpath = 'expedition';
11025 if (!isModEnabled('shipping')) {
11026 continue; // Do not show if module disabled
11027 }
11028 } elseif ($objecttype == 'reception') {
11029 $tplpath = 'reception';
11030 if (!isModEnabled('reception')) {
11031 continue; // Do not show if module disabled
11032 }
11033 } elseif ($objecttype == 'delivery') {
11034 $tplpath = 'delivery';
11035 if (!getDolGlobalInt('MAIN_SUBMODULE_DELIVERY')) {
11036 continue; // Do not show if sub module disabled
11037 }
11038 } elseif ($objecttype == 'ficheinter') {
11039 $tplpath = 'fichinter';
11040 if (!isModEnabled('intervention')) {
11041 continue; // Do not show if module disabled
11042 }
11043 } elseif ($objecttype == 'invoice_supplier') {
11044 $tplpath = 'fourn/facture';
11045 } elseif ($objecttype == 'order_supplier') {
11046 $tplpath = 'fourn/commande';
11047 } elseif ($objecttype == 'expensereport') {
11048 $tplpath = 'expensereport';
11049 } elseif ($objecttype == 'subscription') {
11050 $tplpath = 'adherents';
11051 } elseif ($objecttype == 'conferenceorbooth') {
11052 $tplpath = 'eventorganization';
11053 } elseif ($objecttype == 'conferenceorboothattendee') {
11054 $tplpath = 'eventorganization';
11055 } elseif ($objecttype == 'mo') {
11056 $tplpath = 'mrp';
11057 if (!isModEnabled('mrp')) {
11058 continue; // Do not show if module disabled
11059 }
11060 } elseif ($objecttype == 'project_task') {
11061 $tplpath = 'projet/tasks';
11062 }
11063
11064 global $linkedObjectBlock; // Will be used into tpl
11065 $linkedObjectBlock = $objects;
11066
11067 // Output template part (modules that overwrite templates must declare this into descriptor)
11068 $dirtpls = array_merge($conf->modules_parts['tpl'], array('/' . $tplpath . '/tpl'));
11069
11070 foreach ($dirtpls as $reldir) {
11071 $reldir = rtrim($reldir, '/');
11072 if ($nboftypesoutput == ($nbofdifferenttypes - 1)) { // No more type to show after
11073 global $noMoreLinkedObjectBlockAfter; // Will be used into tpl
11074 $noMoreLinkedObjectBlockAfter = 1;
11075 }
11076 $file = dol_buildpath($reldir . '/' . $tplname . '.tpl.php');
11077 if (file_exists($file)) {
11078 $res = @include $file;
11079 if ($res) {
11080 $nboftypesoutput++;
11081 break;
11082 }
11083 }
11084 }
11085 }
11086
11087 if (!$nboftypesoutput) {
11088 print '<tr><td colspan="7"><span class="opacitymedium">' . $langs->trans("None") . '</span></td></tr>';
11089 }
11090
11091 print '</table>';
11092
11093 if (!empty($compatibleImportElementsList)) {
11094 $res = @include dol_buildpath('core/tpl/objectlinked_lineimport.tpl.php');
11095 }
11096
11097 print '</div>';
11098 }
11099
11100 return $nbofdifferenttypes;
11101 }
11102
11112 public function showLinkToObjectBlock($object, $restrictlinksto = array(), $excludelinksto = array(), $nooutput = 0)
11113 {
11114 global $conf, $langs, $hookmanager, $form;
11115 global $action;
11116
11117 dol_syslog(__METHOD__, LOG_DEBUG);
11118
11119 if (empty($form)) {
11120 $form = new Form($this->db);
11121 }
11122
11123 $linktoelem = '';
11124 $linktoelemlist = '';
11125 $listofidcompanytoscan = '';
11126
11127 if (!is_object($object->thirdparty)) {
11128 if ($object->element == 'subscription' && isset($object->fk_adherent)) {
11129 $subby = new Subscription($object->db);
11130 $subby->fetch($object->id);
11131 $adh = new Adherent($object->db);
11132 //$fk_adherent = $object->fk_adherent;
11133 // creating new subscription object only to fetch the adherent which obviously exists given the if statement above are Inefficient, but else phan complains
11134 $fk_adherent = $subby->fk_adherent;
11135 $adh->fetch($fk_adherent);
11136 $thirdparty_id = $adh->fetch_thirdparty();
11137 }
11138 } else {
11139 $thirdparty_id = $object->thirdparty->id;
11140 }
11141
11142 $possiblelinks = array();
11143
11144 $dontIncludeCompletedItems = getDolGlobalString('DONT_INCLUDE_COMPLETED_ELEMENTS_LINKS');
11145
11146 if (!empty($thirdparty_id) && $thirdparty_id > 0) {
11147 $listofidcompanytoscan = (int) $thirdparty_id;
11148 if (is_object($object->thirdparty) && ($object->thirdparty->parent > 0) && getDolGlobalString('THIRDPARTY_INCLUDE_PARENT_IN_LINKTO')) {
11149 $listofidcompanytoscan .= ',' . (int) $object->thirdparty->parent;
11150 }
11151 if (($object->fk_project > 0) && getDolGlobalString('THIRDPARTY_INCLUDE_PROJECT_THIRDPARY_IN_LINKTO')) {
11152 include_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
11153 $tmpproject = new Project($this->db);
11154 $tmpproject->fetch((int) $object->fk_project);
11155 if ($tmpproject->socid > 0 && ($tmpproject->socid != $thirdparty_id)) {
11156 $listofidcompanytoscan .= ',' . (int) $tmpproject->socid;
11157 }
11158 unset($tmpproject);
11159 }
11160
11161 $possiblelinks = array(
11162 'propal' => array(
11163 'enabled' => isModEnabled('propal'),
11164 'perms' => 1,
11165 'label' => 'LinkToProposal',
11166 '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' : ''),
11167 ),
11168 'shipping' => array(
11169 'enabled' => isModEnabled('shipping'),
11170 'perms' => 1,
11171 'label' => 'LinkToExpedition',
11172 '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' : ''),
11173 ),
11174 'order' => array(
11175 'enabled' => isModEnabled('order'),
11176 'perms' => 1,
11177 'label' => 'LinkToOrder',
11178 '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' : ''),
11179 'linkname' => 'commande',
11180 ),
11181 'subscription' => array(
11182 'enabled' => isModEnabled('member'),
11183 'perms' => 1,
11184 'label' => 'LinkToMemberSubscription',
11185 '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') . ')',
11186 'linkname' => 'subscription',
11187 ),
11188 'conferenceorboothattendee' => array(
11189 'enabled' => isModEnabled('eventorganization'),
11190 'perms' => 1,
11191 'label' => 'LinkToConferenceOrBoothAttendee',
11192 '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 " . $this->db->prefix() . "societe as s, " . $this->db->prefix() . "eventorganization_conferenceorboothattendee as a WHERE a.fk_soc = s.rowid AND a.fk_soc IN (" . $this->db->sanitize($listofidcompanytoscan) . ') AND s.entity IN (' . getEntity('conferenceorboothattendee') . ')' . (empty($object->fk_project) ? '' : ' AND a.fk_project = ' . (int) $object->fk_project),
11193 'linkname' => 'attendee'
11194 ),
11195 'invoice' => array(
11196 'enabled' => isModEnabled('invoice'),
11197 'perms' => 1,
11198 'label' => 'LinkToInvoice',
11199 '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' : ''),
11200 'linkname' => 'facture',
11201 ),
11202 'invoice_template' => array(
11203 'enabled' => isModEnabled('invoice'),
11204 'perms' => 1,
11205 'label' => 'LinkToTemplateInvoice',
11206 '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') . ')',
11207 ),
11208 'contrat' => array(
11209 'enabled' => isModEnabled('contract'),
11210 'perms' => 1,
11211 'label' => 'LinkToContract',
11212 '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
11213 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',
11214 ),
11215 'fichinter' => array(
11216 'enabled' => isModEnabled('intervention'),
11217 'perms' => 1,
11218 'label' => 'LinkToIntervention',
11219 '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') . ')',
11220 ),
11221 'supplier_proposal' => array(
11222 'enabled' => isModEnabled('supplier_proposal'),
11223 'perms' => 1,
11224 'label' => 'LinkToSupplierProposal',
11225 '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' : ''),
11226 ),
11227 'order_supplier' => array(
11228 'enabled' => isModEnabled("supplier_order"),
11229 'perms' => 1,
11230 'label' => 'LinkToSupplierOrder',
11231 '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' : ''),
11232 ),
11233 'invoice_supplier' => array(
11234 'enabled' => isModEnabled("supplier_invoice"),
11235 'perms' => 1, 'label' => 'LinkToSupplierInvoice',
11236 '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' : ''),
11237 ),
11238 'ticket' => array(
11239 'enabled' => isModEnabled('ticket'),
11240 'perms' => 1,
11241 'label' => 'LinkToTicket',
11242 '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' : ''),
11243 ),
11244 'mo' => array(
11245 'enabled' => isModEnabled('mrp'),
11246 'perms' => 1,
11247 'label' => 'LinkToMo',
11248 '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' : ''),
11249 ),
11250 );
11251 }
11252
11253 if ($object->table_element == 'commande_fournisseur') {
11254 $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' : '');
11255 } elseif ($object->table_element == 'mrp_mo') {
11256 $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' : '');
11257 }
11258
11259 $reshook = 0; // Ensure $reshook is defined for static analysis
11260 if (!empty($listofidcompanytoscan)) { // If empty, we don't have criteria to scan the object we can link to
11261 // Can complete the possiblelink array
11262 $hookmanager->initHooks(array('commonobject'));
11263 $parameters = array('listofidcompanytoscan' => $listofidcompanytoscan, 'possiblelinks' => $possiblelinks);
11264 $reshook = $hookmanager->executeHooks('showLinkToObjectBlock', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
11265 }
11266
11267 if (empty($reshook)) {
11268 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
11269 $possiblelinks = array_merge($possiblelinks, $hookmanager->resArray);
11270 }
11271 } elseif ($reshook > 0) {
11272 if (is_array($hookmanager->resArray) && count($hookmanager->resArray)) {
11273 $possiblelinks = $hookmanager->resArray;
11274 }
11275 }
11276
11277 if (!empty($possiblelinks)) {
11278 $object->fetchObjectLinked();
11279 }
11280
11281 // Build the html part with possible suggested links
11282 $htmltoenteralink = '';
11283 foreach ($possiblelinks as $key => $possiblelink) {
11284 $num = 0;
11285 if (empty($possiblelink['enabled'])) {
11286 continue;
11287 }
11288
11289
11290 // If we ask a resource form external module (instead of default path)
11291 $module = '';
11292 if (preg_match('/^([^@]+)@([^@]+)$/i', $key, $regs)) { // 'myobject@mymodule'
11293 $key = $regs[1];
11294 $module = $regs[2];
11295 }
11296
11297 if (!empty($possiblelink['perms']) && (empty($restrictlinksto) || in_array($key, $restrictlinksto)) && (empty($excludelinksto) || !in_array($key, $excludelinksto))) {
11298 $htmltoenteralink .= '<div id="' . $key . 'list"' . (empty($conf->use_javascript_ajax) ? '' : ' style="display:none"') . '>';
11299
11300 // Section for free ref input
11301 if (!getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11302 $htmltoenteralink .= '<br>'."\n";
11303 $htmltoenteralink .= '<!-- form to add a link from anywhere -->'."\n";
11304 $htmltoenteralink .= '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinkedbyref' . $key . '">';
11305 $htmltoenteralink .= '<input type="hidden" name="token" value="' . newToken() . '">';
11306 $htmltoenteralink .= '<input type="hidden" name="action" value="addlinkbyref">';
11307 $htmltoenteralink .= '<input type="hidden" name="id" value="' . $object->id . '">';
11308 $htmltoenteralink .= '<input type="hidden" name="addlink" value="' . $key .(!empty($module) ? '@'.$module : ''). '">';
11309 $htmltoenteralink .= '<table class="noborder">';
11310 $htmltoenteralink .= '<tr class="liste_titre">';
11311 //print '<td>' . $langs->trans("Ref") . '</td>';
11312 $htmltoenteralink .= '<td class="center"><input type="text" placeholder="'.dol_escape_htmltag($langs->trans("Ref")).'" name="reftolinkto" value="' . dol_escape_htmltag(GETPOST('reftolinkto', 'alpha')) . '">';
11313 $htmltoenteralink .= '<br>';
11314 $htmltoenteralink .= '<input type="submit" class="button smallpaddingimp valignmiddle" value="' . $langs->trans('ToLink') . '">&nbsp;';
11315 $htmltoenteralink .= '<input type="submit" class="button smallpaddingimp" name="cancel" value="' . $langs->trans('Cancel') . '">';
11316 $htmltoenteralink .= '</td>';
11317 $htmltoenteralink .= '</tr>';
11318 $htmltoenteralink .= '</table>';
11319 $htmltoenteralink .= '</form>';
11320 }
11321
11322 $sql = $possiblelink['sql'];
11323
11324 $resqllist = $this->db->query($sql);
11325 if ($resqllist) {
11326 $num = $this->db->num_rows($resqllist);
11327
11328 if ($num > 0) {
11329 // Section for free predefined list
11330 if (getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11331 $htmltoenteralink .= '<br>';
11332 }
11333 $htmltoenteralink .= '<!-- form to add a link from object to same thirdparty -->'."\n";
11334 $htmltoenteralink .= '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formlinked' . $key . '">';
11335 $htmltoenteralink .= '<input type="hidden" name="token" value="' . newToken() . '">';
11336 $htmltoenteralink .= '<input type="hidden" name="action" value="addlink">';
11337 $htmltoenteralink .= '<input type="hidden" name="id" value="' . $object->id . '">';
11338 $htmltoenteralink .= '<input type="hidden" name="addlink" value="' . $key . (!empty($module) ? '@'.$module : ''). '">';
11339 $htmltoenteralink .= '<table class="noborder">';
11340
11341 switch ($key) {
11342 case 'conferenceorboothattendee':
11343 // Custom logic for linking to attendees
11344 $htmltoenteralink .= $this->makeAddLinkToAttendee($object, $key, $possiblelink, $num, $resqllist);
11345 break;
11346
11347 default:
11348 // Standard logic for all other object types
11349 $htmltoenteralink .= $this->makeAddLinkToObject($object, $key, $possiblelink, $num, $resqllist);
11350 break;
11351 }
11352
11353 $htmltoenteralink .= '</table>';
11354 $htmltoenteralink .= '<div class="center">';
11355 if ($num) {
11356 $htmltoenteralink .= '<input type="submit" class="button valignmiddle marginleftonly marginrightonly smallpaddingimp" value="' . $langs->trans('ToLink') . '">';
11357 }
11358 if (empty($conf->use_javascript_ajax)) {
11359 $htmltoenteralink .= '<input type="submit" class="button button-cancel marginleftonly marginrightonly smallpaddingimp" name="cancel" value="' . $langs->trans("Cancel") . '"></div>';
11360 } else {
11361 $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>';
11362 }
11363 $htmltoenteralink .= '</form>';
11364 }
11365
11366 $this->db->free($resqllist);
11367 } else {
11368 dol_print_error($this->db);
11369 }
11370 $htmltoenteralink .= '</div>';
11371
11372
11373 // Complete the list for the combo box
11374 if ($num > 0 || !getDolGlobalString('MAIN_HIDE_LINK_BY_REF_IN_LINKTO')) {
11375 $linktoelemlist .= '<li><a href="#linkto' . $key . '" class="linkto dropdowncloseonclick" rel="' . $key . '">' . $langs->trans($possiblelink['label']) . ' (' . $num . ')</a></li>';
11376 // } else $linktoelem.=$langs->trans($possiblelink['label']);
11377 } else {
11378 $linktoelemlist .= '<li><span class="linktodisabled">' . $langs->trans($possiblelink['label']) . ' (0)</span></li>';
11379 }
11380 }
11381 }
11382
11383 if ($linktoelemlist) {
11384 $linktoelem = '
11385 <dl class="dropdown" id="linktoobjectname">
11386 ';
11387 if (!empty($conf->use_javascript_ajax)) {
11388 $linktoelem .= '<dt><a href="#linktoobjectname"><span class="fas fa-link paddingrightonly"></span>' . $langs->trans("LinkTo") . '...</a></dt>';
11389 }
11390 $linktoelem .= '<dd>
11391 <div class="multiselectlinkto">
11392 <ul class="ulselectedfields">' . $linktoelemlist . '
11393 </ul>
11394 </div>
11395 </dd>
11396 </dl>';
11397 } else {
11398 $linktoelem = '';
11399 }
11400
11401 if (!empty($conf->use_javascript_ajax)) {
11402 print '<!-- Add js to show linkto box -->
11403 <script nonce="' . getNonce() . '">
11404 jQuery(document).ready(function() {
11405 jQuery(".linkto").click(function() {
11406 console.log("We choose to show/hide links for rel="+jQuery(this).attr(\'rel\')+" so #"+jQuery(this).attr(\'rel\')+"list");
11407 jQuery("#"+jQuery(this).attr(\'rel\')+"list").toggle();
11408 });
11409 });
11410 </script>
11411 ';
11412 }
11413
11414 if ($nooutput) {
11415 return array('linktoelem' => $linktoelem, 'htmltoenteralink' => $htmltoenteralink);
11416 } else {
11417 print $htmltoenteralink;
11418 }
11419
11420 return $linktoelem;
11421 }
11422
11437 public function selectyesno($htmlname, $value = '', $option = 0, $disabled = false, $useempty = 0, $addjscombo = 0, $morecss = 'yesno width75', $labelyes = 'Yes', $labelno = 'No')
11438 {
11439 global $langs;
11440
11441 $yes = "yes";
11442 $no = "no";
11443 if ($option) {
11444 $yes = "1";
11445 $no = "0";
11446 }
11447
11448 $disabled = ($disabled ? ' disabled' : '');
11449
11450 $resultyesno = '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . '"' . $disabled . '>' . "\n";
11451 if ($useempty) {
11452 $resultyesno .= '<option value="-1"' . (($value < 0) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
11453 }
11454 if (("$value" == 'yes') || ($value == 1)) {
11455 $resultyesno .= '<option value="' . $yes . '" selected>' . $langs->trans($labelyes) . '</option>' . "\n";
11456 $resultyesno .= '<option value="' . $no . '">' . $langs->trans($labelno) . '</option>' . "\n";
11457 } else {
11458 $selected = (($useempty && $value != '0' && $value != 'no') ? '' : ' selected');
11459 $resultyesno .= '<option value="' . $yes . '">' . $langs->trans($labelyes) . '</option>' . "\n";
11460 $resultyesno .= '<option value="' . $no . '"' . $selected . '>' . $langs->trans($labelno) . '</option>' . "\n";
11461 }
11462 $resultyesno .= '</select>' . "\n";
11463
11464 if ($addjscombo) {
11465 $resultyesno .= ajax_combobox($htmlname, array(), 0, 0, 'resolve', ($useempty < 0 ? (string) $useempty : '-1'), $morecss);
11466 }
11467
11468 return $resultyesno;
11469 }
11470
11471 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
11472
11482 public function select_export_model($selected = '', $htmlname = 'exportmodelid', $type = '', $useempty = 0)
11483 {
11484 // phpcs:enable
11485 $sql = "SELECT rowid, label";
11486 $sql .= " FROM " . $this->db->prefix() . "export_model";
11487 $sql .= " WHERE type = '" . $this->db->escape($type) . "'";
11488 $sql .= " ORDER BY rowid";
11489 $result = $this->db->query($sql);
11490 if ($result) {
11491 print '<select class="flat" id="select_' . $htmlname . '" name="' . $htmlname . '">';
11492 if ($useempty) {
11493 print '<option value="-1">&nbsp;</option>';
11494 }
11495
11496 $num = $this->db->num_rows($result);
11497 $i = 0;
11498 while ($i < $num) {
11499 $obj = $this->db->fetch_object($result);
11500 if ($selected == $obj->rowid) {
11501 print '<option value="' . $obj->rowid . '" selected>';
11502 } else {
11503 print '<option value="' . $obj->rowid . '">';
11504 }
11505 print $obj->label;
11506 print '</option>';
11507 $i++;
11508 }
11509 print "</select>";
11510 } else {
11511 dol_print_error($this->db);
11512 }
11513 }
11514
11533 public function showrefnav($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $morehtmlright = '')
11534 {
11535 global $conf, $langs, $hookmanager, $extralanguages;
11536
11537 $ret = '';
11538 if (empty($fieldid)) {
11539 $fieldid = 'rowid';
11540 }
11541 if (empty($fieldref)) {
11542 $fieldref = 'ref';
11543 }
11544
11545 // Preparing gender's display if there is one
11546 $addgendertxt = '';
11547 if (property_exists($object, 'gender') && !empty($object->gender)) {
11548 $addgendertxt = ' ';
11549 switch ($object->gender) {
11550 case 'man':
11551 $addgendertxt .= '<i class="fas fa-mars valignmiddle"></i>';
11552 break;
11553 case 'woman':
11554 $addgendertxt .= '<i class="fas fa-venus valignmiddle"></i>';
11555 break;
11556 case 'other':
11557 $addgendertxt .= '<i class="fas fa-transgender valignmiddle"></i>';
11558 break;
11559 }
11560 }
11561
11562 // Add where from hooks
11563 if (is_object($hookmanager)) {
11564 $parameters = array('showrefnav' => true);
11565 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
11566 if (!empty($hookmanager->resPrint)) {
11567 if (empty($object->next_prev_filter) && preg_match('/^\s*AND/i', $hookmanager->resPrint)) {
11568 $object->next_prev_filter = (string) preg_replace('/^\s*AND\s*/i', '', $hookmanager->resPrint);
11569 } elseif (!empty($object->next_prev_filter) && !preg_match('/^\s*AND/i', $hookmanager->resPrint)) {
11570 $object->next_prev_filter .= ' AND '.$hookmanager->resPrint;
11571 } else {
11572 $object->next_prev_filter .= $hookmanager->resPrint;
11573 }
11574 }
11575 }
11576
11577 $previous_ref = $next_ref = '';
11578 if ($shownav) {
11579 //print "paramid=$paramid,morehtml=$morehtml,shownav=$shownav,fieldid=$fieldid,filedref=$fieldref,morehtmlref=$morehtmlref,moreparam=$moreparam";
11580 $object->load_previous_next_ref((isset($object->next_prev_filter) ? $object->next_prev_filter : ''), $fieldid, $nodbprefix);
11581
11582 $navurl = $_SERVER["PHP_SELF"];
11583
11584 // Special case for token card
11585 if ($paramid == 'api_token_card') {
11586 if (preg_match('/\/user\/api_token/', $navurl)) {
11587 $navurl = preg_replace('/card/', 'list', $navurl);
11588 $paramid = 'id';
11589 }
11590 }
11591
11592 // Special case for project/task page
11593 if ($paramid == 'project_ref') {
11594 if (preg_match('/\/tasks\/(task|contact|note|document)\.php/', $navurl)) { // TODO Remove this when nav with project_ref on task pages are ok
11595 $navurl = preg_replace('/\/tasks\/(task|contact|time|note|document)\.php/', '/tasks.php', $navurl);
11596 $paramid = 'ref';
11597 }
11598 }
11599
11600 $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>';
11601 $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>';
11602 }
11603
11604 //print "xx".$previous_ref."x".$next_ref;
11605 $ret .= '<!-- Start banner content --><div style="vertical-align: middle">';
11606
11607 // Right part of banner
11608 if ($morehtmlright) {
11609 $ret .= '<div class="inline-block floatleft">' . $morehtmlright . '</div>';
11610 }
11611
11612 if ($previous_ref || $next_ref || $morehtml) {
11613 $ret .= '<div class="pagination paginationref"><ul class="right">';
11614 }
11615 if ($morehtml && getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
11616 $ret .= '<!-- morehtml --><li class="noborder litext' . (($shownav && $previous_ref && $next_ref) ? ' clearbothonsmartphone' : '') . '">' . $morehtml . '</li>';
11617 }
11618 if ($shownav && ($previous_ref || $next_ref)) {
11619 $ret .= '<li class="pagination">' . $previous_ref . '</li>';
11620 $ret .= '<li class="pagination">' . $next_ref . '</li>';
11621 }
11622 if ($previous_ref || $next_ref || $morehtml) {
11623 $ret .= '</ul></div>';
11624 }
11625
11626 // Status
11627 $parameters = array('morehtmlstatus' => $morehtmlstatus);
11628 $reshook = $hookmanager->executeHooks('moreHtmlStatus', $parameters, $object); // Note that $action and $object may have been modified by hook
11629 if (empty($reshook)) {
11630 $morehtmlstatus .= $hookmanager->resPrint;
11631 } else {
11632 $morehtmlstatus = $hookmanager->resPrint;
11633 }
11634 if ($morehtmlstatus) {
11635 $ret .= '<!-- status --><div class="statusref">' . $morehtmlstatus . '</div>';
11636 }
11637
11638 $parameters = array();
11639 $reshook = $hookmanager->executeHooks('moreHtmlRef', $parameters, $object); // Note that $action and $object may have been modified by hook
11640 if (empty($reshook)) {
11641 $morehtmlref .= $hookmanager->resPrint;
11642 } elseif ($reshook > 0) {
11643 $morehtmlref = $hookmanager->resPrint;
11644 }
11645
11646 // Left part of banner
11647 if ($morehtmlleft) {
11648 if ($conf->browser->layout == 'phone') {
11649 $ret .= '<!-- morehtmlleft --><div class="floatleft">' . $morehtmlleft . '</div>';
11650 } else {
11651 $ret .= '<!-- morehtmlleft --><div class="inline-block floatleft">' . $morehtmlleft . '</div>';
11652 }
11653 }
11654
11655 //if ($conf->browser->layout == 'phone') $ret.='<div class="clearboth"></div>';
11656 $ret .= '<!-- Ref or ID --><div class="inline-block floatleft valignmiddle maxwidth750 marginbottomonly refid' . (($shownav && ($previous_ref || $next_ref)) ? ' refidpadding' : '') . '">';
11657
11658 // For thirdparty, contact, user, member, the ref is the id, so we show something else
11659 if ($object->element == 'societe') {
11660 $ret .= '<span class="valignmiddle">'.dolPrintHTML((string) $object->name).'</span>';
11661
11662 // List of extra languages
11663 $arrayoflangcode = array();
11664 if (getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE')) {
11665 $arrayoflangcode[] = getDolGlobalString('PDF_USE_ALSO_LANGUAGE_CODE');
11666 }
11667
11668 if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
11669 if (!is_object($extralanguages)) {
11670 include_once DOL_DOCUMENT_ROOT . '/core/class/extralanguages.class.php';
11671 $extralanguages = new ExtraLanguages($this->db);
11672 }
11673 $extralanguages->fetch_name_extralanguages('societe');
11674
11675 // Guard against PHP 8 'Undefined array key' when MAIN_USE_ALTERNATE_TRANSLATION_FOR
11676 // is not configured and fetch_name_extralanguages() leaves attributes empty (issue #34596).
11677 if (!empty($extralanguages->attributes['societe']) && !empty($extralanguages->attributes['societe']['name'])) {
11678 $object->fetchValuesForExtraLanguages();
11679
11680 $htmltext = '';
11681 // If there is extra languages
11682 foreach ($arrayoflangcode as $extralangcode) {
11683 $htmltext .= picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
11684 if ($object->array_languages['name'][$extralangcode]) {
11685 $htmltext .= $object->array_languages['name'][$extralangcode];
11686 } else {
11687 $htmltext .= '<span class="opacitymedium">' . $langs->trans("SwitchInEditModeToAddTranslation") . '</span>';
11688 }
11689 }
11690 $ret .= '<!-- Show translations of name -->' . "\n";
11691 $ret .= $this->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
11692 }
11693 }
11694 } elseif ($object->element == 'member') {
11695 '@phan-var-force Adherent $object';
11696 $ret .= $object->ref . '<br>';
11697 $fullname = $object->getFullName($langs);
11698 if ($object->morphy == 'mor' && $object->societe) {
11699 $ret .= '<span class="valignmiddle">'.dolPrintHTML((string) $object->societe) . ((!empty($fullname) && $object->societe != $fullname) ? ' (' . dol_htmlentities($fullname) . $addgendertxt . ')' : '').'</span>';
11700 } else {
11701 $ret .= '<span class="valignmiddle">'.dolPrintHTML($fullname) . $addgendertxt . ((!empty($object->societe) && $object->societe != $fullname) ? ' (' . dol_htmlentities((string) $object->societe) . ')' : '').'</span>';
11702 }
11703 } elseif (in_array($object->element, array('contact', 'user'))) {
11704 $ret .= '<span class="valignmiddle">'.dolPrintHTML($object->getFullName($langs)).'</span>'.$addgendertxt;
11705 } elseif ($object->element == 'usergroup') {
11706 $ret .= dol_htmlentities((string) $object->name);
11707 } elseif (in_array($object->element, array('action', 'agenda'))) {
11708 '@phan-var-force ActionComm $object';
11709 $ret .= $object->ref . '<br>' . $object->label;
11710 } elseif (in_array($object->element, array('adherent_type'))) {
11711 $ret .= $object->label;
11712 } elseif ($object->element == 'ecm_directories') {
11713 $ret .= '';
11714 } elseif ($object->element == 'accountingbookkeeping' && !empty($object->context['mode']) && $object->context['mode'] == '_tmp') {
11715 $ret .= '<span class="valignmiddle">'.$langs->trans("Draft").'</span>';
11716 } elseif ($object instanceof Ticket) {
11717 '@phan-var-force Ticket $object';
11718 $ret .= '<span class="valignmiddle">'.dolPrintHTML(!empty($object->$fieldref) ? $object->$fieldref : "").'</span>';
11719 $ret .= ' &nbsp; <span class="nobold small" title="'.dolPrintHTMLForAttribute($langs->trans("TicketTrackId")).'">('.$object->track_id.')</span>';
11720 } elseif ($fieldref != 'none') {
11721 // Generic case
11722 $ret .= '<span class="valignmiddle">'.dolPrintHTML(!empty($object->$fieldref) ? $object->$fieldref : "").'</span>';
11723 }
11724 if ($morehtmlref) {
11725 // don't add a additional space, when "$morehtmlref" starts with a HTML div tag
11726 if (substr($morehtmlref, 0, 4) != '<div') {
11727 $ret .= ' ';
11728 }
11729
11730 $ret .= '<!-- morehtmlref -->'.$morehtmlref;
11731 }
11732
11733 $ret .= '</div>';
11734
11735 $ret .= '</div><!-- End banner content -->';
11736
11737 return $ret;
11738 }
11739
11740
11749 public function showbarcode(&$object, $width = 100, $morecss = '')
11750 {
11751 //Check if barcode is filled in the card
11752 if (empty($object->barcode)) {
11753 return '';
11754 }
11755
11756 // Complete object if not complete
11757 if (empty($object->barcode_type_code) || empty($object->barcode_type_coder)) {
11758 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
11759 $result = $object->fetchBarCode();
11760 //Check if fetchBarCode() failed
11761 if ($result < 1) {
11762 return '<!-- ErrorFetchBarcode -->';
11763 }
11764 }
11765
11766 // Barcode image @phan-suppress-next-line PhanUndeclaredProperty
11767 $url = DOL_URL_ROOT . '/viewimage.php?modulepart=barcode&generator=' . urlencode($object->barcode_type_coder) . '&code=' . urlencode($object->barcode) . '&encoding=' . urlencode($object->barcode_type_code);
11768 $out = '<!-- url barcode = ' . $url . ' -->';
11769 $out .= '<img src="' . $url . '"' . ($morecss ? ' class="' . $morecss . '"' : '') . '>';
11770
11771 return $out;
11772 }
11773
11792 public static function showphoto($modulepart, $object, $width = 100, $height = 0, $caneditfield = 0, $cssclass = 'photowithmargin', $imagesize = '', $addlinktofullsize = 1, $cache = 0, $forcecapture = '', $noexternsourceoverwrite = 0, $usesharelinkifavailable = 0)
11793 {
11794 global $conf, $db, $langs;
11795
11796 $entity = (empty($object->entity) ? $conf->entity : $object->entity);
11797 $id = (empty($object->id) ? $object->rowid : $object->id); // @phan-suppress-current-line PhanUndeclaredProperty (->rowid)
11798
11799 $dir = '';
11800 $file = '';
11801 $originalfile = '';
11802 $altfile = '';
11803 $email = '';
11804 $capture = '';
11805 if ($modulepart == 'societe') {
11806 $dir = $conf->societe->multidir_output[$entity];
11807 if (!empty($object->logo)) {
11808 if (dolIsAllowedForPreview($object->logo)) {
11809 if ((string) $imagesize == 'mini') {
11810 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_mini'); // getImageFileNameForSize include the thumbs
11811 } elseif ((string) $imagesize == 'small') {
11812 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . getImageFileNameForSize($object->logo, '_small');
11813 } else {
11814 $file = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
11815 }
11816 $originalfile = get_exdir(0, 0, 0, 0, $object, 'thirdparty') . 'logos/' . $object->logo;
11817 }
11818 }
11819 $email = $object->email;
11820 } elseif ($modulepart == 'contact') {
11821 $dir = $conf->societe->multidir_output[$entity] . '/contact';
11822 $photo = $object->photo; // Copy to help static analysis
11823 if (!empty($photo)) {
11824 if (dolIsAllowedForPreview($photo)) {
11825 if ((string) $imagesize == 'mini') {
11826 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11827 } elseif ((string) $imagesize == 'small') {
11828 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . getImageFileNameForSize($photo, '_small');
11829 } else {
11830 $file = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $photo;
11831 }
11832 $originalfile = get_exdir(0, 0, 0, 0, $object, 'contact') . 'photos/' . $photo;
11833 }
11834 }
11835 $email = $object->email;
11836 $capture = 'user';
11837 } elseif ($modulepart == 'userphoto') {
11838 $dir = $conf->user->dir_output;
11839 $photo = $object->photo; // Copy to help static analysis
11840 if (!empty($photo)) {
11841 if (dolIsAllowedForPreview($photo)) {
11842 if ((string) $imagesize == 'mini') {
11843 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11844 } elseif ((string) $imagesize == 'small') {
11845 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . getImageFileNameForSize($photo, '_small');
11846 } else {
11847 $file = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $photo;
11848 }
11849 $originalfile = get_exdir(0, 0, 0, 0, $object, 'user') . 'photos/' . $photo;
11850 }
11851 }
11852 if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {
11853 $altfile = $object->id . ".jpg"; // For backward compatibility
11854 }
11855 $email = $object->email;
11856 $capture = 'user';
11857 } elseif ($modulepart == 'memberphoto') {
11858 $dir = $conf->member->dir_output;
11859 $photo = $object->photo; // Copy to help static analysis
11860 if (!empty($photo)) {
11861 if (dolIsAllowedForPreview($photo)) {
11862 if ((string) $imagesize == 'mini') {
11863 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($photo, '_mini');
11864 } elseif ((string) $imagesize == 'small') {
11865 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . getImageFileNameForSize($photo, '_small');
11866 } else {
11867 $file = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $photo;
11868 }
11869 $originalfile = get_exdir(0, 0, 0, 0, $object, 'member') . 'photos/' . $photo;
11870 }
11871 }
11872 if (getDolGlobalString('MAIN_OLD_IMAGE_LINKS')) {
11873 $altfile = $object->id . ".jpg"; // For backward compatibility
11874 }
11875 $email = $object->email;
11876 $capture = 'user';
11877 } else {
11878 // Generic case to show photos
11879 // TODO Implement this method in previous objects so we can always use this generic method.
11880 if ($modulepart != "unknown" && method_exists($object, 'getDataToShowPhoto')) {
11881 $tmpdata = $object->getDataToShowPhoto($modulepart, $imagesize);
11882
11883 $dir = $tmpdata['dir'];
11884 $file = $tmpdata['file'];
11885 $originalfile = $tmpdata['originalfile'];
11886 $altfile = $tmpdata['altfile'];
11887 $email = $tmpdata['email'];
11888 $capture = $tmpdata['capture'];
11889 }
11890 }
11891
11892 if ($forcecapture) {
11893 $capture = $forcecapture;
11894 }
11895
11896 $ret = '';
11897
11898 if ($dir) {
11899 if ($file && file_exists($dir . "/" . $file)) {
11900 if ($addlinktofullsize) {
11901 $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
11902 if ($urladvanced) {
11903 $ret .= '<a href="' . $urladvanced . '">';
11904 } else {
11905 $ret .= '<a href="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
11906 }
11907 }
11908
11909 $sharekey = '';
11910 if ($usesharelinkifavailable) {
11911 // $dir is a full path '/home/.../dolibarr_documents/module'
11912 $relativefileforecm = preg_replace('/^'.preg_quote(DOL_DATA_ROOT.'/', '/').'/', '', $dir.'/'.$originalfile);
11913 // $relativefileforecme = 'module/...'
11914 require_once DOL_DOCUMENT_ROOT . '/ecm/class/ecmfiles.class.php';
11915 $ecmfiles = new EcmFiles($db);
11916 $ecmfiles->fetch(0, '', $relativefileforecm);
11917
11918 $sharekey = (string) $ecmfiles->share;
11919 }
11920
11921 if (!empty($sharekey)) {
11922 $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) . '">';
11923 } else {
11924 $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) . '">';
11925 }
11926 if ($addlinktofullsize) {
11927 $ret .= '</a>';
11928 }
11929 } elseif ($altfile && file_exists($dir . "/" . $altfile)) {
11930 if ($addlinktofullsize) {
11931 $urladvanced = getAdvancedPreviewUrl($modulepart, $originalfile, 0, '&entity=' . $entity);
11932 if ($urladvanced) {
11933 $ret .= '<a href="' . $urladvanced . '">';
11934 } else {
11935 $ret .= '<a href="' . DOL_URL_ROOT . '/viewimage.php?modulepart=' . $modulepart . '&entity=' . $entity . '&file=' . urlencode($originalfile) . '&cache=' . $cache . '">';
11936 }
11937 }
11938 $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) . '">';
11939 if ($addlinktofullsize) {
11940 $ret .= '</a>';
11941 }
11942 } else {
11943 $nophoto = '/public/theme/common/nophoto.png';
11944 $defaultimg = 'identicon'; // For gravatar
11945 if (in_array($modulepart, array('societe', 'userphoto', 'contact', 'memberphoto'))) { // For modules that need a special image when photo not found
11946 if ($modulepart == 'societe' || ($modulepart == 'memberphoto' && !empty($object->morphy) && strpos($object->morphy, 'mor') !== false)) {
11947 $nophoto = 'company';
11948 } else {
11949 $nophoto = '/public/theme/common/user_anonymous.png';
11950 if (!empty($object->gender) && $object->gender == 'man') {
11951 $nophoto = '/public/theme/common/user_man.png';
11952 }
11953 if (!empty($object->gender) && $object->gender == 'woman') {
11954 $nophoto = '/public/theme/common/user_woman.png';
11955 }
11956 }
11957 }
11958
11959 if (isModEnabled('gravatar') && $email && empty($noexternsourceoverwrite)) {
11960 // see https://gravatar.com/site/implement/images/php/
11961 $ret .= '<!-- Put link to gravatar -->';
11962 $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
11963 } else {
11964 if ($nophoto == 'company') {
11965 $ret .= '<div class="divforspanimg valignmiddle inline-block center photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . '>' . img_picto('', 'company') . '</div>';
11966 //$ret .= '<div class="difforspanimgright"></div>';
11967 } else {
11968 $ret .= '<img class="photo' . $modulepart . ($cssclass ? ' ' . $cssclass : '') . '" alt="" ' . ($width ? ' width="' . $width . '"' : '') . ($height ? ' height="' . $height . '"' : '') . ' src="' . DOL_URL_ROOT . $nophoto . '">';
11969 }
11970 }
11971 }
11972
11973 if ($caneditfield) {
11974 if ($object->photo) {
11975 $ret .= "<br>\n";
11976 }
11977 $ret .= '<table class="nobordernopadding centpercent">';
11978 if ($object->photo) {
11979 $ret .= '<tr><td><input type="checkbox" class="flat photodelete" name="deletephoto" id="photodelete"> <label for="photodelete">' . $langs->trans("Delete") . '</label><br><br></td></tr>';
11980 }
11981 $ret .= '<tr><td class="tdoverflow">';
11982 $maxfilesizearray = getMaxFileSizeArray();
11983 $maxmin = $maxfilesizearray['maxmin'];
11984 if ($maxmin > 0) {
11985 $ret .= '<input type="hidden" name="MAX_FILE_SIZE" value="' . ($maxmin * 1024) . '">'; // MAX_FILE_SIZE must precede the field type=file
11986 }
11987 $ret .= '<input type="file" class="flat maxwidth200onsmartphone" name="photo" id="photoinput" accept="image/*"' . ($capture ? ' capture="' . dolPrintHTMLForAttribute($capture) . '"' : '') . '>';
11988 $ret .= '</td></tr>';
11989 $ret .= '</table>';
11990 }
11991 }
11992
11993 return $ret;
11994 }
11995
11996 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
11997
12014 public function select_dolgroups($selected = 0, $htmlname = 'groupid', $show_empty = 0, $exclude = '', $disabled = 0, $include = '', $enableonly = array(), $force_entity = '0', $multiple = false, $morecss = 'minwidth200')
12015 {
12016 // phpcs:enable
12017 global $conf, $user, $langs;
12018
12019 // Allow excluding groups
12020 $excludeGroups = null;
12021 if (is_array($exclude)) {
12022 $excludeGroups = implode(",", $exclude);
12023 }
12024 // Allow including groups
12025 $includeGroups = null;
12026 if (is_array($include)) {
12027 $includeGroups = implode(",", $include);
12028 }
12029
12030 if (!is_array($selected)) {
12031 $selected = array($selected);
12032 }
12033
12034 $out = '';
12035
12036 // Build sql to search groups
12037 $sql = "SELECT ug.rowid, ug.nom as name";
12038 if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
12039 $sql .= ", e.label";
12040 }
12041 $sql .= " FROM " . $this->db->prefix() . "usergroup as ug ";
12042 if (isModEnabled('multicompany') && $conf->entity == 1 && $user->admin && !$user->entity) {
12043 $sql .= " LEFT JOIN " . $this->db->prefix() . "entity as e ON e.rowid=ug.entity";
12044 if ($force_entity) {
12045 $sql .= " WHERE ug.entity IN (0, " . ((int) $force_entity) . ")";
12046 } else {
12047 $sql .= " WHERE ug.entity IS NOT NULL";
12048 }
12049 } else {
12050 $sql .= " WHERE ug.entity IN (0, " . ((int) $conf->entity) . ")";
12051 }
12052 if (is_array($exclude) && $excludeGroups) {
12053 $sql .= " AND ug.rowid NOT IN (" . $this->db->sanitize($excludeGroups) . ")";
12054 }
12055 if (is_array($include) && $includeGroups) {
12056 $sql .= " AND ug.rowid IN (" . $this->db->sanitize($includeGroups) . ")";
12057 }
12058 $sql .= " ORDER BY ug.nom ASC";
12059
12060 dol_syslog(get_class($this) . "::select_dolgroups", LOG_DEBUG);
12061 $resql = $this->db->query($sql);
12062 if ($resql) {
12063 // Enhance with select2
12064 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12065
12066 $out .= '<select class="flat' . ($morecss ? ' ' . $morecss : '') . '" id="' . $htmlname . '" name="' . $htmlname . ($multiple ? '[]' : '') . '" ' . ($multiple ? 'multiple' : '') . ' ' . ($disabled ? ' disabled' : '') . '>';
12067
12068 $num = $this->db->num_rows($resql);
12069 $i = 0;
12070 if ($num) {
12071 if ($show_empty && !$multiple) {
12072 $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '>&nbsp;</option>' . "\n";
12073 }
12074
12075 while ($i < $num) {
12076 $obj = $this->db->fetch_object($resql);
12077 $disableline = 0;
12078 if (is_array($enableonly) && count($enableonly) && !in_array($obj->rowid, $enableonly)) {
12079 $disableline = 1;
12080 }
12081
12082 $label = $obj->name;
12083 $labelhtml = $obj->name;
12084 if (isModEnabled('multicompany') && !getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE') && $conf->entity == 1) {
12085 $label .= " (" . $obj->label . ")";
12086 $labelhtml .= ' <span class="opacitymedium">(' . $obj->label . ')</span>';
12087 }
12088
12089 $out .= '<option value="' . $obj->rowid . '"';
12090 if ($disableline) {
12091 $out .= ' disabled';
12092 }
12093 if ((isset($selected[0]) && is_object($selected[0]) && $selected[0]->id == $obj->rowid)
12094 || ((!isset($selected[0]) || !is_object($selected[0])) && !empty($selected) && in_array($obj->rowid, $selected))) {
12095 $out .= ' selected';
12096 }
12097 $out .= ' data-html="'.dol_escape_htmltag($labelhtml).'"';
12098 $out .= '>';
12099 $out .= $label;
12100 $out .= '</option>';
12101 $i++;
12102 }
12103 } else {
12104 if ($show_empty) {
12105 $out .= '<option value="-1"' . (in_array(-1, $selected) ? ' selected' : '') . '></option>' . "\n";
12106 }
12107 $out .= '<option value="" disabled>' . $langs->trans("NoUserGroupDefined") . '</option>';
12108 }
12109 $out .= '</select>';
12110
12111 $out .= ajax_combobox($htmlname);
12112 } else {
12113 dol_print_error($this->db);
12114 }
12115
12116 return $out;
12117 }
12118
12119
12126 public function showFilterButtons($pos = '')
12127 {
12128 $out = '<div class="nowraponall">';
12129 $out .= '<button type="submit" class="liste_titre button_search reposition" name="button_search_x" value="x"><span class="fas fa-search"></span></button>';
12130 $out .= '<button type="submit" class="liste_titre button_removefilter reposition" name="button_removefilter_x" value="x"><span class="fas fa-times"></span></button>';
12131 $out .= '</div>';
12132
12133 return $out;
12134 }
12135
12144 public function showCheckAddButtons($cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
12145 {
12146 global $conf;
12147
12148 $out = '';
12149
12150 if (!empty($conf->use_javascript_ajax)) {
12151 $out .= '<div class="inline-block checkallactions"><input type="checkbox" id="' . $cssclass . 's" name="' . $cssclass . 's" class="checkallactions"></div>';
12152 }
12153 $out .= '<script nonce="' . getNonce() . '">
12154 $(document).ready(function() {
12155 $("#' . $cssclass . 's").click(function() {
12156 if($(this).is(\':checked\')){
12157 console.log("We check all ' . $cssclass . ' and trigger the change method");
12158 $(".' . $cssclass . '").prop(\'checked\', true).trigger(\'change\');
12159 }
12160 else
12161 {
12162 console.log("We uncheck all");
12163 $(".' . $cssclass . '").prop(\'checked\', false).trigger(\'change\');
12164 }' . "\n";
12165 if ($calljsfunction) {
12166 $out .= 'if (typeof initCheckForSelect == \'function\') { initCheckForSelect(0, "' . $massactionname . '", "' . $cssclass . '"); } else { console.log("No function initCheckForSelect found. Call won\'t be done."); }';
12167 }
12168 $out .= ' });
12169/*
12170 $(".' . $cssclass . '").change(function() {
12171 console.log("We check and change the tr class highlight after a change on .'.$cssclass.'");
12172 var $row = $(this).closest("tr");
12173 if ($row.length) {
12174 var anyChecked = $row.find(\'input[type="checkbox"].checkforselect:checked\').length > 0;
12175 console.log("anychecked="+anyChecked);
12176 if (!anyChecked) {
12177 $row.removeClass("highlight");
12178 } else {
12179 $row.addClass("highlight");
12180 }
12181 }
12182 });
12183*/
12184 });
12185 </script>';
12186
12187 return $out;
12188 }
12189
12199 public function showFilterAndCheckAddButtons($addcheckuncheckall = 0, $cssclass = 'checkforaction', $calljsfunction = 0, $massactionname = "massaction")
12200 {
12201 $out = $this->showFilterButtons();
12202 if ($addcheckuncheckall) {
12203 $out .= $this->showCheckAddButtons($cssclass, $calljsfunction, $massactionname);
12204 }
12205 return $out;
12206 }
12207
12221 public function selectExpenseCategories($selected = '', $htmlname = 'fk_c_exp_tax_cat', $useempty = 0, $excludeid = array(), $target = '', $default_selected = 0, $params = array(), $info_admin = 1)
12222 {
12223 global $langs, $user;
12224
12225 $out = '';
12226 $sql = "SELECT rowid, label FROM " . $this->db->prefix() . "c_exp_tax_cat WHERE active = 1";
12227 $sql .= " AND entity IN (0," . getEntity('exp_tax_cat') . ")";
12228 if (!empty($excludeid)) {
12229 $sql .= " AND rowid NOT IN (" . $this->db->sanitize(implode(',', $excludeid)) . ")";
12230 }
12231 $sql .= " ORDER BY label";
12232
12233 $resql = $this->db->query($sql);
12234 if ($resql) {
12235 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp maxwidth200">';
12236 if ($useempty) {
12237 $out .= '<option value="0">&nbsp;</option>';
12238 }
12239
12240 while ($obj = $this->db->fetch_object($resql)) {
12241 $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . $langs->trans($obj->label) . '</option>';
12242 }
12243 $out .= '</select>';
12244 $out .= ajax_combobox('select_' . $htmlname);
12245
12246 if (!empty($htmlname) && $user->admin && $info_admin) {
12247 $out .= ' ' . info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
12248 }
12249
12250 if (!empty($target)) {
12251 $sql = "SELECT c.id FROM " . $this->db->prefix() . "c_type_fees as c WHERE c.code = 'EX_KME' AND c.active = 1";
12252 $resql = $this->db->query($sql);
12253 if ($resql) {
12254 if ($this->db->num_rows($resql) > 0) {
12255 $obj = $this->db->fetch_object($resql);
12256 $out .= '<script nonce="' . getNonce() . '">
12257 $(function() {
12258 $("select[name=' . $target . ']").on("change", function() {
12259 var current_val = $(this).val();
12260 if (current_val == ' . $obj->id . ') {';
12261 if (!empty($default_selected) || !empty($selected)) {
12262 $out .= '$("select[name=' . $htmlname . ']").val("' . ($default_selected > 0 ? $default_selected : $selected) . '");';
12263 }
12264
12265 $out .= '
12266 $("select[name=' . $htmlname . ']").change();
12267 }
12268 });
12269
12270 $("select[name=' . $htmlname . ']").change(function() {
12271
12272 if ($("select[name=' . $target . ']").val() == ' . $obj->id . ') {
12273 // get price of kilometer to fill the unit price
12274 $.ajax({
12275 method: "POST",
12276 dataType: "json",
12277 data: { fk_c_exp_tax_cat: $(this).val(), token: \'' . currentToken() . '\' },
12278 url: "' . (DOL_URL_ROOT . '/expensereport/ajax/ajaxik.php?' . implode('&', $params)) . '",
12279 }).done(function( data, textStatus, jqXHR ) {
12280 console.log(data);
12281 if (typeof data.up != "undefined") {
12282 $("input[name=value_unit]").val(data.up);
12283 $("select[name=' . $htmlname . ']").attr("title", data.title);
12284 } else {
12285 $("input[name=value_unit]").val("");
12286 $("select[name=' . $htmlname . ']").attr("title", "");
12287 }
12288 });
12289 }
12290 });
12291 });
12292 </script>';
12293 }
12294 }
12295 }
12296 } else {
12297 dol_print_error($this->db);
12298 }
12299
12300 return $out;
12301 }
12302
12311 public function selectExpenseRanges($selected = '', $htmlname = 'fk_range', $useempty = 0)
12312 {
12313 global $conf, $langs;
12314
12315 $out = '';
12316 $sql = "SELECT rowid, range_ik FROM " . $this->db->prefix() . "c_exp_tax_range";
12317 $sql .= " WHERE entity = " . ((int) $conf->entity) . " AND active = 1";
12318
12319 $resql = $this->db->query($sql);
12320 if ($resql) {
12321 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
12322 if ($useempty) {
12323 $out .= '<option value="0"></option>';
12324 }
12325
12326 while ($obj = $this->db->fetch_object($resql)) {
12327 $out .= '<option ' . ($selected == $obj->rowid ? 'selected="selected"' : '') . ' value="' . $obj->rowid . '">' . price($obj->range_ik, 0, $langs, 1, 0) . '</option>';
12328 }
12329 $out .= '</select>';
12330 } else {
12331 dol_print_error($this->db);
12332 }
12333
12334 return $out;
12335 }
12336
12347 public function selectExpenseFees($selected = '', $htmlname = 'fk_c_type_fees', $useempty = 0, $allchoice = 1, $useid = 0)
12348 {
12349 global $langs;
12350
12351 $out = '';
12352 $sql = "SELECT id, code, label";
12353 $sql .= " FROM ".$this->db->prefix()."c_type_fees";
12354 $sql .= " WHERE active = 1";
12355
12356 $resql = $this->db->query($sql);
12357 if ($resql) {
12358 $out = '<select id="select_' . $htmlname . '" name="' . $htmlname . '" class="' . $htmlname . ' flat minwidth75imp">';
12359 if ($useempty) {
12360 $out .= '<option value="0"></option>';
12361 }
12362 if ($allchoice) {
12363 $out .= '<option value="-1">' . $langs->trans('AllExpenseReport') . '</option>';
12364 }
12365
12366 $field = 'code';
12367 if ($useid) {
12368 $field = 'id';
12369 }
12370
12371 while ($obj = $this->db->fetch_object($resql)) {
12372 $key = $langs->trans($obj->code);
12373 $out .= '<option ' . ($selected == $obj->{$field} ? 'selected="selected"' : '') . ' value="' . $obj->{$field} . '">' . ($key != $obj->code ? $key : $obj->label) . '</option>';
12374 }
12375 $out .= '</select>';
12376
12377 $out .= ajax_combobox('select_'.$htmlname);
12378 } else {
12379 dol_print_error($this->db);
12380 }
12381
12382 return $out;
12383 }
12384
12403 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)
12404 {
12405 global $user, $conf, $langs;
12406
12407 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
12408
12409 if (is_null($usertofilter)) {
12410 $usertofilter = $user;
12411 }
12412
12413 $out = '';
12414
12415 $hideunselectables = false;
12416 if (getDolGlobalString('INVOICE_HIDE_UNSELECTABLES')) {
12417 $hideunselectables = true;
12418 }
12419
12420 if (empty($projectsListId)) {
12421 if (!$usertofilter->hasRight('projet', 'all', 'lire')) {
12422 $projectstatic = new Project($this->db);
12423 $projectsListId = $projectstatic->getProjectsAuthorizedForUser($usertofilter, 0, 1);
12424 }
12425 }
12426
12427 // Search all projects
12428 $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";
12429 $sql .= " FROM " . $this->db->prefix() . "facture as f";
12430 $sql .= " INNER JOIN " . $this->db->prefix() . "projet as p ON p.entity IN (" . getEntity('project') . ") AND f.fk_projet = p.rowid";
12431 $sql .= " LEFT JOIN " . $this->db->prefix() . "societe as s ON s.rowid = p.fk_soc";
12432 $sql .= " WHERE f.fk_statut = 0"; // Draft invoices only
12433 //if ($projectsListId) $sql.= " AND p.rowid IN (".$this->db->sanitize($projectsListId).")";
12434 //if ($socid == 0) $sql.= " AND (p.fk_soc=0 OR p.fk_soc IS NULL)";
12435 //if ($socid > 0) $sql.= " AND (p.fk_soc=".((int) $socid)." OR p.fk_soc IS NULL)";
12436 $sql .= " ORDER BY p.ref, f.ref ASC";
12437
12438 $resql = $this->db->query($sql);
12439 if ($resql) {
12440 // Use select2 selector
12441 if (!empty($conf->use_javascript_ajax)) {
12442 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12443 $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus);
12444 $out .= $comboenhancement;
12445 $morecss = 'minwidth200imp maxwidth500';
12446 }
12447
12448 if (empty($option_only)) {
12449 $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
12450 }
12451 if (!empty($show_empty)) {
12452 $out .= '<option value="0" class="optiongrey">';
12453 if (!is_numeric($show_empty)) {
12454 $out .= $show_empty;
12455 } else {
12456 $out .= '&nbsp;';
12457 }
12458 $out .= '</option>';
12459 }
12460 $num = $this->db->num_rows($resql);
12461 $i = 0;
12462 if ($num) {
12463 while ($i < $num) {
12464 $obj = $this->db->fetch_object($resql);
12465 // 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.
12466 if ($socid > 0 && (empty($obj->fk_soc) || $obj->fk_soc == $socid) && !$usertofilter->hasRight('societe', 'lire')) {
12467 // Do nothing
12468 } else {
12469 if ($discard_closed == 1 && $obj->fk_statut == Project::STATUS_CLOSED) {
12470 $i++;
12471 continue;
12472 }
12473
12474 $labeltoshow = '';
12475
12476 if ($showproject == 'all') {
12477 $labeltoshow .= dol_trunc($obj->ref, 18); // Invoice ref
12478 if ($obj->name) {
12479 $labeltoshow .= ' - ' . $obj->name; // Soc name
12480 }
12481
12482 $disabled = 0;
12483 if ($obj->fk_statut == Project::STATUS_DRAFT) {
12484 $disabled = 1;
12485 $labeltoshow .= ' - ' . $langs->trans("Draft");
12486 } elseif ($obj->fk_statut == Project::STATUS_CLOSED) {
12487 if ($discard_closed == 2) {
12488 $disabled = 1;
12489 }
12490 $labeltoshow .= ' - ' . $langs->trans("Closed");
12491 } elseif ($socid > 0 && (!empty($obj->fk_soc) && $obj->fk_soc != $socid)) {
12492 $disabled = 1;
12493 $labeltoshow .= ' - ' . $langs->trans("LinkedToAnotherCompany");
12494 }
12495 }
12496
12497 if (!empty($selected) && $selected == $obj->rowid) {
12498 $out .= '<option value="' . $obj->rowid . '" selected';
12499 //if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
12500 $out .= '>' . $labeltoshow . '</option>';
12501 } else {
12502 if ($hideunselectables && $disabled && ($selected != $obj->rowid)) {
12503 $resultat = '';
12504 } else {
12505 $resultat = '<option value="' . $obj->rowid . '"';
12506 if ($disabled) {
12507 $resultat .= ' disabled';
12508 }
12509 //if ($obj->public) $labeltoshow.=' ('.$langs->trans("Public").')';
12510 //else $labeltoshow.=' ('.$langs->trans("Private").')';
12511 $resultat .= '>';
12512 $resultat .= $labeltoshow;
12513 $resultat .= '</option>';
12514 }
12515 $out .= $resultat;
12516 }
12517 }
12518 $i++;
12519 }
12520 }
12521 if (empty($option_only)) {
12522 $out .= '</select>';
12523 }
12524
12525 $this->db->free($resql);
12526
12527 return $out;
12528 } else {
12529 dol_print_error($this->db);
12530 return '';
12531 }
12532 }
12533
12548 public function selectInvoiceRec($selected = '', $htmlname = 'facrecid', $maxlength = 24, $option_only = 0, $show_empty = '1', $forcefocus = 0, $disabled = 0, $morecss = 'maxwidth500')
12549 {
12550 global $conf, $langs;
12551
12552 $out = '';
12553
12554 dol_syslog('FactureRec::fetch', LOG_DEBUG);
12555
12556 $sql = 'SELECT f.rowid, f.entity, f.titre as title, f.suspended, f.fk_soc';
12557 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_rec as f';
12558 $sql .= " WHERE f.entity IN (" . getEntity('invoice') . ")";
12559 $sql .= " ORDER BY f.titre ASC";
12560
12561 $resql = $this->db->query($sql);
12562 if ($resql) {
12563 // Use select2 selector
12564 if (!empty($conf->use_javascript_ajax)) {
12565 include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
12566 $comboenhancement = ajax_combobox($htmlname, array(), 0, $forcefocus);
12567 $out .= $comboenhancement;
12568 $morecss = 'minwidth200imp maxwidth500';
12569 }
12570
12571 if (empty($option_only)) {
12572 $out .= '<select class="valignmiddle flat' . ($morecss ? ' ' . $morecss : '') . '"' . ($disabled ? ' disabled="disabled"' : '') . ' id="' . $htmlname . '" name="' . $htmlname . '">';
12573 }
12574 if (!empty($show_empty)) {
12575 $out .= '<option value="0" class="optiongrey">';
12576 if (!is_numeric($show_empty)) {
12577 $out .= $show_empty;
12578 } else {
12579 $out .= '&nbsp;';
12580 }
12581 $out .= '</option>';
12582 }
12583 $num = $this->db->num_rows($resql);
12584 if ($num) {
12585 while ($obj = $this->db->fetch_object($resql)) {
12586 $labeltoshow = dol_trunc($obj->title, 18); // Invoice ref
12587
12588 $disabled = 0;
12589 if (!empty($obj->suspended)) {
12590 $disabled = 1;
12591 $labeltoshow .= ' - ' . $langs->trans("Closed");
12592 }
12593
12594
12595 if (!empty($selected) && $selected == $obj->rowid) {
12596 $out .= '<option value="' . $obj->rowid . '" selected';
12597 //if ($disabled) $out.=' disabled'; // with select2, field can't be preselected if disabled
12598 $out .= '>' . $labeltoshow . '</option>';
12599 } else {
12600 if ($disabled && ($selected != $obj->rowid)) {
12601 $resultat = '';
12602 } else {
12603 $resultat = '<option value="' . $obj->rowid . '"';
12604 if ($disabled) {
12605 $resultat .= ' disabled';
12606 }
12607 $resultat .= '>';
12608 $resultat .= $labeltoshow;
12609 $resultat .= '</option>';
12610 }
12611 $out .= $resultat;
12612 }
12613 }
12614 }
12615 if (empty($option_only)) {
12616 $out .= '</select>';
12617 }
12618
12619 print $out;
12620
12621 $this->db->free($resql);
12622 return $num;
12623 } else {
12624 $this->errors[] = $this->db->lasterror;
12625 return -1;
12626 }
12627 }
12628
12629
12640 public function searchComponent($arrayofcriterias, $search_component_params, $arrayofinputfieldsalreadyoutput = array(), $search_component_params_hidden = '', $arrayoffiltercriterias = array())
12641 {
12642 // TODO: Use $arrayoffiltercriterias param instead of $arrayofcriterias to include linked object fields in search
12643 global $langs, $form;
12644
12645 //require_once DOL_DOCUMENT_ROOT."/core/class/html.formother.class.php";
12646 //$formother = new FormOther($this->db);
12647
12648 if ($search_component_params_hidden != '' && !preg_match('/^\‍(.*\‍)$/', $search_component_params_hidden)) { // If $search_component_params_hidden does not start and end with ()
12649 $search_component_params_hidden = '(' . $search_component_params_hidden . ')';
12650 }
12651
12652 $ret = '<!-- searchComponent -->';
12653
12654 $ret .= '<div class="divadvancedsearchfieldcomp centpercent inline-block">';
12655 $ret .= '<a href="#" class="dropdownsearch-toggle unsetcolor">';
12656 $ret .= '<span class="fas fa-filter linkobject boxfilter paddingright pictofixedwidth" title="' . dol_escape_htmltag($langs->trans("Filters")) . '" id="idsubimgproductdistribution"></span>';
12657 $ret .= '</a>';
12658
12659 $ret .= '<div class="divadvancedsearchfieldcompinput inline-block minwidth500 maxwidth300onsmartphone">';
12660
12661 // Show select fields as tags.
12662 $ret .= '<div id="divsearch_component_params" name="divsearch_component_params" class="noborderbottom search_component_params inline-block valignmiddle">';
12663
12664 if ($search_component_params_hidden) {
12665 // Split the criteria on each AND
12666 //var_dump($search_component_params_hidden);
12667
12668 $arrayofandtags = dolForgeExplodeAnd($search_component_params_hidden);
12669
12670 // $arrayofandtags is now array( '...' , '...', ...)
12671 // Show each AND part
12672 foreach ($arrayofandtags as $tmpkey => $tmpval) {
12673 $errormessage = '';
12674 $searchtags = forgeSQLFromUniversalSearchCriteria($tmpval, $errormessage, 1, 1);
12675 if ($errormessage) {
12676 $this->error = 'ERROR in parsing search string: '.$errormessage;
12677 }
12678 // Remove first and last parenthesis but only if first is the opening and last the closing of the same group
12679 include_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
12680 $searchtags = removeGlobalParenthesis($searchtags);
12681
12682 $ret .= '<span class="marginleftonlyshort valignmiddle tagsearch" data-ufilterid="'.($tmpkey + 1).'" data-ufilter="'.dol_escape_htmltag($tmpval).'">';
12683 $ret .= '<span class="tagsearchdelete select2-selection__choice__remove" data-ufilterid="'.($tmpkey + 1).'">x</span> ';
12684 $ret .= dol_escape_htmltag($searchtags);
12685 $ret .= '</span>';
12686 }
12687 }
12688
12689 //$ret .= '<button type="submit" class="liste_titre button_search paddingleftonly" name="button_search_x" value="x"><span class="fa fa-search"></span></button>';
12690
12691 //$ret .= search_component_params
12692 //$texttoshow = '<div class="opacitymedium inline-block search_component_searchtext">'.$langs->trans("Search").'</div>';
12693 //$ret .= '<div class="search_component inline-block valignmiddle">'.$texttoshow.'</div>';
12694
12695 $show_search_component_params_hidden = 1;
12696 if ($show_search_component_params_hidden) {
12697 $ret .= '<input type="hidden" name="show_search_component_params_hidden" value="1">';
12698 }
12699 $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%')) -->";
12700 $ret .= '<input type="hidden" id="search_component_params_hidden" name="search_component_params_hidden" value="' . dol_escape_htmltag($search_component_params_hidden) . '">';
12701 // $ret .= "<!-- sql= ".forgeSQLFromUniversalSearchCriteria($search_component_params_hidden, $errormessage)." -->";
12702
12703 // TODO : Use $arrayoffiltercriterias instead of $arrayofcriterias
12704 // For compatibility with forms that show themself the search criteria in addition of this component, we output these fields
12705 foreach ($arrayofcriterias as $criteria) {
12706 foreach ($criteria as $criteriafamilykey => $criteriafamilyval) {
12707 if (in_array('search_' . $criteriafamilykey, $arrayofinputfieldsalreadyoutput)) {
12708 continue;
12709 }
12710 if (in_array($criteriafamilykey, array('rowid', 'ref_ext', 'entity', 'extraparams'))) {
12711 continue;
12712 }
12713 if (in_array($criteriafamilyval['type'], array('date', 'datetime', 'timestamp'))) {
12714 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_start">';
12715 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startyear">';
12716 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startmonth">';
12717 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_startday">';
12718 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_end">';
12719 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endyear">';
12720 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endmonth">';
12721 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '_endday">';
12722 } else {
12723 $ret .= '<input type="hidden" name="search_' . $criteriafamilykey . '">';
12724 }
12725 }
12726 }
12727
12728 $ret .= '</div>';
12729
12730 $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";
12731 $ret .= '<input type="text" placeholder="' . $langs->trans("Filters") . '" id="search_component_params_input" name="search_component_params_input" class="noborderall search_component_input" value="">';
12732
12733 $ret .= '</div>';
12734 $ret .= '</div>';
12735
12736 $ret .= '<script>
12737 jQuery(".tagsearchdelete").click(function(e) {
12738 var filterid = $(this).parents().attr("data-ufilterid");
12739 console.log("We click to delete the criteria nb "+filterid);
12740
12741 // Regenerate the search_component_params_hidden with all data-ufilter except the one to delete, and post the page
12742 var newparamstring = \'\';
12743 $(\'.tagsearch\').each(function(index, element) {
12744 tmpfilterid = $(this).attr("data-ufilterid");
12745 if (tmpfilterid != filterid) {
12746 // We keep this criteria
12747 if (newparamstring == \'\') {
12748 newparamstring = $(this).attr("data-ufilter");
12749 } else {
12750 newparamstring = newparamstring + \' AND \' + $(this).attr("data-ufilter");
12751 }
12752 }
12753 });
12754 console.log("newparamstring = "+newparamstring);
12755
12756 jQuery("#search_component_params_hidden").val(newparamstring);
12757
12758 // We repost the form
12759 $(this).closest(\'form\').submit();
12760 });
12761
12762 jQuery("#search_component_params_input").keydown(function(e) {
12763 console.log("We press a key on the filter field that is "+jQuery("#search_component_params_input").val());
12764 console.log(e.which);
12765 if (jQuery("#search_component_params_input").val() == "" && e.which == 8) {
12766 /* We click on back when the input field is already empty */
12767 event.preventDefault();
12768 jQuery("#divsearch_component_params .tagsearch").last().remove();
12769 /* Regenerate content of search_component_params_hidden from remaining .tagsearch */
12770 var s = "";
12771 jQuery("#divsearch_component_params .tagsearch").each(function( index ) {
12772 if (s != "") {
12773 s = s + " AND ";
12774 }
12775 s = s + $(this).attr("data-ufilter");
12776 });
12777 console.log("New value for search_component_params_hidden = "+s);
12778 jQuery("#search_component_params_hidden").val(s);
12779 }
12780 });
12781
12782 </script>
12783 ';
12784
12785 // Convert $arrayoffiltercriterias into a json object that can be used in jquery to build the search component dynamically
12786 $arrayoffiltercriterias_json = json_encode($arrayoffiltercriterias);
12787 $ret .= '<script>
12788 var arrayoffiltercriterias = ' . $arrayoffiltercriterias_json . ';
12789 </script>';
12790
12791
12792 $arrayoffilterfieldslabel = array();
12793 foreach ($arrayoffiltercriterias as $key => $val) {
12794 $arrayoffilterfieldslabel[$key]['label'] = $val['label'];
12795 $arrayoffilterfieldslabel[$key]['data-type'] = $val['type'];
12796 }
12797
12798 // Adding the div for search assistance
12799 $ret .= '<div class="search-component-assistance">';
12800 $ret .= '<div>';
12801
12802 $ret .= '<p class="assistance-title">' . img_picto('', 'filter') . ' ' . $langs->trans('FilterAssistance') . ' </p>';
12803
12804 $ret .= '<p class="assistance-errors error" style="display:none">' . $langs->trans('AllFieldsRequired') . ' </p>';
12805
12806 $ret .= '<div class="operand">';
12807 $ret .= $form->selectarray('search_filter_field', $arrayoffilterfieldslabel, '', $langs->trans("Fields"), 0, 0, '', 0, 0, 0, '', 'width200 combolargeelem', 1);
12808 $ret .= '</div>';
12809
12810 $ret .= '<span class="separator"></span>';
12811
12812 // Operator selector (will be populated dynamically)
12813 $ret .= '<div class="operator">';
12814 $ret .= '<select class="operator-selector width150" id="operator-selector"">';
12815 $ret .= '</select>';
12816 $ret .= '<script>$(document).ready(function() {';
12817 $ret .= ' $(".operator-selector").select2({';
12818 $ret .= ' placeholder: \'' . dol_escape_js($langs->transnoentitiesnoconv('Operator')) . '\'';
12819 $ret .= ' });';
12820 $ret .= '});</script>';
12821 $ret .= '</div>';
12822
12823 $ret .= '<span class="separator"></span>';
12824
12825 $ret .= '<div class="value">';
12826 // Input field for entering values
12827 $ret .= '<input type="text" class="flat width100 value-input" placeholder="' . dolPrintHTML($langs->trans('Value')) . '">';
12828
12829 // Date selector
12830 $dateOne = '';
12831 $ret .= '<span class="date-one" style="display:none">';
12832 $ret .= $form->selectDate(($dateOne ? $dateOne : -1), 'dateone', 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '');
12833 $ret .= '</span>';
12834
12835 // Value selector (will be populated dynamically) based on search_filter_field value if a selected value has an array of values
12836 $ret .= '<select class="value-selector width150" id="value-selector" style="display:none">';
12837 $ret .= '</select>';
12838 $ret .= '<script>
12839 $(document).ready(function() {
12840 $("#value-selector").select2({
12841 placeholder: "' . dol_escape_js($langs->trans('Value')) . '"
12842 });
12843 $("#value-selector").hide();
12844 $("#value-selector").next(".select2-container").hide();
12845 });
12846 </script>';
12847
12848 $ret .= '</div>';
12849
12850 $ret .= '<div class="btn-div">';
12851 $ret .= '<button class="button buttongen button-save add-filter-btn" type="button">' . $langs->trans("addToFilter") . '</button>';
12852 $ret .= '</div>';
12853
12854 $ret .= '</div>';
12855 //$ret .= '</tbody></table>';
12856
12857 // End of the assistance div
12858 $ret .= '</div>';
12859
12860 // Script jQuery to show/hide the floating assistance
12861 $ret .= '<script>
12862 $(document).ready(function() {
12863 $("#search_component_params_input").on("click", function() {
12864 const inputPosition = $(this).offset();
12865 const inputHeight = $(this).outerHeight();
12866 $(".search-component-assistance").css({
12867 top: inputPosition.top + inputHeight + 5 + "px",
12868 left: $("#divsearch_component_params").position().left
12869 }).slideToggle(200);
12870 });
12871 $(document).on("click", function(e) {
12872 if (!$(e.target).closest("#search_component_params_input, .search-component-assistance, #ui-datepicker-div").length) {
12873 $(".search-component-assistance").hide();
12874 }
12875 });
12876 });
12877 </script>';
12878
12879 $ret .= '<script>
12880 $(document).ready(function() {
12881 $(".search_filter_field").on("change", function() {
12882 console.log("We change search_filter_field");
12883
12884 let maybenull = 0;
12885 const selectedField = $(this).find(":selected");
12886 let fieldType = selectedField.data("type");
12887 const selectedFieldValue = selectedField.val();
12888
12889 // If the selected field has an array of values then ask toshow the value selector instead of the value input
12890 if (arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"] !== undefined) {
12891 fieldType = "select";
12892 }
12893
12894 // If the selected field may be null then ask to append the "IsDefined" and "IsNotDefined" operators
12895 if (arrayoffiltercriterias[selectedFieldValue]["maybenull"] !== undefined) {
12896 maybenull = 1;
12897 }
12898 const operators = getOperatorsForFieldType(fieldType, maybenull);
12899 const operatorSelector = $(".operator-selector");
12900
12901 // Clear existing options
12902 operatorSelector.empty();
12903
12904 // Populate operators
12905 Object.entries(operators).forEach(function([operator, label]) {
12906 operatorSelector.append("<option value=\'" + operator + "\'>" + label + "</option>");
12907 });
12908
12909 operatorSelector.trigger("change.select2");
12910
12911 // Clear and hide all input elements initially
12912 $(".value-input, .dateone, .datemonth, .dateyear").val("").hide();
12913 $("#datemonth, #dateyear").val(null).trigger("change.select2");
12914 $("#dateone").datepicker("setDate", null);
12915 $(".date-one, .date-month, .date-year").hide();
12916 $("#value-selector").val("").hide();
12917 $("#value-selector").next(".select2-container").hide();
12918 $("#value-selector").val(null).trigger("change.select2");
12919
12920 if (fieldType === "date" || fieldType === "datetime" || fieldType === "timestamp") {
12921 $(".date-one").show();
12922 } else if (arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"] !== undefined) {
12923 var arrayofkeyval = arrayoffiltercriterias[selectedFieldValue]["arrayofkeyval"];
12924 var valueSelector = $("#value-selector");
12925 valueSelector.empty();
12926 Object.entries(arrayofkeyval).forEach(function([key, val]) {
12927 valueSelector.append("<option value=\'" + key + "\'>" + val + "</option>");
12928 });
12929 valueSelector.trigger("change.select2");
12930
12931 $("#value-selector").show();
12932 $("#value-selector").next(".select2-container").show();
12933 } else {
12934 $(".value-input").show();
12935 }
12936 });
12937
12938 $("#operator-selector").on("change", function() {
12939 console.log("We change operator-selector");
12940
12941 const selectedOperator = $(this).find(":selected").val();
12942 if (selectedOperator === "IsDefined" || selectedOperator === "IsNotDefined") {
12943 // Disable all value input elements
12944 $(".value-input, .dateone, .datemonth, .dateyear").val("").prop("disabled", true);
12945 $("#datemonth, #dateyear").val(null).trigger("change.select2");
12946 $("#dateone").datepicker("setDate", null).datepicker("option", "disabled", true);
12947 $(".date-one, .date-month, .date-year").prop("disabled", true);
12948 $("#value-selector").val("").prop("disabled", true);
12949 $("#value-selector").val(null).trigger("change.select2");
12950 } else {
12951 // Enable all value input elements
12952 $(".value-input, .dateone, .datemonth, .dateyear").prop("disabled", false);
12953 $(".date-one, .date-month, .date-year").prop("disabled", false);
12954 $("#dateone").datepicker("option", "disabled", false);
12955 $("#value-selector").prop("disabled", false);
12956 }
12957 });
12958
12959 $(".add-filter-btn").on("click", function(event) {
12960 console.log("We click on add-filter-btn");
12961
12962 event.preventDefault();
12963
12964 const field = $(".search_filter_field").val();
12965 const operator = $(".operator-selector").val();
12966 let value = $(".value-input").val();
12967 const fieldType = $(".search_filter_field").find(":selected").data("type");
12968
12969 if (["date", "datetime", "timestamp"].includes(fieldType)) {
12970 const year = $("#dateoneyear").val().toString().padStart(4, "0");;
12971 const month = $("#dateonemonth").val().toString().padStart(2, "0");
12972 const day = $("#dateoneday").val().toString().padStart(2, "0");
12973 value = `${year}-${month}-${day}`;
12974 console.log("value="+value);
12975 }
12976
12977 // If the selected field has an array of values then take the selected value
12978 if (arrayoffiltercriterias[field]["arrayofkeyval"] !== undefined) {
12979 value = $("#value-selector").val();
12980 }
12981
12982 // If the operator is "IsDefined" or "IsNotDefined" then set the value to 1 (it will not be used)
12983 if (operator === "IsDefined" || operator === "IsNotDefined") {
12984 value = "1";
12985 }
12986
12987 const filterString = generateFilterString(field, operator, value, fieldType);
12988
12989 // Submit the form
12990 if (filterString !== "" && field !== "" && operator !== "" && value !== "") {
12991 $("#search_component_params_input").val($("#search_component_params_input").val() + " " + filterString);
12992 $("#search_component_params_input").closest("form").submit();
12993 } else {
12994 $(".assistance-errors").show();
12995 }
12996 });
12997 });
12998 </script>';
12999
13000 return $ret;
13001 }
13002
13014 public function selectModelMail($prefix, $modelType = '', $default = 0, $addjscombo = 0, $selected = 0, $morecss = '')
13015 {
13016 global $langs, $user;
13017
13018 $retstring = '';
13019
13020 $TModels = array();
13021
13022 include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmail.class.php';
13023 $formmail = new FormMail($this->db);
13024 $result = $formmail->fetchAllEMailTemplate($modelType, $user, $langs);
13025
13026 if ($default) {
13027 $TModels[0] = $langs->trans('DefaultMailModel');
13028 }
13029 if ($result > 0) {
13030 foreach ($formmail->lines_model as $model) {
13031 $TModels[(int) $model->id] = $model->label;
13032 }
13033 }
13034
13035 $retstring .= '<select class="flat'.($morecss ? ' '.$morecss : '').'" id="select_' . $prefix . 'model_mail" name="' . $prefix . 'model_mail">';
13036
13037 foreach ($TModels as $id_model => $label_model) {
13038 $retstring .= '<option value="' . $id_model . '"';
13039 if (!empty($selected) && ((int) $selected) == $id_model) {
13040 $retstring .= "selected";
13041 }
13042 $retstring .= ">" . $label_model . "</option>";
13043 }
13044
13045 $retstring .= "</select>";
13046
13047 if ($addjscombo) {
13048 $retstring .= ajax_combobox('select_' . $prefix . 'model_mail');
13049 }
13050
13051 return $retstring;
13052 }
13053
13065 public function buttonsSaveCancel($save_label = 'Save', $cancel_label = 'Cancel', $morebuttons = array(), $withoutdiv = false, $morecss = '', $dol_openinpopup = '')
13066 {
13067 global $langs;
13068
13069 $buttons = array();
13070
13071 $save = array(
13072 'name' => 'save',
13073 'label_key' => $save_label,
13074 );
13075
13076 if ($save_label == 'Create' || $save_label == 'Add') {
13077 $save['name'] = 'add';
13078 } elseif ($save_label == 'Modify') {
13079 $save['name'] = 'edit';
13080 }
13081
13082 $cancel = array(
13083 'name' => 'cancel',
13084 'label_key' => 'Cancel',
13085 );
13086
13087 // If MAIN_BUTTON_POSITION_FIRST_OR_LEFT not set, default is to have main action first, then complementary, then cancel at end
13088 if (!getDolGlobalInt('MAIN_BUTTON_POSITION_FIRST_OR_LEFT')) {
13089 !empty($save_label) ? $buttons[] = $save : '';
13090 if (!empty($morebuttons)) {
13091 $buttons[] = $morebuttons;
13092 }
13093 !empty($cancel_label) ? $buttons[] = $cancel : '';
13094 } else {
13095 if (!empty($morebuttons)) {
13096 $buttons[] = $morebuttons;
13097 }
13098 !empty($cancel_label) ? $buttons[] = $cancel : '';
13099 !empty($save_label) ? $buttons[] = $save : '';
13100 }
13101
13102 $retstring = $withoutdiv ? '' : '<div class="center">';
13103
13104 foreach ($buttons as $button) {
13105 $addclass = empty($button['addclass']) ? '' : $button['addclass'];
13106 $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'])) . '">';
13107 }
13108 $retstring .= $withoutdiv ? '' : '</div>';
13109
13110 if ($dol_openinpopup) {
13111 $retstring .= '<!-- buttons are shown into a $dol_openinpopup=' . dol_escape_htmltag($dol_openinpopup) . ' context, so we enable the close of dialog on cancel -->' . "\n";
13112 $retstring .= '<script nonce="' . getNonce() . '">';
13113 $retstring .= 'jQuery(".button-cancel").click(function(e) {
13114 e.preventDefault(); console.log(\'We click on cancel in iframe popup ' . dol_escape_js($dol_openinpopup) . '\');
13115 window.parent.jQuery(\'#idfordialog' . dol_escape_js($dol_openinpopup) . '\').dialog(\'close\');
13116 });';
13117 $retstring .= '</script>';
13118 }
13119
13120 return $retstring;
13121 }
13122
13123
13124 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
13125
13132 {
13133 // phpcs:enable
13134 global $langs;
13135
13136 $num = count($this->cache_invoice_subtype);
13137 if ($num > 0) {
13138 return 0; // Cache already loaded
13139 }
13140
13141 dol_syslog(__METHOD__, LOG_DEBUG);
13142
13143 $sql = "SELECT rowid, code, label as label";
13144 $sql .= " FROM " . MAIN_DB_PREFIX . 'c_invoice_subtype';
13145 $sql .= " WHERE active = 1";
13146
13147 $resql = $this->db->query($sql);
13148 if ($resql) {
13149 $num = $this->db->num_rows($resql);
13150 $i = 0;
13151 while ($i < $num) {
13152 $obj = $this->db->fetch_object($resql);
13153
13154 // If translation exists, we use it, otherwise we take the default wording
13155 $label = ($langs->trans("InvoiceSubtype" . $obj->rowid) != "InvoiceSubtype" . $obj->rowid) ? $langs->trans("InvoiceSubtype" . $obj->rowid) : (($obj->label != '-') ? $obj->label : '');
13156 $this->cache_invoice_subtype[$obj->rowid]['rowid'] = $obj->rowid;
13157 $this->cache_invoice_subtype[$obj->rowid]['code'] = $obj->code;
13158 $this->cache_invoice_subtype[$obj->rowid]['label'] = $label;
13159 $i++;
13160 }
13161
13162 $this->cache_invoice_subtype = dol_sort_array($this->cache_invoice_subtype, 'code', 'asc', 0, 0, 1);
13163
13164 return $num;
13165 } else {
13166 dol_print_error($this->db);
13167 return -1;
13168 }
13169 }
13170
13171
13182 public function getSelectInvoiceSubtype($selected = 0, $htmlname = 'subtypeid', $addempty = 0, $noinfoadmin = 0, $morecss = '')
13183 {
13184 global $langs, $user;
13185
13186 $out = '';
13187 dol_syslog(__METHOD__ . " selected=" . $selected . ", htmlname=" . $htmlname, LOG_DEBUG);
13188
13189 $this->load_cache_invoice_subtype();
13190
13191 $out .= '<select id="' . $htmlname . '" class="flat selectsubtype' . ($morecss ? ' ' . $morecss : '') . '" name="' . $htmlname . '">';
13192 if ($addempty) {
13193 $out .= '<option value="0">&nbsp;</option>';
13194 }
13195
13196 foreach ($this->cache_invoice_subtype as $rowid => $subtype) {
13197 $label = $subtype['label'];
13198 $out .= '<option value="' . $subtype['rowid'] . '"';
13199 if ($selected == $subtype['rowid']) {
13200 $out .= ' selected="selected"';
13201 }
13202 $out .= '>';
13203 $out .= $label;
13204 $out .= '</option>';
13205 }
13206
13207 $out .= '</select>';
13208 if ($user->admin && empty($noinfoadmin)) {
13209 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
13210 }
13211 $out .= ajax_combobox($htmlname);
13212
13213 return $out;
13214 }
13215
13225 public function getSearchFilterToolInput($dataTarget, $htmlName = 'search-tools-input', $value = '', $params = [])
13226 {
13227 global $langs;
13228
13229 $attr = array(
13230 'type' => 'search',
13231 'name' => $htmlName,
13232 'value' => $value,
13233 'class' => "search-tool-input",
13234 'placeholder' => $langs->trans('Search'),
13235 'autocomplete' => 'off'
13236 );
13237
13238 // Optional data attr
13239 // 'autofocus' : will set auto focus on field ,
13240 // data-counter-target : will get count results
13241 // data-no-item-target : will be display if count results is 0
13242
13243 if ($dataTarget !== false) {
13244 $attr['data-search-tool-target'] = $dataTarget;
13245 }
13246
13247 // Override attr
13248 if (!empty($params['attr']) && is_array($params['attr'])) {
13249 foreach ($params['attr'] as $key => $value) {
13250 if ($key == 'class') {
13251 $attr['class'] .= ' '.$value;
13252 } elseif ($key == 'classOverride') {
13253 $attr['class'] = $value;
13254 } else {
13255 $attr[$key] = $value;
13256 }
13257 }
13258 }
13259
13260 // automatic add tooltip when title is detected
13261 if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
13262 $attr['class'] .= ' classfortooltip';
13263 }
13264
13265 $TCompiledAttr = [];
13266 foreach ($attr as $key => $value) {
13267 if (in_array($key, ['data-target'])
13268 || (!empty($params['use_unsecured_unescapedattr']) && is_array($params['use_unsecured_unescapedattr']) && in_array($key, $params['use_unsecured_unescapedattr']))) { // Not recommended
13269 $value = dol_htmlentities($value, ENT_QUOTES | ENT_SUBSTITUTE);
13270 } else {
13271 $value = dolPrintHTMLForAttribute($value);
13272 }
13273
13274 $TCompiledAttr[] = $key . '="' . $value . '"'; // $value has been escaped by the dolPrintHTMLForAttribute... just before
13275 }
13276
13277 $compiledAttributes = implode(' ', $TCompiledAttr);
13278
13279
13280 return '<div class="search-tool-container"><input '.$compiledAttributes.'></div>';
13281 }
13282
13296 public function inputType($type, $name, $value = '', $id = '', $morecss = '', $moreparam = '', $label = '', $addInputLabel = '')
13297 {
13298 $out = '';
13299 if ($label != '') {
13300 $out .= '<label for="' . dolPrintHTMLForAttribute($id) . '">';
13301 }
13302 $out .= '<input type="' . dolPrintHTMLForAttribute($type) . '"';
13303 $out .= ' class="flat valignmiddle maxwidthonsmartphone ' . dolPrintHTMLForAttribute($morecss) . '"';
13304 if ($id != '') {
13305 $out .= ' id="' . dolPrintHTMLForAttribute($id) . '"';
13306 }
13307 $out .= ' name="' . dolPrintHTMLForAttribute($name) . '"';
13308 $out .= ' value="' . dolPrintHTMLForAttribute($value) . '" ';
13309 $out .= ($moreparam ? ' ' . $moreparam : '');
13310 $out .= ' />' . $addInputLabel;
13311 if ($label != '') {
13312 $out .= $label . '</label>';
13313 }
13314
13315 return $out;
13316 }
13317
13330 public function inputSelectAjax($htmlName, $array, $id, $ajaxUrl, $ajaxData = [], $morecss = 'minwidth75', $moreparam = '')
13331 {
13332 $out = "
13333 <script>
13334 $(document).ready(function () {
13335 $('#" . $htmlName . "').select2({
13336 ajax: {
13337 url: '" . $ajaxUrl . "',
13338 dataType: 'json',
13339 delay: 250, // wait 250 milliseconds before triggering the request
13340 data: function (params) {
13341 var query = {
13342 search: params.term,
13343 page: params.page || 1";
13344 if (!empty($ajaxData) && is_array($ajaxData)) {
13345 foreach ($ajaxData as $key => $value) {
13346 $out .= ", " . $key . ": '" . $value . "'";
13347 }
13348 }
13349 $out .= "
13350 }
13351 return query;
13352 }
13353 }
13354 })
13355 });
13356 </script>";
13357
13358 $out .= $this->selectarray($htmlName, $array, $id, 0, 0, 0, $moreparam, 0, 0, 0, '', $morecss);
13359
13360 return $out;
13361 }
13362
13372 public function inputHtml($htmlName, $value, $morecss = '', $moreparam = '')
13373 {
13374 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
13375 $doleditor = new DolEditor($htmlName, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_5, '90%');
13376
13377 return (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
13378 }
13379
13390 public function inputText($htmlName, $value, $morecss = '', $moreparam = '', $options = array())
13391 {
13392 global $langs;
13393
13394 $out = '';
13395 if (!empty($options)) {
13396 // If the textarea field has a list of arrayofkeyval into its definition, we suggest a combo with possible values to fill the textarea.
13397 $out .= $this->selectarray($htmlName . "_multiinput", $options, '', 1, 0, 0, $moreparam, 0, 0, 0, '', "flat maxwidthonphone" . $morecss);
13398 $out .= '<input id="' . $htmlName . '_multiinputadd" type="button" class="button" value="' . $langs->trans("Add") . '">';
13399 $out .= "<script>";
13400 $out .= '
13401 function handlemultiinputdisabling(htmlname){
13402 console.log("We handle the disabling of used options for "+htmlname+"_multiinput");
13403 multiinput = $("#"+htmlname+"_multiinput");
13404 multiinput.find("option").each(function(){
13405 tmpval = $("#"+htmlname).val();
13406 tmpvalarray = tmpval.split("\n");
13407 valtotest = $(this).val();
13408 if(tmpvalarray.includes(valtotest)){
13409 $(this).prop("disabled",true);
13410 } else {
13411 if($(this).prop("disabled") == true){
13412 console.log(valtotest)
13413 $(this).prop("disabled", false);
13414 }
13415 }
13416 });
13417 }
13418
13419 $(document).ready(function () {
13420 $("#' . $htmlName . '_multiinputadd").on("click",function() {
13421 tmpval = $("#' . $htmlName . '").val();
13422 tmpvalarray = tmpval.split(",");
13423 valtotest = $("#' . $htmlName . '_multiinput").val();
13424 if(valtotest != -1 && !tmpvalarray.includes(valtotest)){
13425 console.log("We add the selected value to the text area ' . $htmlName . '");
13426 if(tmpval == ""){
13427 tmpval = valtotest;
13428 } else {
13429 tmpval = tmpval + "\n" + valtotest;
13430 }
13431 $("#' . $htmlName . '").val(tmpval);
13432 handlemultiinputdisabling("' . $htmlName . '");
13433 $("#' . $htmlName . '_multiinput").val(-1);
13434 } else {
13435 console.log("We add nothing the text area ' . $htmlName . '");
13436 }
13437 });
13438 $("#' . $htmlName . '").on("change",function(){
13439 handlemultiinputdisabling("' . $htmlName . '");
13440 });
13441 handlemultiinputdisabling("' . $htmlName . '");
13442 })';
13443 $out .= "</script>";
13444 $value = str_replace(',', "\n", $value);
13445 }
13446
13447 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
13448 $doleditor = new DolEditor($htmlName, (string) $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
13449 $out .= (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
13450
13451 return $out;
13452 }
13453
13464 public function inputRadio($htmlName, $options, $selectedValue, $morecss = '', $moreparam = '')
13465 {
13466 $out = '';
13467 foreach ($options as $optionKey => $optionLabel) {
13468 $selected = ((string) $selectedValue) === ((string) $optionKey) ? ' checked="checked"' : '';
13469 $optionId = $htmlName . '_' . $optionKey;
13470 $out .= '<input class="flat' . $morecss . '" type="radio" name="' . $htmlName . '" id="' . $optionId . '" value="' . dolPrintHTMLForAttribute((string) $optionKey) . '"' . $selected . $moreparam . '/><label for="' . $optionId . '">' . $optionLabel . '</label><br>';
13471 }
13472
13473 return $out;
13474 }
13475
13486 public function inputStars($htmlName, $size, $value, $morecss = '', $moreparam = '')
13487 {
13488 $out = '<input type="hidden" class="flat ' . $morecss . '" name="' . $htmlName . '" id="' . $htmlName . '" value="' . dolPrintHTMLForAttribute((string) $value) . '"' . $moreparam . '>';
13489 $out .= '<div class="star-selection" id="' . $htmlName . '_selection">';
13490 for ($i = 1; $i <= $size; $i++) {
13491 $out .= '<span class="star" data-value="' . $i . '">' . img_picto('', 'fontawesome_star_fas') . '</span>';
13492 }
13493 $out .= '</div>';
13494 $out .= '<script>
13495 jQuery(function($) { /* commonobject.class.php 1 */
13496 let container = $("#' . $htmlName . '_selection");
13497 let selectedStars = parseInt($("#' . $htmlName . '").val()) || 0;
13498 container.find(".star").each(function() {
13499 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13500 });
13501 container.find(".star").on("mouseover", function() {
13502 let selectedStar = $(this).data("value");
13503 container.find(".star").each(function() {
13504 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
13505 });
13506 });
13507 container.on("mouseout", function() {
13508 container.find(".star").each(function() {
13509 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13510 });
13511 });
13512 container.find(".star").off("click").on("click", function() {
13513 selectedStars = $(this).data("value");
13514 if (selectedStars === 1 && $("#' . $htmlName . '").val() == 1) {
13515 selectedStars = 0;
13516 }
13517 $("#' . $htmlName . '").val(selectedStars);
13518 container.find(".star").each(function() {
13519 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
13520 });
13521 });
13522 });
13523 </script>';
13524
13525 return $out;
13526 }
13527
13537 public function inputIcon($htmlName, $value, $morecss = '', $moreparam = '')
13538 {
13539 global $langs;
13540
13541 /* External lib inclusion are not allowed in backoffice. Also lib is included several time if there is several icon file.
13542 Some code must be added into main when MAIN_ADD_ICONPICKER_JS is set to add of lib in html header
13543 $out ='<link rel="stylesheet" href="'.dol_buildpath('/myfield/css/fontawesome-iconpicker.min.css', 1).'">';
13544 $out.='<script src="'.dol_buildpath('/myfield/js/fontawesome-iconpicker.min.js', 1).'"></script>';
13545 */
13546 $out = '<input type="text" class="form-control icp icp-auto iconpicker-element iconpicker-input flat ' . $morecss . ' maxwidthonsmartphone"';
13547 $out .= ' name="' . $htmlName . '" id="' . $htmlName . '" value="' . dolPrintHTMLForAttribute((string) $value) . '" ' . ((string) $moreparam) . '>';
13548 if (getDolGlobalInt('MAIN_ADD_ICONPICKER_JS')) {
13549 $out .= '<script>';
13550 $options = "{ title: '<b>" . $langs->trans("IconFieldSelector") . "</b>', placement: 'right', showFooter: false, templates: {";
13551 $options .= "iconpicker: '<div class=\"iconpicker\"><div style=\"background-color:#EFEFEF;\" class=\"iconpicker-items\"></div></div>',";
13552 $options .= "iconpickerItem: '<a role=\"button\" href=\"#\" class=\"iconpicker-item\" style=\"background-color:#DDDDDD;\"><i></i></a>',";
13553 // $options.="buttons: '<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm\">".$langs->trans("Cancel")."</button>";
13554 // $options.="<button style=\"background-color:#FFFFFF;\" class=\"iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm\">".$langs->trans("Save")."</button>',";
13555 $options .= "footer: '<div class=\"popover-footer\" style=\"background-color:#EFEFEF;\"></div>',";
13556 $options .= "search: '<input type=\"search\" class\"form-control iconpicker-search\" placeholder=\"" . $langs->trans("TypeToFilter") . "\" />',";
13557 $options .= "popover: '<div class=\"iconpicker-popover popover\">";
13558 $options .= " <div class=\"arrow\" ></div>";
13559 $options .= " <div class=\"popover-title\" style=\"text-align:center;background-color:#EFEFEF;\"></div>";
13560 $options .= " <div class=\"popover-content \" ></div>";
13561 $options .= "</div>'}}";
13562 $out .= "$('#" . $htmlName . "').iconpicker(" . $options . ");";
13563 $out .= '</script>';
13564 }
13565
13566 return $out;
13567 }
13568
13577 public function inputGeoPoint($htmlName, $value, $type = '')
13578 {
13579 require_once DOL_DOCUMENT_ROOT . '/core/class/dolgeophp.class.php';
13580 require_once DOL_DOCUMENT_ROOT . '/core/class/geomapeditor.class.php';
13581 $dolgeophp = new DolGeoPHP($this->db);
13582 $geomapeditor = new GeoMapEditor();
13583
13584 $geojson = '{}';
13585 $centroidjson = getDolGlobalString('MAIN_INFO_SOCIETE_GEO_COORDINATES', '{}');
13586 if (!empty($value)) {
13587 $tmparray = $dolgeophp->parseGeoString($value);
13588 $geojson = $tmparray['geojson'];
13589 $centroidjson = $tmparray['centroidjson'];
13590 }
13591
13592 return $geomapeditor->getHtml($htmlName, $geojson, $centroidjson, $type);
13593 }
13594
13601 public function outputMultiValues($values)
13602 {
13603 $out = '';
13604 $toPrint = array();
13605 $values = is_array($values) ? $values : array();
13606
13607 foreach ($values as $value) {
13608 $toPrint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $value . '</li>';
13609 }
13610 if (!empty($toPrint)) {
13611 $out = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toPrint) . '</ul></div>';
13612 }
13613
13614 return $out;
13615 }
13616
13624 public function outputStars($size, $value)
13625 {
13626 $out = '<div class="star-selection" data-value="' . dolPrintHTMLForAttribute((string) $value) . '">';
13627 for ($i = 1; $i <= $size; $i++) {
13628 $out .= '<span class="star' . ($i <= $value ? ' active' : '') . '" data-value="' . $i . '">' . img_picto('', 'fontawesome_star_fas') . '</span>';
13629 }
13630 $out .= '</div>';
13631
13632 return $out;
13633 }
13634
13641 public function outputIcon($value)
13642 {
13643 $out = '<span class="' . dolPrintHTMLForAttribute((string) $value) . '"></span>';
13644
13645 return $out;
13646 }
13647
13655 public function outputGeoPoint($value, $type)
13656 {
13657 $out = '';
13658
13659 if (!empty($value)) {
13660 require_once DOL_DOCUMENT_ROOT . '/core/class/dolgeophp.class.php';
13661 $dolgeophp = new DolGeoPHP($this->db);
13662 if ($type == 'point') {
13663 $out = $dolgeophp->getXYString($value);
13664 } else { // multipts, linestrg, polygon
13665 $out = $dolgeophp->getPointString($value);
13666 }
13667 }
13668
13669 return $out;
13670 }
13671
13686 public function getNomUrl(&$object, $withpicto = 0, $option = '', $maxlength = 0, $save_lastsearch_value = -1, $notooltip = 0, $morecss = '', $add_label = 0, $sep = ' - ')
13687 {
13688 if (is_object($object) && method_exists($object, 'getNomUrl')) {
13689 $out = $object->getNomUrl($withpicto, $option, $maxlength, $save_lastsearch_value, $notooltip, $morecss, $add_label, $sep);
13690 return $out;
13691 } else {
13692 return '';
13693 }
13694 }
13695}
$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.
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
dol_print_phone($phone, $countrycode='', $contactid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='paddingright')
Format phone numbers according to country.
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.
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 '.
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.
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.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
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.
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
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.
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
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.
getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
Return URL we can use for advanced preview links.
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_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.
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.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
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.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
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...
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.
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...
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...
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
a disabled
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
buildzip.php
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